<?php

namespace App\Core\Payment;

use App\Core\Payment\Concerns\GatesInstallmentByCurrency;

abstract class AbstractPayment implements PaymentInterface
{
    use GatesInstallmentByCurrency;

    protected $config;

    protected $isTestMode = true;

    /** Taksit farkını kim uygular: 'procp' (PROCP hesaplar+charge) | 'gateway' (banka/hosted). */
    protected string $installmentSurchargeMode = 'procp';

    public function __construct(array $config = [])
    {
        $this->config = $config;
        $this->isTestMode = $config['test_mode'] ?? true;
    }

    /**
     * Zorunlu fonksiyonlar - alt sınıflar tarafından uygulanmalı
     */
    abstract public function attemptPayment(array $params);

    abstract public function makePayment(array $params);

    abstract public function handlePaymentCallback(array $params);

    abstract public function refundPayment(array $params);

    abstract public function generatePaymentLink(array $params);

    /**
     * Opsiyonel fonksiyonlar için varsayılan implementasyonlar
     */
    public function serializeCardInfo(array $cardInfo)
    {
        // Kart bilgilerini güvenli şekilde serialize et
        return [
            'card_number' => $this->maskCardNumber($cardInfo['card_number'] ?? ''),
            'card_holder' => $cardInfo['card_holder'] ?? '',
            'expiry_month' => $cardInfo['expiry_month'] ?? '',
            'expiry_year' => $cardInfo['expiry_year'] ?? '',
            'card_token' => $cardInfo['card_token'] ?? null,
        ];
    }

    public function performPreFraudCheck(array $params)
    {
        return [
            'status' => 'success',
            'risk_score' => 'low',
            'message' => 'Fraud check passed',
        ];
    }

