<?php

declare(strict_types=1);

namespace Srp\Tests;

use PHPUnit\Framework\TestCase;
use Srp\Redirect\FilterEngine;

/**
 * Contract test for redirect/FilterEngine.php — the timed filter/normal cycle.
 *
 * Tests rely on default timing (no runtime cache file, no env override), so
 * each test uses a unique non-existent cache sub-directory and clears the
 * SRP_* env vars app_env() reads. isFilterMode() takes an injectable $now,
 * which is what makes the cycle math deterministic here.
 */
final class FilterEngineTest extends TestCase
{
    private string $cacheSubDir;

    protected function setUp(): void
    {
        // Guarantee getTiming()/getFilterUrl() see no runtime cache file.
        $this->cacheSubDir = 'srp_test_' . bin2hex(random_bytes(6));

        foreach (['SRP_FILTER_SECONDS', 'SRP_CYCLE_SECONDS', 'SRP_FILTER_URL'] as $key) {
            unset($_ENV[$key], $_SERVER[$key]);
            putenv($key);
        }
    }

    public function testInsideFilterWindowReturnsTrue(): void
    {
        $engine = new FilterEngine($this->cacheSubDir); // 120s filter / 300s cycle

        self::assertTrue($engine->isFilterMode(0));    // 0 % 300 = 0  < 120
        self::assertTrue($engine->isFilterMode(119));  // 119          < 120
        self::assertTrue($engine->isFilterMode(360));  // 360 % 300=60 < 120
    }

    public function testOutsideFilterWindowReturnsFalse(): void
    {
        $engine = new FilterEngine($this->cacheSubDir);

        self::assertFalse($engine->isFilterMode(120)); // 120 not < 120
        self::assertFalse($engine->isFilterMode(299)); // 299 not < 120
        self::assertFalse($engine->isFilterMode(420)); // 420 % 300 = 120
    }

    public function testZeroFilterSecondsDisablesFilterEntirely(): void
    {
        $engine = new FilterEngine($this->cacheSubDir, 0);

        self::assertFalse($engine->isFilterMode(0));
        self::assertFalse($engine->isFilterMode(50));
    }

    public function testZeroCycleSecondsDoesNotThrowAndDisablesFilter(): void
    {
        // Guards the `$now % $cycleSeconds` modulo against DivisionByZeroError
        // if a caller ever constructs the engine with a non-positive cycle.
        $engine = new FilterEngine($this->cacheSubDir, 120, 0);

        self::assertFalse($engine->isFilterMode(0));
        self::assertFalse($engine->isFilterMode(50));
    }

    public function testGetTimingFallsBackToDefaults(): void
    {
        $engine = new FilterEngine($this->cacheSubDir, 90, 240);

        self::assertSame([90, 240], $engine->getTiming());
    }

    public function testGetFilterUrlReturnsNullWhenUnconfigured(): void
    {
        $engine = new FilterEngine($this->cacheSubDir);

        self::assertNull($engine->getFilterUrl());
    }

    public function testGetFilterUrlAcceptsHttpsDefault(): void
    {
        $engine = new FilterEngine($this->cacheSubDir, 120, 300, 'https://example.com/filter');

        self::assertSame('https://example.com/filter', $engine->getFilterUrl());
    }

    public function testGetFilterUrlRejectsNonHttps(): void
    {
        $engine = new FilterEngine($this->cacheSubDir, 120, 300, 'http://example.com/filter');

        self::assertNull($engine->getFilterUrl());
    }
}
