<?php

namespace Tests\Unit\CampaignPerformance;

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

/**
 * Column order of the Campaign Performance table.
 *
 * The table takes its columns from the shared KpiRegistry, but this page wants
 * CVR and the revenue columns at the far right rather than in registry order. That
 * reordering lives in the presenter (TRAILING_METRIC_IDS), so adding a metric to
 * the registry — or reordering it for the dashboard / breakdown tables — would
 * silently drag those columns back into the middle. These tests fail loudly if
 * that happens.
 */
final class CampaignPerformanceTableColumnOrderTest extends TestCase
{
    private const TRAILING = ['cvr', 'est_revenue', 'adv_revenue'];

    public function testCvrAndBothRevenueColumnsAreTheLastThree(): void
    {
        $ids = array_column(CampaignPerformanceTablePresenter::tableMetrics(), 'id');

        $this->assertSame(self::TRAILING, array_slice($ids, -3));
    }

    public function testNoMetricIsDroppedOrDuplicated(): void
    {
        $registryIds = array_column(KpiRegistry::metrics(), 'id');
        $tableIds    = array_column(CampaignPerformanceTablePresenter::tableMetrics(), 'id');

        $this->assertCount(count($registryIds), $tableIds, 'Reordering must not add or drop columns.');
        $this->assertSame(count($tableIds), count(array_unique($tableIds)), 'A metric was emitted twice.');

        sort($registryIds);
        sort($tableIds);
        $this->assertSame($registryIds, $tableIds);
    }

    public function testLeadingColumnsKeepRegistryOrder(): void
    {
        $expected = array_values(array_filter(
            array_column(KpiRegistry::metrics(), 'id'),
            static function (string $id): bool {
                return !in_array($id, self::TRAILING, true);
            }
        ));

        $ids = array_column(CampaignPerformanceTablePresenter::tableMetrics(), 'id');

        $this->assertSame($expected, array_slice($ids, 0, count($expected)));
    }

    public function testEstimatedAndAdvertiserColumnsArePaired(): void
    {
        $positions = array_flip(array_column(CampaignPerformanceTablePresenter::tableMetrics(), 'id'));

        $this->assertSame(
            1,
            abs($positions['conversions'] - $positions['adv_conversions']),
            'Est. Conversions and Adv. Conversions must be adjacent.'
        );
        $this->assertSame(
            1,
            abs($positions['est_revenue'] - $positions['adv_revenue']),
            'Est. Revenue and Adv. Revenue must be adjacent.'
        );
    }

    public function testChartableMetricsExcludeTheAdvertiserPair(): void
    {
        $chartable = array_column(KpiRegistry::chartableMetrics(), 'id');

        $this->assertNotContains('adv_conversions', $chartable);
        $this->assertNotContains('adv_revenue', $chartable);
        $this->assertCount(count(KpiRegistry::metrics()) - 2, $chartable);
        $this->assertContains('conversions', $chartable, 'Our own conversion count is still plotted.');
    }

    public function testCosAndRoasStillDeriveFromEstimatedRevenue(): void
    {
        $kpis = KpiRegistry::expandTotals([
            'clicks'          => 100,
            'spend'           => 50,
            'conversions'     => 10,
            'est_revenue'     => 200,
            'adv_conversions' => 77,
            'adv_revenue'     => 999.5,
        ]);

        $this->assertSame(200.0 / 50.0, $kpis['roas']);
        $this->assertSame(50.0 / 200.0 * 100.0, $kpis['cos']);
        $this->assertSame(77.0, $kpis['adv_conversions'], 'Advertiser figures pass through untouched.');
        $this->assertSame(999.5, $kpis['adv_revenue']);
    }

    public function testAdvertiserMetricsDefaultToZeroWhenNotSupplied(): void
    {
        $kpis = KpiRegistry::expandTotals(['clicks' => 1]);

        $this->assertSame(0.0, $kpis['adv_conversions']);
        $this->assertSame(0.0, $kpis['adv_revenue']);
    }
}
