<?php

namespace App\Services\Payments;

use App\Contracts\Payments\PaymentProviderContract;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class ProviderRegistry
{
    protected array $providers = [];

    protected array $configurations = [];

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

    /**
     * Register a payment provider
     */
    public function register(string $name, PaymentProviderContract $provider): void
    {
        $this->providers[$name] = $provider;

        Log::info('Payment provider registered', [
            'provider' => $name,
            'class' => get_class($provider),
        ]);
    }

    /**
     * Get a specific provider
     */
    public function get(string $name): ?PaymentProviderContract
    {
        return $this->providers[$name] ?? null;
    }

    /**
     * Get all registered providers
     */
    public function getAllProviders(): Collection
    {
        return collect($this->providers);
    }

    /**
     * Get only active providers
     */
    public function getActiveProviders(): Collection
    {
        return collect($this->providers)
            ->filter(fn ($provider) => $provider->isActive());
    }

    /**
     * Check if provider exists
     */
    public function has(string $name): bool
    {
        return isset($this->providers[$name]);
    }

    /**
     * Unregister a provider
     */
    public function unregister(string $name): bool
    {
        if (isset($this->providers[$name])) {
            unset($this->providers[$name]);
            Log::info('Payment provider unregistered', ['provider' => $name]);

            return true;
        }

        return false;
    }

    /**
     * Get provider configuration
     */
    public function getConfiguration(string $providerName): array
    {
        return $this->configurations[$providerName] ?? [];
    }

    /**
     * Update provider configuration
     */
    public function updateConfiguration(string $providerName, array $config): void
    {
        $this->configurations[$providerName] = $config;

        Cache::put("payment_config_{$providerName}", $config);

        Log::info('Payment provider configuration updated', [
            'provider' => $providerName,
            'config_keys' => array_keys($config),
        ]);
    }

    /**
     * Get providers that support recurring payments
     */
    public function getRecurringProviders(): Collection
    {
        return $this->getActiveProviders()
            ->filter(fn ($provider) => $provider->supportsRecurring());
    }

    /**
     * Get providers that support one-time payments
     */
    public function getOneTimeProviders(): Collection
    {
        return $this->getActiveProviders()
            ->filter(fn ($provider) => $provider->supportsOneTime());
    }

    /**
     * Get providers that support a specific currency
     */
    public function getProvidersForCurrency(string $currency): Collection
    {
        return $this->getActiveProviders()
            ->filter(function ($provider) use ($currency) {
                return in_array($currency, $provider->getSupportedCurrencies());
            });
    }

    /**
     * Get provider by webhook URL pattern
     */
    public function getProviderByWebhookUrl(string $url): ?PaymentProviderContract
    {
        return $this->getActiveProviders()
            ->first(function ($provider) use ($url) {
                $config = $provider->getConfiguration();
                $webhookUrl = $config['webhook_url'] ?? null;

                return $webhookUrl && str_contains($url, parse_url($webhookUrl, PHP_URL_PATH));
            });
    }

    /**
     * Validate provider health status
     */
    public function validateProviders(): array
    {
        $results = [];

        foreach ($this->providers as $name => $provider) {
            try {
                $isActive = $provider->isActive();
                $config = $provider->getConfiguration();

                $results[$name] = [
                    'active' => $isActive,
                    'configured' => ! empty($config),
                    'supports_recurring' => $provider->supportsRecurring(),
                    'supports_one_time' => $provider->supportsOneTime(),
                    'supported_currencies' => $provider->getSupportedCurrencies(),
                    'last_checked' => now()->toISOString(),
                ];

                if (! $isActive) {
                    Log::warning('Payment provider is inactive', ['provider' => $name]);
                }

            } catch (\Exception $e) {
                $results[$name] = [
                    'active' => false,
                    'error' => $e->getMessage(),
                    'last_checked' => now()->toISOString(),
                ];

                Log::error('Payment provider health check failed', [
                    'provider' => $name,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        return $results;
    }

    /**
     * Get provider statistics
     */
    public function getProviderStats(): array
    {
        $stats = [
            'total_providers' => count($this->providers),
            'active_providers' => 0,
            'recurring_providers' => 0,
            'one_time_providers' => 0,
            'supported_currencies' => [],
            'providers' => [],
        ];

        foreach ($this->providers as $name => $provider) {
            $isActive = $provider->isActive();

            if ($isActive) {
                $stats['active_providers']++;
            }

            if ($provider->supportsRecurring()) {
                $stats['recurring_providers']++;
            }

            if ($provider->supportsOneTime()) {
                $stats['one_time_providers']++;
            }

            $stats['supported_currencies'] = array_merge(
                $stats['supported_currencies'],
                $provider->getSupportedCurrencies()
            );

            $stats['providers'][$name] = [
                'active' => $isActive,
                'class' => get_class($provider),
                'supports_recurring' => $provider->supportsRecurring(),
                'supports_one_time' => $provider->supportsOneTime(),
                'currencies' => $provider->getSupportedCurrencies(),
            ];
        }

        $stats['supported_currencies'] = array_unique($stats['supported_currencies']);

        return $stats;
    }

    /**
     * Load provider configurations from cache/database
     */
    protected function loadConfigurations(): void
    {
        // Load from cache first
        $cachedConfigs = Cache::get('payment_providers_config', []);

        if (empty($cachedConfigs)) {
            // Load from database or config files
            $this->configurations = config('payment.providers', []);

            // Cache for 1 hour
            Cache::put('payment_providers_config', $this->configurations, 3600);
        } else {
            $this->configurations = $cachedConfigs;
        }
    }

    /**
     * Register default providers
     */
    protected function registerDefaultProviders(): void
    {
        // Auto-register providers based on configuration
        $enabledProviders = config('payment.enabled_providers', []);

        foreach ($enabledProviders as $providerName) {
            $this->registerProviderByName($providerName);
        }
    }

    /**
     * Register provider by name using configuration
     */
    protected function registerProviderByName(string $providerName): void
    {
        $providerClass = config("payment.providers.{$providerName}.class");

        if (! $providerClass || ! class_exists($providerClass)) {
            Log::error('Payment provider class not found', [
                'provider' => $providerName,
                'class' => $providerClass,
            ]);

            return;
        }

        try {
            $config = $this->getConfiguration($providerName);
            $provider = new $providerClass($config);

            if ($provider instanceof PaymentProviderContract) {
                $this->register($providerName, $provider);
            } else {
                Log::error('Payment provider does not implement contract', [
                    'provider' => $providerName,
                    'class' => $providerClass,
                ]);
            }

        } catch (\Exception $e) {
            Log::error('Failed to register payment provider', [
                'provider' => $providerName,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Refresh provider (useful for configuration changes)
     */
    public function refreshProvider(string $name): bool
    {
        if (! isset($this->providers[$name])) {
            return false;
        }

        // Unregister current instance
        unset($this->providers[$name]);

        // Re-register with fresh configuration
        $this->registerProviderByName($name);

        return isset($this->providers[$name]);
    }

    /**
     * Enable/disable a provider
     */
    public function toggleProvider(string $name, bool $enabled): bool
    {
        $config = $this->getConfiguration($name);

        if (empty($config)) {
            return false;
        }

        $config['enabled'] = $enabled;
        $this->updateConfiguration($name, $config);

        // Refresh the provider to apply changes
        return $this->refreshProvider($name);
    }

    /**
     * Get provider for fallback
     */
    public function getFallbackProvider(): ?PaymentProviderContract
    {
        $fallbackProvider = config('payment.fallback_provider');

        if ($fallbackProvider && $this->has($fallbackProvider)) {
            $provider = $this->get($fallbackProvider);

            if ($provider && $provider->isActive()) {
                return $provider;
            }
        }

        // Return first active provider as fallback
        return $this->getActiveProviders()->first();
    }
}