    public function saveCard(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    public function deleteCard(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    public function payWithSavedCard(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    public function start3DPayment(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    public function complete3DPayment(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    public function testConnection(array $params)
    {
        throw new \Exception(__('modules.payment_function_not_supported'));
    }

    /**
     * Gömülü (external iframe) sunum için embed edilecek URL'i döndürür.
     *
     * Sözleşme: dönen dizide status=success ise 'payment_url' (iframe src) bulunur;
     * aksi halde status=error + message. Controller (handleExternalIframe) bu metodu
     * gateway'e özel dallanma YAPMADAN çağırır — hangi iç akışın (hosted-link vs
     * get-token embed) kullanılacağına MODÜL karar verir (module-is-truth).
     *
     * Varsayılan: hosted-link üreten gateway'ler (PayPal approval-url, PayU redirectUri
     * vb.) için generatePaymentLink doğru embeddable URL'i verir. Frame-friendly ayrı
     * bir embed sayfası olan gateway'ler (ör. PayTR get-token → odeme/guvenli) bu metodu
     * override eder.
     */
    public function getIframeUrl(array $params): array
    {
        return $this->generatePaymentLink($params);
    }

    /**
     * Yardımcı metotlar
     */
    protected function callApi($endpoint, $method = 'POST', $data = [])
    {
        // Ortak API çağrı kodu
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $endpoint);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getDefaultHeaders());

        if (! empty($data)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'http_code' => $httpCode,
            'response' => json_decode($response, true),
            'raw_response' => $response,
        ];
    }

    protected function getDefaultHeaders()
    {
        return [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: Payment Gateway Module',
        ];
    }

    // =========================================================================
    //  Transport Helpers — PRC-888
    // =========================================================================
    //
    //  `callApi()` global JSON gönderiyor; birçok gateway (özellikle TR sanal
    //  POS + aggregatörler) form-urlencoded veya XML ister. Bu helper'lar
    //  transport'u açıkça seçtirir + TLS doğrulama VARSAYILAN AÇIK + timeout.
    //  Modüller `postForm()`/`postXml()`/`postJson()` çağırır.

    /** Form-urlencoded POST (application/x-www-form-urlencoded). */
    protected function postForm(string $url, array $data, array $headers = [], array $options = []): array
    {
        return $this->httpPost($url, $data, 'form', $headers, $options);
    }

    /** Raw XML POST (text/xml). */
    protected function postXml(string $url, string $xml, array $headers = [], array $options = []): array
    {
        return $this->httpPost($url, $xml, 'xml', $headers, $options);
    }

    /** JSON POST (application/json). */
    protected function postJson(string $url, $data, array $headers = [], array $options = []): array
    {
        return $this->httpPost($url, $data, 'json', $headers, $options);
    }

    /**
     * Format-farkında POST. `$format`: json|form|xml|raw.
     *
     * $options: `verify` (bool, default true — TLS peer/host doğrulama),
     * `timeout` (int sn, default 30), `connect_timeout` (int sn, default 10).
     *
     * @return array{http_code:int,response:mixed,raw_response:string,error:?string}
     */
    protected function httpPost(string $url, $body = [], string $format = 'json', array $headers = [], array $options = []): array
    {
        [$payload, $finalHeaders] = $this->buildTransport($format, $body, $headers);

        $ch = curl_init();
        $verify = $options['verify'] ?? true;
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => $finalHeaders,
            CURLOPT_SSL_VERIFYPEER => $verify,
            CURLOPT_SSL_VERIFYHOST => $verify ? 2 : 0,
            CURLOPT_TIMEOUT        => $options['timeout'] ?? 30,
            CURLOPT_CONNECTTIMEOUT => $options['connect_timeout'] ?? 10,
            // Defense-in-depth: yalnız HTTP(S) — file://gopher:// vb. SSRF sınıfını kapat.
            CURLOPT_PROTOCOLS       => CURLPROTO_HTTPS | CURLPROTO_HTTP,
            CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP,
        ]);
        if ($payload !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        }

        $response = curl_exec($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        return [
            'http_code'    => $httpCode,
            'raw_response' => $response === false ? '' : (string) $response,
            'response'     => $format === 'json' ? json_decode((string) $response, true) : $response,
            'error'        => $error !== '' ? $error : null,
        ];
    }

    /**
     * Transport payload + header'ı formata göre kurar (saf — curl'süz, test edilebilir).
     *
     * @return array{0:?string,1:array}  [payload, headers]
     */
    protected function buildTransport(string $format, $body, array $headers = []): array
    {
        $contentType = null;
        switch ($format) {
            case 'form':
                $payload = is_array($body) ? http_build_query($body) : (string) $body;
                $contentType = 'application/x-www-form-urlencoded';
                break;
            case 'xml':
                if (! is_string($body)) {
                    throw new \InvalidArgumentException('XML transport requires a pre-serialized string body.');
                }
                $payload = $body;
                $contentType = 'text/xml; charset=UTF-8';
                break;
            case 'raw':
                $payload = ($body === null) ? null : (string) $body;
                break;
            case 'json':
            default:
                $payload = ($body === null || $body === []) ? null : json_encode($body);
                $contentType = 'application/json';
                break;
        }

        // Content-Type yalnızca caller elle geçmediyse eklenir (modül override edebilir).
        if ($contentType !== null && ! $this->hasHeader($headers, 'content-type')) {
            $headers[] = 'Content-Type: '.$contentType;
        }

        return [$payload, $headers];
    }

    /** Bir header listesinde (`'Name: value'`) belirli bir header adı var mı? */
    protected function hasHeader(array $headers, string $name): bool
    {
        $needle = strtolower($name);
        foreach ($headers as $header) {
            if (! is_string($header)) {
                continue;
            }
            $key = strtolower(trim(explode(':', $header, 2)[0] ?? ''));
            if ($key === $needle) {
                return true;
            }
        }

        return false;
    }

    // =========================================================================
    //  Callback Signature Verification — PRC-888 (fail-CLOSED)
    // =========================================================================
    //
    //  Gateway suite'inin EN YAYGIN + EN TEHLİKELİ açığı: callback/webhook imza
    //  doğrulaması eksik veya fail-open → saldırgan sahte "ödendi" callback'i
    //  gönderip bedava sipariş açtırıyor. Sözleşme: `handlePaymentCallback` bir
    //  ödemeyi "paid" işaretlemeden ÖNCE gateway imzasını `secureCompare()` ile
    //  doğrulamalı; imza eksik/yanlışsa işlemi REDDETMELİ (fail-closed).

    /**
     * Timing- ve tip-güvenli imza karşılaştırması.
     *
     * `hash_equals` sarmalayıcı. Taraflardan biri null/boş ise **false** döner
     * (fail-closed) — böylece eksik imza asla "eşleşti" sayılmaz. `==` ile
     * karşılaştırma (type-juggling `'0e123'==...` + timing) YASAK; bunu kullan.
     */
    protected function secureCompare(?string $known, ?string $received): bool
    {
        if ($known === null || $received === null || $known === '' || $received === '') {
            return false;
        }

        return hash_equals($known, $received);
    }

    /**
     * HMAC imza yapı-taşı. `$binary=true` ise ham binary döner (base64 için).
     */
    protected function hmac(string $algo, string $data, string $key, bool $binary = false): string
    {
        return hash_hmac($algo, $data, $key, $binary);
    }

    protected function maskCardNumber($cardNumber)
    {
        $cardNumber = preg_replace('/\s+/', '', $cardNumber);
        if (strlen($cardNumber) < 6) {
            return str_repeat('*', strlen($cardNumber));
        }

        return substr($cardNumber, 0, 4).str_repeat('*', strlen($cardNumber) - 8).substr($cardNumber, -4);
    }

    public function validateConfig()
    {
        // Modül yapılandırmasını doğrulama
        return [
            'status' => 'success',
            'message' => 'Configuration is valid',
        ];
    }

    /**
     * Modülün desteklediği özellikleri döndürür
     */
    public function getFeatures(): array
    {
        return [
            'refund' => false,
            '3d_secure' => false,
            'save_card' => false,
            'recurring' => false,
            'fraud_check' => false,
            'installment' => false,
        ];
    }

    public function getInstallmentPlans(float $amount, ?string $currencyCode = null): array
    {
        $enabled = filter_var($this->getConfig('installment_enabled', false), FILTER_VALIDATE_BOOLEAN);
        if (! $enabled) {
            return [];
        }

        // PRC-689 — Taksit para birimi geçidi: yalnızca izin verilen para
        // biriminde (varsayılan TRY) taksit sun. Uygun değilse boş dizi → seçici
        // gizlenir (tek çekim). $currencyCode null ise geriye-uyum: kapı uygulanmaz.
        if (! $this->installmentCurrencyAllowed($currencyCode)) {
            return [];
        }

        $rows = json_decode((string) $this->getConfig('installment_plans', '[]'), true);
        if (! is_array($rows)) {
            $rows = [];
        }

        // Tek çekim her zaman ilk.
        // PRC-1057: 'payment_id' carrier — numeric-count gateway'lerde daima null
        // (klasik akış); yalnız PaymentId-tabanlı gateway (iPay88) doldurur.
        $plans = [[
            'count' => 1, 'rate' => 0.0, 'fee' => 0.0, 'surcharge' => 0.0,
            'total' => round($amount, 2), 'monthly' => round($amount, 2),
            'surcharge_applied_by' => $this->installmentSurchargeMode,
            'label' => (string) 1, 'payment_id' => null,
        ]];

        foreach ($rows as $row) {
            $count = (int) ($row['count'] ?? 0);
            if ($count < 2) {
                continue;
            }

            // Miktar-aralığı filtresi (PRC-878): 0/eksik = sınırsız.
            $minAmount = isset($row['min_amount']) ? (float) $row['min_amount'] : 0.0;
            $maxAmount = isset($row['max_amount']) ? (float) $row['max_amount'] : 0.0;
            if ($minAmount > 0 && $amount < $minAmount) {
                continue; // tutarımız alt sınırın altında
            }
            if ($maxAmount > 0 && $amount > $maxAmount) {
                continue; // tutarımız üst sınırın üstünde
            }

            $rate = (float) ($row['rate'] ?? 0);
            $fee = (float) ($row['fee'] ?? 0);

            $surcharge = $this->installmentSurchargeMode === 'procp'
                ? round($amount * ($rate / 100) + $fee, 2)
                : 0.0;
            $total = round($amount + $surcharge, 2);

            $plans[] = [
                'count' => $count,
                'rate' => $rate,
                'fee' => $fee,
                'surcharge' => $surcharge,
                'total' => $total,
                'monthly' => round($total / $count, 2),
                'surcharge_applied_by' => $this->installmentSurchargeMode,
                'label' => (string) $count,
                'payment_id' => null, // PRC-1057: numeric-count planlarda daima null
            ];
        }

        return $plans;
    }

    /** Modüllerin getConfigFields()'te merge edebileceği standart taksit config alanları. */
    protected function installmentConfigFields(): array
    {
        return [
            'installment_enabled' => [
                'type' => 'checkbox',
                'label' => 'Installments Enabled',
                'description' => 'Enable installment options for this gateway',
                'value' => 'false',
            ],
            'installment_plans' => [
                'type' => 'textarea',
                'label' => 'Installment Plans (JSON)',
                'description' => 'JSON array: [{"count":3,"rate":2,"fee":0}] — count (2-36), rate (%), fee (fixed), min_amount, max_amount (optional; 0/absent = unbounded)',
                'value' => '[]',
            ],
            // PRC-689 — Taksitin sunulacağı para birimleri (CSV, ISO kodu). Sepet
            // para birimi bu listede değilse taksit seçici gizlenir (tek çekim).
            'installment_currencies' => [
                'type' => 'text',
                'label' => 'Installment Currencies',
                'description' => 'Comma-separated ISO currency codes where installments are offered (e.g. TRY). Cart currency outside this list → single payment only.',
                'value' => 'TRY',
            ],
        ];
    }

    /**
     * Get module information
     */
    public function getModuleInfo(): array
    {
        // Sınıf adından modül adını al
        $className = get_class($this);
        $moduleName = basename(str_replace('\\', '/', $className));

        // Varsayılan bilgiler
        $defaultInfo = [
            'name' => strtolower($moduleName),
            'label' => $moduleName,
            'description' => $moduleName.' payment gateway',
            'logo' => $this->getModuleLogo($moduleName),
            'version' => '1.0',
            'author' => 'System',
        ];

        // module.json dosyasını kontrol et
        $moduleDir = $this->getModuleDirectory();
        $jsonPath = $moduleDir.'/module.json';

        if (file_exists($jsonPath)) {
            $jsonData = json_decode(file_get_contents($jsonPath), true);
            if (is_array($jsonData)) {
                // JSON verilerini varsayılan verilere ekle (JSON öncelikli)
                return array_merge($defaultInfo, $jsonData);
            }
        }

        return $defaultInfo;
    }

    /**
     * Get module directory path
     */
    protected function getModuleDirectory(): string
    {
        $className = get_class($this);
        $reflector = new \ReflectionClass($className);

        return dirname($reflector->getFileName());
    }

    /**
     * Get module logo path
     */
    protected function getModuleLogo(string $moduleName): string
    {
        $moduleDir = $this->getModuleDirectory();
        $logoExtensions = ['png', 'jpg', 'jpeg', 'svg', 'webp'];

        // Modül klasöründe logo ara
        foreach ($logoExtensions as $extension) {
            $logoPath = $moduleDir.'/logo.'.$extension;
            if (file_exists($logoPath)) {
                // Modül klasöründen public'e erişilebilir path döndür
                return '/modules/payment/'.strtolower($moduleName).'/logo.'.$extension;
            }
        }

        // Logo bulunamazsa varsayılan döndür
        return '/assets/img/payment-gateways/default.svg';
    }

    /**
     * Get configuration fields
     */
    public function getConfigFields(): array
    {
        $features = $this->getFeatures();
        $fields = [
            'status' => [
                'type' => 'select',
                'label' => 'Status',
                'description' => 'Gateway status (active/inactive)',
                'required' => true,
                'value' => 'inactive',
                'options' => [
                    'active' => 'Active',
                    'inactive' => 'Inactive',
                ],
            ],
            'test_mode' => [
                'type' => 'checkbox',
                'label' => 'Test Mode',
                'description' => 'Enable test mode for this gateway',
                'value' => 'true',
            ],
            'display_name' => [
                'type' => 'text',
                'label' => 'Display Name',
                'description' => 'Custom display name for this gateway',
                'required' => false,
                'value' => '',
                'placeholder' => 'Enter custom display name',
            ],
            'sort_order' => [
                'type' => 'number',
                'label' => 'Sort Order',
                'description' => 'Display order (lower numbers appear first)',
                'required' => false,
                'value' => '0',
                'placeholder' => '0',
            ],
        ];

        // Para birimi çevirici ekle - sistemdeki aktif currency'lerden
        $currencyOptions = [''];
        try {
            $activeCurrencies = \App\Models\Currency::where('status', true)
                ->orderBy('is_default', 'desc')
                ->orderBy('code')
                ->get();

            $currencyOptions = ['' => 'No conversion'];
            foreach ($activeCurrencies as $currency) {
                $label = $currency->name.' ('.$currency->code.')';
                if ($currency->is_default) {
                    $label .= ' - Default';
                }
                $currencyOptions[$currency->code] = $label;
            }
        } catch (\Exception $e) {
            // Hata durumunda varsayılan seçenekler
            $currencyOptions = [
                '' => 'No conversion',
                'USD' => 'US Dollar (USD)',
                'EUR' => 'Euro (EUR)',
                'TRY' => 'Turkish Lira (TRY)',
            ];
        }

        $fields['currency_converter'] = [
            'type' => 'select',
            'label' => 'Convert Currency To',
            'description' => 'Convert payments to this currency',
            'required' => false,
            'value' => '',
            'options' => $currencyOptions,
        ];

        // 2D/3D güvenlik seçimi
        if ($features['3d_secure'] ?? false) {
            $fields['security_type'] = [
                'type' => 'select',
                'label' => 'Security Type',
                'description' => 'Choose payment security method',
                'required' => true,
                'value' => '3d',
                'options' => [
                    '2d' => '2D Secure (Faster)',
                    '3d' => '3D Secure (More Secure)',
                    'auto' => 'Auto (Let gateway decide)',
                ],
            ];
        }

        // Kart kaydetme ayarları
        if ($features['save_card'] ?? false) {
            $fields['allow_save_card'] = [
                'type' => 'checkbox',
                'label' => 'Allow Card Saving',
                'description' => 'Allow customers to save their payment cards',
                'value' => 'true',
            ];

            $fields['allow_saved_card_payment'] = [
                'type' => 'checkbox',
                'label' => 'Allow Saved Card Payment',
                'description' => 'Allow payments with previously saved cards',
                'value' => 'true',
            ];
        }

        // Ücret ayarları
        $fields['fee_percentage'] = [
            'type' => 'number',
            'label' => 'Fee Percentage (%)',
            'description' => 'Percentage fee to charge for this gateway',
            'required' => false,
            'value' => '0',
            'placeholder' => '0.00',
            'step' => '0.01',
        ];

        $fields['fee_fixed'] = [
            'type' => 'number',
            'label' => 'Fixed Fee',
            'description' => 'Fixed fee to charge for this gateway',
            'required' => false,
            'value' => '0',
            'placeholder' => '0.00',
            'step' => '0.01',
        ];

        return $fields;
    }

    /**
     * Test modunda mı?
     *
     * test_mode config alanı checkbox olduğu için DB'de string 'true'/'false' olarak saklanır.
     * Ham property'yi bool'a zorla; (bool)'false' = true olduğu için filter_var kullanılır:
     *   filter_var('false', FILTER_VALIDATE_BOOLEAN) === false  ✓
     *   filter_var('true',  FILTER_VALIDATE_BOOLEAN) === true   ✓
     *   filter_var(true,    FILTER_VALIDATE_BOOLEAN) === true   ✓ (bare gateway default)
     */
    public function isTestMode(): bool
    {
        return filter_var($this->isTestMode, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * Konfigürasyon değerini al
     *
     * @param  mixed  $default
     * @return mixed
     */
    protected function getConfig(string $key, $default = null)
    {
        return $this->config[$key] ?? $default;
    }

    /**
     * Tüm konfigürasyonu al
     */
    protected function getAllConfig(): array
    {
        return $this->config;
    }

    /**
     * Konfigürasyon değerini ayarla
     *
     * @param  mixed  $value
     */
    protected function setConfig(string $key, $value): void
    {
        $this->config[$key] = $value;
    }
}
