<?php
declare(strict_types=1);

namespace SeoScope\Licensing;

final class LicenseGuard
{
    // 3 days grace period (seconds)
    public const GRACE_SECONDS = 3 * 24 * 60 * 60;

    public function __construct(
        private string $root,
        private array $config
    ) {
    }

    public function isAllowedPath(string $path): bool
    {
        // Always allow licensing page and auth endpoints
        $allow = [
            '/license',
            '/login',
            '/setup',
            '/forgot-password',
            '/reset-password',
            '/logout',
        ];
        return in_array($path, $allow, true);
    }

    /**
     * Ensures license is valid. Returns null when OK; otherwise returns a redirect URL.
     */
    public function enforce(string $path): ?string
    {
        if ($this->isAllowedPath($path)) {
            return null;
        }
        $storage = new LicenseStorage($this->root);
        $state = $storage->load();
        if ($state->token === null || $state->token === '') {
            return seoscope_url('/license');
        }
        $domain = $this->currentDomain();
        $installId = $storage->installId();

        $now = time();
        $needsValidate = $state->nextCheckAt === null || $now >= $state->nextCheckAt;
        if (!$needsValidate) {
            // Still within schedule; allow only if active, or if in grace (server unreachable) within grace window.
            if ($state->status === 'active') {
                return null;
            }
            if ($state->status === 'grace' && $state->lastOkAt !== null && ($now - $state->lastOkAt) <= self::GRACE_SECONDS) {
                return null;
            }
            return seoscope_url('/license');
        }

        $client = new LicenseClient($this->licensingBaseUrl());
        $res = $client->validate($state->token, $domain, $installId);

        if ($res['ok'] && is_array($res['data'])) {
            $data = $res['data'];
            $valid = !empty($data['valid']);
            if ($valid) {
                $next = is_numeric($data['next_check_at'] ?? null) ? (int) $data['next_check_at'] : ($now + 86400);
                $new = new LicenseState('active', $state->token, $now, $next, null);
                $storage->save($new);
                return null;
            }
            // When the licensing server explicitly says the license is not valid (revoked/not_activated/etc),
            // block immediately (grace is reserved for temporary server outages).
            $new = new LicenseState('invalid', $state->token, $state->lastOkAt, $now + 3600, is_string($data['error'] ?? null) ? (string) $data['error'] : null);
            $storage->save($new);
            return seoscope_url('/license');
        }

        // Server unreachable or non-2xx: allow within grace
        if ($state->lastOkAt !== null && ($now - $state->lastOkAt) <= self::GRACE_SECONDS) {
            $new = new LicenseState('grace', $state->token, $state->lastOkAt, $now + 3600, 'server_unreachable');
            $storage->save($new);
            return null;
        }
        $new = new LicenseState('expired', $state->token, $state->lastOkAt, $now + 3600, 'server_unreachable');
        $storage->save($new);
        return seoscope_url('/license');
    }

    private function licensingBaseUrl(): string
    {
        $lic = $this->config['licensing'] ?? [];
        if (is_array($lic) && is_string($lic['url'] ?? null) && (string) $lic['url'] !== '') {
            return (string) $lic['url'];
        }
        return 'https://licensing.seoscope.cloud';
    }

    private function currentDomain(): string
    {
        $host = $_SERVER['HTTP_HOST'] ?? '';
        $host = is_string($host) ? $host : '';
        $host = strtolower(trim($host));
        $host = preg_replace('~:\\d+$~', '', $host) ?? $host;
        return $host !== '' ? $host : 'localhost';
    }
}

