<?php
declare(strict_types=1);

namespace App\Models;

use Throwable;

class PaymentProvider extends BaseModel
{
    protected string $table = 'payment_providers';

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

    public function listForAdmin(): array
    {
        $statement = $this->db()->query('SELECT * FROM payment_providers ORDER BY is_active DESC, id ASC');
        $providers = [];

        foreach ($statement->fetchAll() as $row) {
            $row['config'] = $this->decodeConfig((string) ($row['config_json'] ?? '{}'));
            $row['config_masked'] = $this->maskConfig($row['config']);
            $providers[] = $row;
        }

        return $providers;
    }

    public function updateFromAdmin(array $input): array
    {
        $providerKey = $this->normalizeKey((string) ($input['provider_key'] ?? 'test'));
        $existing = $this->findByKey($providerKey);
        $existingConfig = $existing ? $this->decodeConfig((string) ($existing['config_json'] ?? '{}')) : [];
        $config = $this->configFromInput($input, $existingConfig);
        $isActive = !empty($input['is_active']) ? 1 : 0;
        $isTestMode = !empty($input['is_test_mode']) ? 1 : 0;

        $this->db()->beginTransaction();

        try {
            if ($isActive === 1) {
                $this->db()->exec('UPDATE payment_providers SET is_active = 0, updated_at = NOW()');
            }

            $statement = $this->db()->prepare(
                'INSERT INTO payment_providers
                    (provider_key, provider_name, is_active, is_test_mode, config_json, created_at, updated_at)
                 VALUES
                    (:provider_key, :provider_name, :is_active, :is_test_mode, :config_json, NOW(), NOW())
                 ON DUPLICATE KEY UPDATE
                    provider_name = VALUES(provider_name),
                    is_active = VALUES(is_active),
                    is_test_mode = VALUES(is_test_mode),
                    config_json = VALUES(config_json),
                    updated_at = NOW()'
            );
            $statement->execute([
                'provider_key' => $providerKey,
                'provider_name' => self::KNOWN_PROVIDERS[$providerKey],
                'is_active' => $isActive,
                'is_test_mode' => $isTestMode,
                'config_json' => json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            ]);

            if ($this->activeCount() === 0) {
                $fallback = $this->db()->prepare('UPDATE payment_providers SET is_active = 1, is_test_mode = 1, updated_at = NOW() WHERE provider_key = "test"');
                $fallback->execute();
            }

            $this->db()->commit();
        } catch (Throwable $exception) {
            if ($this->db()->inTransaction()) {
                $this->db()->rollBack();
            }

            throw $exception;
        }

        return $this->findByKey($providerKey) ?? [];
    }

    public function findByKey(string $providerKey): ?array
    {
        $statement = $this->db()->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;
    }

    public function knownProviders(): array
    {
        return self::KNOWN_PROVIDERS;
    }

    private function configFromInput(array $input, array $existing): array
    {
        $config = $existing;
        $plainFields = [
            'merchant_id',
            'merchant_key',
            'api_key',
            'callback_url',
            'success_url',
            'failed_url',
            'bank_code',
        ];
        $secretFields = [
            'api_secret',
            'merchant_salt',
            'test_secret',
        ];

        foreach ($plainFields as $field) {
            $config[$field] = trim((string) ($input[$field] ?? ''));
        }

        foreach ($secretFields as $field) {
            $value = trim((string) ($input[$field] ?? ''));
            if ($value !== '' && $value !== '********') {
                $config[$field] = $value;
            }
        }

        $config['installment_enabled'] = !empty($input['installment_enabled']);
        $config['three_d_secure_enabled'] = !empty($input['three_d_secure_enabled']);
        $config['saved_card_enabled'] = !empty($input['saved_card_enabled']);
        $config['tokenization_enabled'] = !empty($input['tokenization_enabled']);
        $config['secret_storage_todo'] = 'Production icin app key tabanli encryption eklenecek; secret degerleri loglanmaz ve UI degeri maskeler.';

        return $config;
    }

    private function maskConfig(array $config): array
    {
        foreach (['api_secret', 'merchant_salt', 'test_secret'] as $field) {
            if (!empty($config[$field])) {
                $config[$field] = '********';
            }
        }

        return $config;
    }

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

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

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

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

    private function activeCount(): int
    {
        return (int) $this->db()->query('SELECT COUNT(*) FROM payment_providers WHERE is_active = 1')->fetchColumn();
    }
}
