<?php

declare(strict_types=1);

namespace Srp\Redirect;

/**
 * FilterEngine — Timed filter cycle management for redirect traffic.
 *
 * Controls the 2-min filter / 3-min normal repeating cycle. Filter timing and
 * URL can be overridden at runtime via files in the srp_bb/ cache directory
 * (written by the admin "Redirect Decision" panel).
 *
 * Priority chain (highest first):
 *   1. Runtime cache file: {tmp}/srp_bb/filter_timing.json
 *   2. Environment: SRP_FILTER_SECONDS / SRP_CYCLE_SECONDS / SRP_FILTER_URL
 *   3. Constants: SRP_FILTER_DURATION / SRP_CYCLE_LENGTH / SRP_FILTER_URL
 *
 * Usage:
 *   $engine = new FilterEngine('srp_bb');
 *   if ($engine->isFilterMode()) { redirect to $engine->getFilterUrl(); }
 */
final class FilterEngine
{
    private string $cacheDir;
    private int $defaultFilterSeconds;
    private int $defaultCycleSeconds;
    private string $defaultFilterUrl;

    public function __construct(
        string $cacheSubDir,
        int $defaultFilterSeconds = 120,
        int $defaultCycleSeconds = 300,
        string $defaultFilterUrl = '',
    ) {
        $this->cacheDir = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR)
            . DIRECTORY_SEPARATOR . $cacheSubDir;
        $this->defaultFilterSeconds = $defaultFilterSeconds;
        $this->defaultCycleSeconds = $defaultCycleSeconds;
        $this->defaultFilterUrl = $defaultFilterUrl;
    }

    /**
     * Check if current time falls within the filter window.
     *
     * @param int|null $now Unix timestamp (injectable for testing).
     */
    public function isFilterMode(?int $now = null): bool
    {
        [$filterSeconds, $cycleSeconds] = $this->getTiming();
        $now ??= time();

        // filterSeconds <= 0 disables the window; cycleSeconds <= 0 would make the
        // modulo below a DivisionByZeroError, so treat it as disabled too.
        if ($filterSeconds <= 0 || $cycleSeconds <= 0) {
            return false;
        }

        return ($now % $cycleSeconds) < $filterSeconds;
    }

    /**
     * Get filter/cycle durations in seconds: [filterSeconds, cycleSeconds].
     *
     * @return array{0: int, 1: int}
     */
    public function getTiming(): array
    {
        // 1. Runtime cache file
        $file = $this->cacheDir . DIRECTORY_SEPARATOR . 'filter_timing.json';
        if (is_file($file)) {
            $raw = @file_get_contents($file, false, null, 0, 256);
            $data = is_string($raw) ? json_decode($raw, true) : null;
            if (
                is_array($data) && isset($data['filter_seconds'], $data['cycle_seconds'])
                && is_numeric($data['filter_seconds']) && is_numeric($data['cycle_seconds'])
            ) {
                $filter = (int) $data['filter_seconds'];
                $cycle  = (int) $data['cycle_seconds'];

                if ($cycle >= 60 && $cycle <= 86400 && $filter >= 0 && $filter <= $cycle) {
                    return [$filter, $cycle];
                }
            }
        }

        // 2. Environment
        $envFilter = (int) app_env('SRP_FILTER_SECONDS', '-1');
        $envCycle  = (int) app_env('SRP_CYCLE_SECONDS', '-1');
        if ($envCycle >= 60 && $envCycle <= 86400 && $envFilter >= 0 && $envFilter <= $envCycle) {
            return [$envFilter, $envCycle];
        }

        // 3. Defaults
        return [$this->defaultFilterSeconds, $this->defaultCycleSeconds];
    }

    /**
     * Get the filter-mode redirect URL, or null if none configured.
     * When null, the filter window redirects traffic to the normal path.
     */
    public function getFilterUrl(): ?string
    {
        // 1. Runtime cache file
        $cacheFile = $this->cacheDir . DIRECTORY_SEPARATOR . 'filter_url.txt';
        if (is_file($cacheFile)) {
            $raw = @file_get_contents($cacheFile, false, null, 0, 2048);
            $url = is_string($raw) ? trim($raw) : '';
            if ($this->isValidHttpsUrl($url)) {
                return $url;
            }
        }

        // 2. Environment
        $envUrl = app_env('SRP_FILTER_URL', '');
        if (
            $envUrl !== null
            && $envUrl !== ''
            && $this->isValidHttpsUrl($envUrl)
        ) {
            return $envUrl;
        }

        // 3. Default
        if ($this->defaultFilterUrl !== '' && $this->isValidHttpsUrl($this->defaultFilterUrl)) {
            return $this->defaultFilterUrl;
        }

        return null;
    }

    private function isValidHttpsUrl(string $url): bool
    {
        return $url !== ''
            && filter_var($url, FILTER_VALIDATE_URL) !== false
            && strtolower((string) parse_url($url, PHP_URL_SCHEME)) === 'https';
    }
}
