<?php

namespace MintyPHP\Service\Settings;

class SettingsApiPolicyGateway
{
    private const API_TOKEN_DEFAULT_TTL_DAYS_FALLBACK = 90;
    private const API_TOKEN_MAX_TTL_DAYS_FALLBACK = 365;
    private const API_TOKEN_TTL_DAYS_MIN = 1;
    private const API_TOKEN_TTL_DAYS_HARD_MAX = 3650;

    public function __construct(private readonly SettingsMetadataGateway $settingsMetadataGateway)
    {
    }

    public function getApiTokenDefaultTtlDays(): int
    {
        $maxDays = $this->getApiTokenMaxTtlDays();
        $value = $this->settingsMetadataGateway->getInt(SettingKeys::API_TOKEN_DEFAULT_TTL_DAYS_KEY);
        if ($value !== null && $value >= self::API_TOKEN_TTL_DAYS_MIN && $value <= self::API_TOKEN_TTL_DAYS_HARD_MAX) {
            return min($value, $maxDays);
        }

        return min(self::API_TOKEN_DEFAULT_TTL_DAYS_FALLBACK, $maxDays);
    }

    public function setApiTokenDefaultTtlDays(?int $days, ?string $description = null): bool
    {
        $value = $days ?? self::API_TOKEN_DEFAULT_TTL_DAYS_FALLBACK;
        if ($value < self::API_TOKEN_TTL_DAYS_MIN || $value > self::API_TOKEN_TTL_DAYS_HARD_MAX) {
            return false;
        }
        if ($value > $this->getApiTokenMaxTtlDays()) {
            return false;
        }

        $desc = $description ?? 'setting.api_token_default_ttl_days';
        return $this->settingsMetadataGateway->set(SettingKeys::API_TOKEN_DEFAULT_TTL_DAYS_KEY, (string) $value, $desc);
    }

    public function getApiTokenMaxTtlDays(): int
    {
        $value = $this->settingsMetadataGateway->getInt(SettingKeys::API_TOKEN_MAX_TTL_DAYS_KEY);
        if ($value !== null && $value >= self::API_TOKEN_TTL_DAYS_MIN && $value <= self::API_TOKEN_TTL_DAYS_HARD_MAX) {
            return $value;
        }

        return self::API_TOKEN_MAX_TTL_DAYS_FALLBACK;
    }

    public function setApiTokenMaxTtlDays(?int $days, ?string $description = null): bool
    {
        $value = $days ?? self::API_TOKEN_MAX_TTL_DAYS_FALLBACK;
        if ($value < self::API_TOKEN_TTL_DAYS_MIN || $value > self::API_TOKEN_TTL_DAYS_HARD_MAX) {
            return false;
        }

        $currentDefault = $this->settingsMetadataGateway->getInt(SettingKeys::API_TOKEN_DEFAULT_TTL_DAYS_KEY);
        if ($currentDefault !== null && $currentDefault > $value) {
            $defaultUpdated = $this->settingsMetadataGateway->set(
                SettingKeys::API_TOKEN_DEFAULT_TTL_DAYS_KEY,
                (string) $value,
                'setting.api_token_default_ttl_days'
            );
            if (!$defaultUpdated) {
                return false;
            }
        }

        $desc = $description ?? 'setting.api_token_max_ttl_days';
        return $this->settingsMetadataGateway->set(SettingKeys::API_TOKEN_MAX_TTL_DAYS_KEY, (string) $value, $desc);
    }

    public function getApiCorsAllowedOrigins(): array
    {
        $stored = $this->settingsMetadataGateway->getValue(SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY);
        if ($stored === null || trim($stored) === '') {
            return [];
        }

        return $this->parseCorsOrigins($stored, false) ?? [];
    }

    public function getApiCorsAllowedOriginsText(): string
    {
        $origins = $this->getApiCorsAllowedOrigins();
        return implode("\n", $origins);
    }

    public function setApiCorsAllowedOrigins(?string $rawOrigins, ?string $description = null): bool
    {
        $value = (string) ($rawOrigins ?? '');
        if (trim($value) === '') {
            return $this->settingsMetadataGateway->set(
                SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY,
                null,
                $description ?? 'setting.api_cors_allowed_origins'
            );
        }

        $origins = $this->parseCorsOrigins($value, true);
        if ($origins === null) {
            return false;
        }

        return $this->settingsMetadataGateway->set(
            SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY,
            implode("\n", $origins),
            $description ?? 'setting.api_cors_allowed_origins'
        );
    }

    private function parseCorsOrigins(string $rawOrigins, bool $strict): ?array
    {
        $parts = preg_split('/[\r\n,]+/', $rawOrigins) ?: [];
        $normalizedOrigins = [];
        foreach ($parts as $part) {
            $origin = $this->normalizeCorsOrigin($part);
            if ($origin === null) {
                if ($strict && trim((string) $part) !== '') {
                    return null;
                }
                continue;
            }
            $normalizedOrigins[$origin] = true;
        }

        return array_keys($normalizedOrigins);
    }

    private function normalizeCorsOrigin(string $origin): ?string
    {
        $origin = trim($origin);
        if ($origin === '') {
            return null;
        }

        $origin = rtrim($origin, '/');
        if ($origin === '') {
            return null;
        }

        $parsed = parse_url($origin);
        if (!is_array($parsed)) {
            return null;
        }

        $scheme = strtolower((string) ($parsed['scheme'] ?? ''));
        $host = strtolower((string) ($parsed['host'] ?? ''));
        $port = isset($parsed['port']) ? (int) $parsed['port'] : null;

        if (!in_array($scheme, ['http', 'https'], true) || $host === '') {
            return null;
        }

        if (
            isset($parsed['path'])
            || isset($parsed['query'])
            || isset($parsed['fragment'])
            || isset($parsed['user'])
            || isset($parsed['pass'])
        ) {
            return null;
        }

        $defaultPort = ($scheme === 'https') ? 443 : 80;
        $portSuffix = ($port !== null && $port !== $defaultPort) ? ':' . $port : '';
        return $scheme . '://' . $host . $portSuffix;
    }
}
