<?php

declare(strict_types=1);

namespace SeoScope\Services;

use SeoScope\Repositories\SettingsRepository;

/**
 * Per-user AI provider credentials and defaults (insights not implemented yet).
 */
final class AiSettingsService
{
    public const PROVIDER_OPENAI = 'openai';

    public const PROVIDER_ANTHROPIC = 'anthropic';

    public const KEY_ENABLED = 'ai.enabled';

    public const KEY_PROVIDER = 'ai.provider';

    public const KEY_OPENAI_API_KEY = 'ai.openai.api_key';

    public const KEY_ANTHROPIC_API_KEY = 'ai.anthropic.api_key';

    public const KEY_OPENAI_MODEL = 'ai.openai.model';

    public const KEY_ANTHROPIC_MODEL = 'ai.anthropic.model';

    public const KEY_OPENAI_TOKEN_LIMIT_MONTHLY = 'ai.openai.token_limit_monthly';

    public const KEY_ANTHROPIC_TOKEN_LIMIT_MONTHLY = 'ai.anthropic.token_limit_monthly';

    public const DEFAULT_OPENAI_MODEL = 'gpt-4o-mini';

    public const DEFAULT_ANTHROPIC_MODEL = 'claude-3-5-haiku-20241022';

    public const MASKED_SECRET = '••••••••••••••••••••••••';

    /** @var list<string> */
    public const PROVIDERS = [
        self::PROVIDER_OPENAI,
        self::PROVIDER_ANTHROPIC,
    ];

    public function __construct(
        private SettingsRepository $repo
    ) {}

    /**
     * @return array{
     *   enabled:bool,
     *   provider:string,
     *   openai_api_key_display:string,
     *   anthropic_api_key_display:string,
     *   openai_model:string,
     *   anthropic_model:string,
     *   has_openai_key:bool,
     *   has_anthropic_key:bool,
     *   active_provider_configured:bool,
     *   openai_token_limit_monthly:int,
     *   anthropic_token_limit_monthly:int
     * }
     */
    public function formSnapshot(): array
    {
        $provider = $this->normalizeProvider((string) ($this->repo->get(self::KEY_PROVIDER) ?? self::PROVIDER_OPENAI));
        $enabled = ($this->repo->get(self::KEY_ENABLED) ?? '') === '1';
        $hasOpenai = $this->hasStoredKey(self::KEY_OPENAI_API_KEY);
        $hasAnthropic = $this->hasStoredKey(self::KEY_ANTHROPIC_API_KEY);
        $activeConfigured = $provider === self::PROVIDER_ANTHROPIC ? $hasAnthropic : $hasOpenai;

        return [
            'enabled' => $enabled,
            'provider' => $provider,
            'openai_api_key_display' => $hasOpenai ? self::MASKED_SECRET : '',
            'anthropic_api_key_display' => $hasAnthropic ? self::MASKED_SECRET : '',
            'openai_model' => $this->openaiModel(),
            'anthropic_model' => $this->anthropicModel(),
            'has_openai_key' => $hasOpenai,
            'has_anthropic_key' => $hasAnthropic,
            'active_provider_configured' => $activeConfigured,
            'openai_token_limit_monthly' => $this->openaiMonthlyTokenLimit(),
            'anthropic_token_limit_monthly' => $this->anthropicMonthlyTokenLimit(),
        ];
    }

    public function isEnabled(): bool
    {
        return ($this->repo->get(self::KEY_ENABLED) ?? '') === '1';
    }

    public function setEnabled(bool $enabled): void
    {
        $this->repo->set(self::KEY_ENABLED, $enabled ? '1' : '0');
    }

    public function provider(): string
    {
        return $this->normalizeProvider((string) ($this->repo->get(self::KEY_PROVIDER) ?? self::PROVIDER_OPENAI));
    }

    public function openaiModel(): string
    {
        $m = trim((string) ($this->repo->get(self::KEY_OPENAI_MODEL) ?? ''));

        return $m !== '' ? $m : self::DEFAULT_OPENAI_MODEL;
    }

