<?php

namespace Tests\Feature;

use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;

/**
 * Campaign edit Save must persist frequency cap the way serving reads it:
 * targeting JSON `frequency` and `campaign.freq_cap` stay in sync, and
 * "No Frequency Limit" actually clears both.
 *
 * php8.2 -d newrelic.enabled=0 -d newrelic.daemon.dont_launch=3 \
 *   vendor/bin/phpunit --bootstrap tests/feature_bootstrap.php \
 *   tests/feature/CampaignUpdateFrequencyCapTest.php
 */
final class CampaignUpdateFrequencyCapTest extends CIUnitTestCase
{
    use FeatureTestTrait;

    private const ADV = 999999991;

    private array $sess;

    /** @var \CodeIgniter\Database\BaseConnection */
    private $conn;

    /** @var list<int> */
    private array $campaignIds = [];

    protected function setUp(): void
    {
        parent::setUp();

        $this->sess = [
            'logged_in' => 1,
            'adv_id'    => self::ADV,
            'user_id'   => self::ADV,
            'username'  => 'QA Bot',
            'role'      => 'admin',
        ];

        $filters = config('Filters');
        unset($filters->filters['csrf']);

        $this->conn = \Config\Database::connect('default');
        $this->cleanup();
    }

    protected function tearDown(): void
    {
        $this->cleanup();
        parent::tearDown();
    }

    private function cleanup(): void
    {
        $camps = $this->conn->table('campaign')->select('id')
            ->where('advertiser_id', self::ADV)->get()->getResultArray();
        foreach ($camps as $r) {
            $cid = (int) $r['id'];
            $this->conn->table('campaign_targeting')->where('campaign_id', $cid)->delete();
            $this->conn->table('campaign_creative_link')->where('campaign_id', $cid)->delete();
        }
        $this->conn->table('campaign')->where('advertiser_id', self::ADV)->delete();
        $this->campaignIds = [];
    }

    private function req()
    {
        return $this->withSession($this->sess);
    }

    private function insertCappedCampaign(): int
    {
        $this->conn->table('campaign')->insert([
            'advertiser_id'   => self::ADV,
            'name'            => 'QA Frequency Cap Campaign',
            'status'          => 'A',
            'bid'             => '0.100000',
            'budget'          => '50.00',
            'campaign_type'   => 1,
            'campaign_mode'   => 'CPM',
            'freq_cap'        => 100,
            'xml_traffic'     => 0,
            'duration'        => 0,
            'linktrust_pixel' => '',
            'site'            => '',
            'pixel'           => '',
            'pixel_conv'      => '',
            'system'          => 0,
            'system_id'       => 0,
            'usonly'          => 0,
        ]);
        $id = (int) $this->conn->insertID();
        $this->campaignIds[] = $id;

        $this->conn->table('campaign_targeting')->insert([
            'campaign_id' => $id,
            'json'        => json_encode([
                'frequency'  => 100,
                'affiliates' => ['120438'],
                'countries'  => ['USA'],
            ]),
        ]);

        return $id;
    }

    /**
     * @return array{row: array<string, mixed>, json: array<string, mixed>}
     */
    private function reload(int $id): array
    {
        $row = $this->conn->table('campaign')->where('id', $id)->get()->getRowArray();
        $targetingRow = $this->conn->table('campaign_targeting')
            ->where('campaign_id', $id)->get()->getRowArray();
        $json = json_decode((string) ($targetingRow['json'] ?? '{}'), true) ?: [];

        return ['row' => $row, 'json' => $json];
    }

    public function testUpdateClearsFrequencyWhenModeIsNone(): void
    {
        $id = $this->insertCappedCampaign();

        $this->req()->post('campaign/update/' . $id, [
            'name'                      => 'QA Frequency Cap Campaign',
            'bid'                       => '0.10',
            'budget'                    => '50',
            'status'                    => 'A',
            'campaign_type'             => 'Display',
            'targeting-frequency-mode'  => 'none',
            'targeting-frequency'       => '1',
        ]);

        $out = $this->reload($id);

        $this->assertArrayNotHasKey('frequency', $out['json'], 'No Frequency Limit must drop targeting.frequency');
        $this->assertSame(['120438'], array_map('strval', (array) ($out['json']['affiliates'] ?? [])));
        $this->assertSame(0, (int) ($out['row']['freq_cap'] ?? -1), 'serving reads campaign.freq_cap');
    }

    public function testUpdateWritesNewFrequencyToJsonAndFreqCap(): void
    {
        $id = $this->insertCappedCampaign();

        $this->req()->post('campaign/update/' . $id, [
            'name'                      => 'QA Frequency Cap Campaign',
            'bid'                       => '0.10',
            'budget'                    => '50',
            'status'                    => 'A',
            'campaign_type'             => 'Display',
            'targeting-frequency-mode'  => 'set',
            'targeting-frequency'       => '50',
        ]);

        $out = $this->reload($id);

        $this->assertSame(50, (int) ($out['json']['frequency'] ?? 0));
        $this->assertSame(50, (int) ($out['row']['freq_cap'] ?? 0), 'campaign.freq_cap must follow the form');
        $this->assertSame(['120438'], array_map('strval', (array) ($out['json']['affiliates'] ?? [])));
    }

    public function testUpdateWithoutFrequencyFieldsLeavesCapAlone(): void
    {
        $id = $this->insertCappedCampaign();

        $this->req()->post('campaign/update/' . $id, [
            'name'   => 'QA Frequency Cap Campaign',
            'bid'    => '0.10',
            'budget' => '50',
            'status' => 'A',
        ]);

        $out = $this->reload($id);

        $this->assertSame(100, (int) ($out['json']['frequency'] ?? 0));
        $this->assertSame(100, (int) ($out['row']['freq_cap'] ?? 0));
    }
}
