<?php

namespace App\Core\Currency;

use App\Core\Module\ConfigFieldNormalizer;
use App\Core\Module\ManageableModuleManager;
use App\Core\Module\TestResult;
use App\Models\Setting;

/**
 * PRC-79 — registry + factory for currency rate drivers.
 *
 * Mirrors EmailManager / SmsManager pattern.
 * PRC-327: implements ManageableModuleManager for the universal admin UI.
 */
class CurrencyDriverManager implements ManageableModuleManager
{
    /** @var array<string, class-string<CurrencyDriverInterface>> */
    protected array $drivers = [];

    public function __construct()
    {
        $this->loadDrivers();
    }

    private function loadDrivers(): void
    {
        $this->drivers = [
            'open_er_api' => \App\Modules\CurrencyDriver\OpenErApi\OpenErApi::class,
            'ecb' => \App\Modules\CurrencyDriver\Ecb\Ecb::class,
            'fixer' => \App\Modules\CurrencyDriver\Fixer\Fixer::class,
            'open_exchange_rates' => \App\Modules\CurrencyDriver\OpenExchangeRates\OpenExchangeRates::class,
        ];
    }

    /**
     * @return array<string, array{label:string, requires_api_key:bool}>
     */
    public function getAvailableDrivers(): array
    {
        $out = [];
        foreach ($this->drivers as $key => $class) {
            try {
                $instance = new $class([]);
                $out[$key] = [
                    'name' => $instance->getName(),
                    'label' => $instance->getLabel(),
                    'requires_api_key' => $instance->requiresApiKey(),
                ];
            } catch (\Throwable $e) {
                // skip broken drivers
            }
        }

        return $out;
    }

    public function driver(string $name, ?array $config = null): CurrencyDriverInterface
    {
        if (! isset($this->drivers[$name])) {
            throw new \InvalidArgumentException("Unknown currency driver: {$name}");
        }
        $class = $this->drivers[$name];
        if ($config === null) {
            $config = $this->configFor($name);
        }

        return new $class($config);
    }

    /**
     * Resolve driver config — anahtarlar modülün getConfigFields()'inden (dosya),
     * value'lar module_settings'ten merge edilir (ModuleConfig::resolved, PRC-655).
     * Eski `fx_{name}_api_key` legacy settings okuması kaldırıldı; ayrıca artık
     * yalnız api_key değil TÜM declared field'lar (base_currency/subscription_plan)
     * döner. Credential'lar /admin/currency-providers'tan module_settings'e yazılır.
     */
    protected function configFor(string $name): array
    {
        return \App\Helpers\ModuleConfig::resolved('currency', $name, $this->getConfigFields($name));
    }

    /**
     * @return array<int, string> ordered list of driver keys (failover sequence)
     */
    public function failoverOrder(): array
    {
        $raw = (string) Setting::getValue('fx_provider_failover_order', 'ecb,open_er_api,open_exchange_rates,fixer');
        $list = array_values(array_filter(array_map('trim', explode(',', $raw))));

        // Preserve order, drop unknowns
        return array_values(array_intersect($list, array_keys($this->drivers)));
    }

    // ─── ManageableModuleManager (PRC-327) ──────────────────────────────

    public function discover(): array
    {
        $out = [];
        foreach ($this->drivers as $name => $class) {
            try {
                $instance = new $class([]);
                $out[] = [
                    'name' => $name,
                    'label' => method_exists($instance, 'getLabel') ? $instance->getLabel() : strtoupper($name),
                ];
            } catch (\Throwable $e) {
                // skip
            }
        }

        return $out;
    }

    /**
     * Config field schema — modül sınıfının kendi getConfigFields()'inden okunur
     * (tek kaynak; PRC-655'te manager-hardcoded `match` kaldırıldı). Modüle yeni
     * field eklemek şemaya + resolved config'e anında yansır.
     */
    public function getConfigFields(string $name): array
    {
        if (! isset($this->drivers[$name])) {
            return [];
        }

        try {
            $instance = new $this->drivers[$name]([]);
            $legacy = method_exists($instance, 'getConfigFields') ? $instance->getConfigFields() : [];
        } catch (\Throwable $e) {
            return [];
        }

        return ConfigFieldNormalizer::normalize(is_array($legacy) ? $legacy : []);
    }

    public function test(string $name, array $config): TestResult
    {
        if (! isset($this->drivers[$name])) {
            return TestResult::failure("Unknown driver: {$name}");
        }

        try {
            $driver = new $this->drivers[$name]($config);
            $base = (string) ($config['base_currency'] ?? Setting::getValue('default_currency_code', 'USD'));
            $rates = $driver->fetch(strtoupper($base));

            if (empty($rates)) {
                return TestResult::failure('Driver returned no rates.');
            }
            return TestResult::success(sprintf('OK — %d rates fetched (base %s).', count($rates), $base));
        } catch (\Throwable $e) {
            return TestResult::failure($e->getMessage());
        }
    }
}
