<?php

namespace Tests\Feature;

use App\Controllers\Reports;
use App\Models\ReportModel;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
use Config\Services;
use ReflectionMethod;

/**
 * Custom Reports persist the date window in adv_reports.json (filter.date), not
 * the app-wide sticky cookie. Edit must re-seed the picker from the saved
 * from/to backup so save cannot overwrite a custom range with today or last30.
 *
 * Run with:
 *   php8.2 -d newrelic.enabled=0 -d newrelic.daemon.dont_launch=3 \
 *     vendor/bin/phpunit -c phpunit.feature.xml tests/feature/CustomReportsDateRangeTest.php
 */
final class CustomReportsDateRangeTest extends CIUnitTestCase
{
    use FeatureTestTrait;

    /** Throwaway advertiser id — never a real account. */
    private const ADV = 999999987;

    private const START = '2026-02-03';
    private const END   = '2026-02-19';

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

    private int $obLevel = 0;

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

        Services::injectMock('response', Services::response(null, false));

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

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

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

    private function cleanup(): void
    {
        $ids = $this->conn->table('adv_reports')
            ->select('id')
            ->where('advertiser_id', self::ADV)
            ->get()
            ->getResultArray();
        foreach ($ids as $row) {
            $this->conn->table('adv_reports_files')->where('report_id', (int) $row['id'])->delete();
        }
        $this->conn->table('adv_reports')->where('advertiser_id', self::ADV)->delete();
    }

    /** @return array<string,mixed> */
    private function session(): array
    {
        return [
            'logged_in' => 1,
            'adv_id'    => self::ADV,
            'user_id'   => self::ADV,
            'role'      => 'admin',
        ];
    }

    /** @param array<string,mixed> $date */
    private function insertReport(string $frequency, array $date): int
    {
        return (new ReportModel())->store(self::ADV, [
            'frequency'    => $frequency,
            'is_mngt_user' => 0,
            'config'       => [
                'name'      => 'QA custom date',
                'frequency' => $frequency,
                'emailto'   => '',
                'format'    => 'hourly',
                'metrics'   => ['spent', 'impressions', 'clicks'],
                'filter'    => ['date' => $date],
            ],
        ]);
    }

    private function editHtml(int $id): string
    {
        $res = $this->withSession($this->session())->get('reports/' . $id . '/edit');
        $res->assertOK();

        return (string) $res->response()->getBody();
    }

    private function expectedCustomLabel(): string
    {
        return date('j M Y', strtotime(self::START)) . ' - ' . date('j M Y', strtotime(self::END));
    }

    public function testEditSeedsCustomRangeIntoPickerAndBackupFields(): void
    {
        $id   = $this->insertReport('once', [
            'range' => 'custom',
            'from'  => self::START,
            'to'    => self::END,
        ]);
        $html = $this->editHtml($id);

        $this->assertStringContainsString('data-start-date="' . self::START . '"', $html);
        $this->assertStringContainsString('data-end-date="' . self::END . '"', $html);
        $this->assertStringContainsString('name="date_from" data-drp-from value="' . self::START . '"', $html);
        $this->assertStringContainsString('name="date_to"   data-drp-to   value="' . self::END . '"', $html);
        $this->assertStringContainsString('name="date_range" value="custom"', $html);
        $this->assertStringContainsString($this->expectedCustomLabel(), $html);
    }

    public function testScheduledCustomReportKeepsCustomOnEditHtml(): void
    {
        $id   = $this->insertReport('daily', [
            'range' => 'custom',
            'from'  => self::START,
            'to'    => self::END,
        ]);
        $html = $this->editHtml($id);

        $this->assertStringContainsString('name="date_range" value="custom"', $html);
        $this->assertStringContainsString('data-start-date="' . self::START . '"', $html);
        $this->assertStringContainsString('data-end-date="' . self::END . '"', $html);
        // Load-time frequency trigger must not wipe custom; only a real user change does.
        $this->assertStringContainsString('!e.isTrigger', $html);
    }

    public function testPresetEditDoesNotSeedCustomBackupDates(): void
    {
        $id   = $this->insertReport('once', [
            'range' => 'last7',
            'from'  => '',
            'to'    => '',
        ]);
        $html = $this->editHtml($id);

        $this->assertStringContainsString('name="date_range" value="last7"', $html);
        $this->assertStringNotContainsString('data-start-date="', $html);
        $this->assertStringContainsString('Last 7 days', $html);
    }

    public function testBuildConfigRoundTripsCustomDateFilter(): void
    {
        $method = new ReflectionMethod(Reports::class, 'buildConfig');
        $method->setAccessible(true);

        $config = $method->invoke(new Reports(), [
            'name'       => 'QA custom date',
            'frequency'  => 'once',
            'date_range' => 'custom',
            'date_from'  => self::START,
            'date_to'    => self::END,
            'metrics'    => ['spent'],
        ]);

        $this->assertSame('custom', $config['filter']['date']['range']);
        $this->assertSame(self::START, $config['filter']['date']['from']);
        $this->assertSame(self::END, $config['filter']['date']['to']);
    }
}