    public function anthropicModel(): string
    {
        $m = trim((string) ($this->repo->get(self::KEY_ANTHROPIC_MODEL) ?? ''));

        return $m !== '' ? $m : self::DEFAULT_ANTHROPIC_MODEL;
    }

    /** Max total tokens per calendar month for OpenAI (0 = unlimited). */
    public function openaiMonthlyTokenLimit(): int
    {
        return self::parseTokenLimit($this->repo->get(self::KEY_OPENAI_TOKEN_LIMIT_MONTHLY));
    }

    /** Max total tokens per calendar month for Anthropic (0 = unlimited). */
    public function anthropicMonthlyTokenLimit(): int
    {
        return self::parseTokenLimit($this->repo->get(self::KEY_ANTHROPIC_TOKEN_LIMIT_MONTHLY));
    }

    public function apiKeyForProvider(?string $provider = null): ?string
    {
        $p = $this->normalizeProvider($provider ?? $this->provider());
        $name = $p === self::PROVIDER_ANTHROPIC ? self::KEY_ANTHROPIC_API_KEY : self::KEY_OPENAI_API_KEY;
        $key = $this->repo->getSecret($name);

        return is_string($key) && trim($key) !== '' ? trim($key) : null;
    }

    public function isReady(): bool
    {
        return $this->isEnabled() && $this->apiKeyForProvider() !== null;
    }

    /**
     * @param array<string, mixed> $post
     */
    public function saveFromPost(array $post): void
    {
        if (array_key_exists('ai_enabled', $post)) {
            $this->setEnabled(!empty($post['ai_enabled']));
        }
        $provider = $this->normalizeProvider((string) ($post['ai_provider'] ?? self::PROVIDER_OPENAI));

        $this->repo->set(self::KEY_PROVIDER, $provider);

        $openaiModel = trim((string) ($post['openai_model'] ?? ''));
        $anthropicModel = trim((string) ($post['anthropic_model'] ?? ''));
        $this->repo->set(self::KEY_OPENAI_MODEL, $openaiModel !== '' ? $openaiModel : null);
        $this->repo->set(self::KEY_ANTHROPIC_MODEL, $anthropicModel !== '' ? $anthropicModel : null);

        $this->saveKeyFromPost(self::KEY_OPENAI_API_KEY, (string) ($post['openai_api_key'] ?? ''));
        $this->saveKeyFromPost(self::KEY_ANTHROPIC_API_KEY, (string) ($post['anthropic_api_key'] ?? ''));

        $this->repo->set(
            self::KEY_OPENAI_TOKEN_LIMIT_MONTHLY,
            self::normalizeTokenLimitPost((string) ($post['openai_token_limit_monthly'] ?? ''))
        );
        $this->repo->set(
            self::KEY_ANTHROPIC_TOKEN_LIMIT_MONTHLY,
            self::normalizeTokenLimitPost((string) ($post['anthropic_token_limit_monthly'] ?? ''))
        );
    }

    private function saveKeyFromPost(string $settingName, string $submitted): void
    {
        $submitted = trim($submitted);
        if ($submitted === '' || $submitted === self::MASKED_SECRET) {
            return;
        }
        $this->repo->setSecret($settingName, $submitted);
    }

    private function hasStoredKey(string $name): bool
    {
        return trim((string) ($this->repo->get($name) ?? '')) !== '';
    }

    public function normalizeProviderPublic(string $raw): string
    {
        return $this->normalizeProvider($raw);
    }

    private function normalizeProvider(string $raw): string
    {
        $raw = strtolower(trim($raw));

        return in_array($raw, self::PROVIDERS, true) ? $raw : self::PROVIDER_OPENAI;
    }

    private static function parseTokenLimit(mixed $raw): int
    {
        if ($raw === null || $raw === '') {
            return 0;
        }
        $n = is_numeric($raw) ? (int) $raw : 0;

        return $n > 0 ? min($n, 2_000_000_000) : 0;
    }

    private static function normalizeTokenLimitPost(string $raw): ?string
    {
        $raw = trim($raw);
        if ($raw === '') {
            return '0';
        }
        if (!is_numeric($raw)) {
            return '0';
        }
        $n = (int) $raw;

        return $n > 0 ? (string) min($n, 2_000_000_000) : '0';
    }
}
