<?php

namespace Tests\Unit\Dashboard;

use App\Services\Dashboard\KpiRegistry;
use PHPUnit\Framework\TestCase;

/**
 * Decimal places for the metric values shown on Dashboard and Campaign Performance.
 *
 * Asserted through KpiRegistry::formatNumber() rather than format_metric() directly,
 * because that is the entry point every presenter actually uses
 * (CampaignPerformanceTablePresenter, DashboardTablePresenter, DashboardKpiPresenter)
 * — so a metric silently switching `type` in the registry fails here too.
 *
 * Plain TestCase, matching the rest of tests/unit — phpunit.xml.dist runs only that
 * directory and its bootstrap does not boot CodeIgniter. It does stub session(), so
 * format_money() falls back to a '$' symbol and a 1.0 rate, which is what these
 * expectations assume. The helper is a function file, so it needs requiring
 * explicitly; the PSR-4 autoloader in the bootstrap only covers App\ classes.
 */
final class MetricFormattingTest extends TestCase
{
    public static function setUpBeforeClass(): void
    {
        require_once dirname(__DIR__, 3) . '/app/Helpers/utils_helper.php';
    }

    /**
     * eCPC / CPC / eCPM were 3dp (`currency_small`) and are now 2dp.
     */
    public function testSmallCurrencyMetricsUseTwoDecimals(): void
    {
        foreach (['ecpc', 'cpc', 'ecpm'] as $id) {
            $this->assertSame('$0.35', KpiRegistry::formatNumber($id, 0.3456), $id . ' should round to 2dp');
            $this->assertSame('$1.00', KpiRegistry::formatNumber($id, 1.0), $id . ' should pad to 2dp');
            $this->assertSame('$12.35', KpiRegistry::formatNumber($id, 12.345), $id);
        }
    }

    /**
     * CVR / COS were 3dp (`percent`) and are now 2dp.
     */
    public function testPercentMetricsUseTwoDecimals(): void
    {
        foreach (['cvr', 'cos'] as $id) {
            $this->assertSame('12.35%', KpiRegistry::formatNumber($id, 12.3456), $id . ' should round to 2dp');
            $this->assertSame('0.00%', KpiRegistry::formatNumber($id, 0.0), $id . ' should pad to 2dp');
            $this->assertSame('100.00%', KpiRegistry::formatNumber($id, 100.0), $id);
        }
    }

    /**
     * Recorded deliberately, not incidentally: CPC is the campaign bid
     * (decimal(10,6)), and ~1,508 live campaigns hold a bid under 0.005, which 2dp
     * renders as $0.00. If that becomes a problem the fix is to point `cpc` at
     * format_bid() — this assertion is what should then be updated, on purpose.
     */
    public function testTinyBidsCollapseToZeroAtTwoDecimals(): void
    {
        $this->assertSame('$0.00', KpiRegistry::formatNumber('cpc', 0.004));
        $this->assertSame('$0.00', KpiRegistry::formatNumber('cpc', 0.000001));
        // 0.005 and up still round away from zero, so the boundary is where expected.
        $this->assertSame('$0.01', KpiRegistry::formatNumber('cpc', 0.005));
    }

    /**
     * Guards the metrics that were already correct, so a future edit to
     * format_metric() cannot quietly change them while chasing the two above.
     */
    public function testAlreadyCorrectMetricsAreUnchanged(): void
    {
        $this->assertSame('1.23%', KpiRegistry::formatNumber('ctr', 1.2345));
        $this->assertSame('$1,234.57', KpiRegistry::formatNumber('spend', 1234.567));
        $this->assertSame('$1,234.57', KpiRegistry::formatNumber('est_revenue', 1234.567));
        $this->assertSame('$12.35', KpiRegistry::formatNumber('cpa', 12.345));
        $this->assertSame('2.35x', KpiRegistry::formatNumber('roas', 2.345));
        $this->assertSame('1,235', KpiRegistry::formatNumber('impressions', 1234.9));
        $this->assertSame('1,235', KpiRegistry::formatNumber('clicks', 1234.9));
        $this->assertSame('12', KpiRegistry::formatNumber('conversions', 12.4));
    }

    /**
     * No metric in the registry may render more than 2 decimals — the whole point of
     * the change, expressed once over the real registry rather than per id, so a
     * newly added metric with a 3dp type fails immediately.
     */
    public function testNoMetricRendersMoreThanTwoDecimals(): void
    {
        foreach (KpiRegistry::metrics() as $metric) {
            $out = KpiRegistry::formatNumber($metric['id'], 1.23456789);
            $digits = 0;
            if (preg_match('/\.(\d+)/', $out, $m) === 1) {
                $digits = strlen($m[1]);
            }
            $this->assertLessThanOrEqual(
                2,
                $digits,
                sprintf('%s (type %s) rendered "%s" — more than 2 decimals', $metric['id'], $metric['type'], $out)
            );
        }
    }
}
