<?php
declare(strict_types=1);

namespace App\Services\Payment;

use App\Core\Database;
use Throwable;

class PaymentProviderManager
{
    private const PROVIDERS = [
        'test' => TestPaymentProvider::class,
        'paytr' => PayTRPaymentProvider::class,
        'iyzico' => IyzicoPaymentProvider::class,
        'param' => ParamPaymentProvider::class,
        'sipay' => SipayPaymentProvider::class,
        'moka' => MokaPaymentProvider::class,
        'paycell' => PaycellPaymentProvider::class,
        'bank_pos' => BankPosPaymentProvider::class,
    ];

    public function __construct(private array $appConfig = [])
    {
    }

    public function active(): PaymentProviderInterface
    {
        if ($this->forceTestProvider()) {
            return $this->make('test', [], true);
        }

        $row = $this->activeRow();
        if ($row === null) {
            return $this->make((string) ($this->appConfig['PAYMENT_PROVIDER'] ?? 'test'), [], (bool) ($this->appConfig['PAYMENT_TEST_MODE'] ?? true));
        }

        return $this->make(
            (string) $row['provider_key'],
            $this->decodeConfig((string) ($row['config_json'] ?? '{}')),
            (bool) ((int) ($row['is_test_mode'] ?? 1))
        );
    }

    public function activeKey(): string
    {
        if ($this->forceTestProvider()) {
            return 'test';
        }

        $row = $this->activeRow();

        return $row ? (string) $row['provider_key'] : (string) ($this->appConfig['PAYMENT_PROVIDER'] ?? 'test');
    }

    public function get(string $providerKey): PaymentProviderInterface
    {
        $providerKey = $this->normalizeKey($providerKey);
        $row = $this->rowByKey($providerKey);

        return $this->make(
            $providerKey,
            $row ? $this->decodeConfig((string) ($row['config_json'] ?? '{}')) : [],
            $row ? (bool) ((int) ($row['is_test_mode'] ?? 1)) : true
        );
    }

    public function activeRow(): ?array
    {
        try {
            $statement = Database::connection($this->appConfig ?: null)->query(
                'SELECT * FROM payment_providers WHERE is_active = 1 ORDER BY id ASC LIMIT 1'
            );
            $row = $statement->fetch();

            return $row ?: null;
        } catch (Throwable) {
            return null;
        }
    }

    public function rowByKey(string $providerKey): ?array
    {
        try {
            $statement = Database::connection($this->appConfig ?: null)->prepare(
                'SELECT * FROM payment_providers WHERE provider_key = :provider_key LIMIT 1'
            );
            $statement->execute(['provider_key' => $this->normalizeKey($providerKey)]);
            $row = $statement->fetch();

            return $row ?: null;
        } catch (Throwable) {
            return null;
        }
    }

    public function providerOptions(): array
    {
        return array_keys(self::PROVIDERS);
    }

    private function make(string $providerKey, array $config, bool $testMode): PaymentProviderInterface
    {
        $providerKey = $this->normalizeKey($providerKey);
        $class = self::PROVIDERS[$providerKey] ?? TestPaymentProvider::class;

        return new $class($config, $testMode);
    }

    private function normalizeKey(string $providerKey): string
    {
        $providerKey = strtolower(trim($providerKey));

        return array_key_exists($providerKey, self::PROVIDERS) ? $providerKey : 'test';
    }

    private function decodeConfig(string $json): array
    {
        $decoded = json_decode($json, true);

        return is_array($decoded) ? $decoded : [];
    }

    private function forceTestProvider(): bool
    {
        return !empty($this->appConfig['PAYMENT_TEST_MODE'])
            && ((string) ($this->appConfig['APP_ENV'] ?? '') === 'local' || !empty($this->appConfig['FEATURE_TEST_MODE']));
    }
}
