<?php

namespace App\Modules\Payment\GarantiBank;

use App\Core\Payment\AbstractPayment;

class GarantiBank extends AbstractPayment
{
    protected $apiUrl;

    protected $threeDGatewayUrl;

    public function __construct(array $config = [])
    {
        parent::__construct($config);

        $this->apiUrl = $this->isTestMode()
            ? 'https://sanalposprovtest.garanti.com.tr/VPServlet'
            : 'https://sanalposprov.garanti.com.tr/VPServlet';

        $this->threeDGatewayUrl = $this->isTestMode()
            ? 'https://sanalposprovtest.garanti.com.tr/servlet/gt3dengine'
            : 'https://sanalposprov.garanti.com.tr/servlet/gt3dengine';
    }

    /**
     * Odeme girisimi baslatir
     *
     * @return array
     */
    public function attemptPayment(array $params)
    {
        $requiredParams = ['amount', 'currency', 'order_id', 'customer_email', 'card_number', 'exp_month', 'exp_year', 'cvv'];

        foreach ($requiredParams as $param) {
            if (! isset($params[$param]) || empty($params[$param])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.garantibank_missing_param', ['param' => $param]),
                ];
            }
        }

        // Fraud kontrol
        $fraudCheck = $this->performPreFraudCheck($params);
        if ($fraudCheck['status'] === 'error') {
            return $fraudCheck;
        }

        return [
            'status' => 'ready',
            'message' => __('admin/modules.garantibank_ready'),
            'data' => [
                'payment_method' => 'garantibank',
                'amount' => $params['amount'],
                'currency' => $params['currency'],
                'order_id' => $params['order_id'],
            ],
        ];
    }

    /**
     * Odeme islemini gerceklestirir (2D)
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $terminalId = $this->getConfig('terminal_id');
        $merchantId = $this->getConfig('merchant_id');
        $provisionUser = $this->getConfig('provision_user', 'PROVAUT');
        $currencyCode = $this->getConfig('currency_code', '949');

        $orderId = $params['order_id'];
        $amount = (int) round($params['amount'] * 100); // Kurus cinsine cevir
        $cardNumber = str_replace([' ', '-'], '', $params['card_number']);
        $expDate = str_pad($params['exp_month'], 2, '0', STR_PAD_LEFT) . substr($params['exp_year'], -2);
        $cvv = $params['cvv'];
        $customerEmail = $params['customer_email'] ?? '';
        $customerIp = $params['client_ip'] ?? $params['user_ip'] ?? request()->ip();

        // Taksit: platform 'installment_count' geçer (peşin=1). <2 ise BOŞ (peşin) →
        // GVPS <Card><Installment> boş bırakılır. WiseCP $installment<1 → '' ile aynı mantık.
        $installment = (int) ($params['installment'] ?? $params['installment_count'] ?? 0);
        $installmentStr = $installment < 2 ? '' : (string) $installment;

        $securityData = $this->generateSecurityData();
        $hashData = $this->generateHashData($orderId, $terminalId, $cardNumber, (string) $amount, $securityData);

        $xml = $this->buildGvpXml([
            'mode' => $this->isTestMode() ? 'TEST' : 'PROD',
            'terminal_id' => $terminalId,
            'merchant_id' => $merchantId,
            'provision_user' => $provisionUser,
            'hash_data' => $hashData,
            'card_number' => $cardNumber,
            'expire_date' => $expDate,
            'cvv' => $cvv,
            'order_id' => $orderId,
            'amount' => (string) $amount,
            'currency_code' => $currencyCode,
            'customer_email' => $customerEmail,
            'customer_ip' => $customerIp,
            'transaction_type' => 'sales',
            'cardholder_present_code' => '0',
            'installment' => $installmentStr,
        ]);

        $response = $this->callBankApi($this->apiUrl, $xml);
        $parsed = $this->parseGvpResponse($response);

        if ($parsed && ($parsed['ReasonCode'] ?? '') === '00') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.garantibank_payment_success'),
                'transaction_id' => $parsed['RetrefNum'] ?? $orderId,
                'auth_code' => $parsed['AuthCode'] ?? '',
                'data' => $parsed,
            ];
        }

        return [
            'status' => 'error',
            'message' => $parsed['ErrorMsg'] ?? $parsed['ReasonCode'] ?? __('admin/modules.garantibank_payment_failed'),
            'data' => $parsed,
        ];
    }

    /**
     * 3D Secure odeme baslatir
     *
     * @return array
     */
    public function start3DPayment(array $params)
    {
        $terminalId = $this->getConfig('terminal_id');
        $merchantId = $this->getConfig('merchant_id');
        $storeKey = $this->getConfig('store_key');
        $currencyCode = $this->getConfig('currency_code', '949');
        // Sanal POS 3D güvenlik tipi (WiseCP config 'type': 3D_FULL/3D_PAY/3D_HALF)
        $securityLevel = $this->getConfig('security_level', '3D_PAY');

        $orderId = $params['order_id'];
        $amount = (int) round($params['amount'] * 100); // Kurus cinsine cevir
        $successUrl = $params['success_url'];
        $errorUrl = $params['fail_url'] ?? $params['error_url'] ?? $params['success_url'];

        // Taksit: platform 'installment_count' geçer (peşin=1); WiseCP 'installment' ile
        // uyumlu fallback. <2 ise BOŞ string (peşin, WiseCP Garanti.php:128) — hash'te de
        // bu şekilde girer, txninstallmentcount da boş kalır. Taksit imzalı alandadır (tamper-proof).
        $installment = (int) ($params['installment'] ?? $params['installment_count'] ?? 0);
        $installmentStr = $installment < 2 ? '' : (string) $installment;

        $cardNumber = str_replace([' ', '-'], '', $params['card_number']);
        $cvv = $params['cvv'];

        // WiseCP Garanti.php:130-131 — SecurityData=sha1(password+padded_tid),
        // 3D HashData=sha1(tid+orderid+amount+successurl+errorurl+'sales'+installment+storekey+SecurityData)
        $securityData = $this->generateSecurityData();
        $secure3dHash = $this->build3DHash(
            $terminalId,
            $orderId,
            (string) $amount,
            $successUrl,
            $errorUrl,
            $installmentStr,
            $storeKey,
            $securityData
        );

        $formData = [
            'secure3dsecuritylevel' => $securityLevel,
            'mode' => $this->isTestMode() ? 'TEST' : 'PROD',
            'apiversion' => 'v0.01',
            'terminalprovuserid' => $this->getConfig('provision_user', 'PROVAUT'),
            'terminaluserid' => $this->getConfig('provision_user', 'PROVAUT'),
            'terminalmerchantid' => $merchantId,
            'terminalid' => $terminalId,
            'txntype' => 'sales',
            'txnamount' => (string) $amount,
            'txncurrencycode' => $currencyCode,
            'txninstallmentcount' => $installmentStr,
            'orderid' => $orderId,
            'successurl' => $successUrl,
            'errorurl' => $errorUrl,
            'customeremailaddress' => $params['customer_email'] ?? '',
            'customeripaddress' => $params['client_ip'] ?? $params['user_ip'] ?? request()->ip(),
            'secure3dhash' => $secure3dHash,
            'cardnumber' => $cardNumber,
            'cardexpiredatemonth' => str_pad($params['exp_month'], 2, '0', STR_PAD_LEFT),
            'cardexpiredateyear' => substr($params['exp_year'], -2),
            'cardcvv2' => $cvv,
        ];

        return [
            'status' => '3d_redirect',
            'message' => __('admin/modules.garantibank_3d_redirect'),
            'redirect_url' => $this->threeDGatewayUrl,
            'method' => 'POST',
            'form_data' => $formData,
            'data' => [
                'order_id' => $orderId,
                'amount' => $amount,
            ],
        ];
    }

    /**
     * 3D Secure odeme tamamlar
     *
     * @return array
     */
    public function complete3DPayment(array $params)
    {
        // GÜVENLİK (fail-CLOSED): banka 3D dönüşünün hash/hashparams imzasını
        // provizyona GİTMEDEN ÖNCE store_key ile yeniden hesaplayıp doğrula
        // (WiseCP Garanti.php:220 check_hash). İmza yoksa/yanlışsa ödeme BAŞARISIZ.
        if (! $this->verifyCallbackSignature($params)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_signature_invalid'),
                'data' => ['mdstatus' => $params['mdstatus'] ?? $params['mdStatus'] ?? ''],
            ];
        }

        // 3D dogrulama sonucunu kontrol et
        $mdStatus = $params['mdstatus'] ?? $params['mdStatus'] ?? '';

        // mdstatus 1,2,3,4 basarili kabul edilir
        if (! in_array($mdStatus, ['1', '2', '3', '4'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_3d_auth_failed'),
                'data' => [
                    'mdstatus' => $mdStatus,
                    'mderrormessage' => $params['mderrormessage'] ?? $params['mdErrorMsg'] ?? '',
                ],
            ];
        }

        // 3D auth verilerini al
        $cavv = $params['cavv'] ?? '';
        $eci = $params['eci'] ?? '';
        $xid = $params['xid'] ?? '';
        $md = $params['md'] ?? '';

        $terminalId = $this->getConfig('terminal_id');
        $merchantId = $this->getConfig('merchant_id');
        $provisionUser = $this->getConfig('provision_user', 'PROVAUT');
        $currencyCode = $this->getConfig('currency_code', '949');

        $orderId = $params['oid'] ?? $params['orderid'] ?? $params['order_id'] ?? '';
        // TUTAR: banka POST'undan DEĞİL, kendi kaydımızdan (expected_amount) oku;
        // imza kapsamındaki txnamount da olsa provizyona kendi otoritemizle git.
        if (isset($params['expected_amount'])) {
            $amount = (int) round(((float) $params['expected_amount']) * 100);
        } else {
            $amount = $params['txnamount'] ?? $params['amount'] ?? '0';
        }
        $customerEmail = $params['customeremailaddress'] ?? $params['customer_email'] ?? '';
        $customerIp = $params['customeripaddress'] ?? $params['client_ip'] ?? request()->ip();

        $securityData = $this->generateSecurityData();
        $hashData = $this->generateHashData($orderId, $terminalId, '', (string) $amount, $securityData);

        $xml = $this->buildGvpXml([
            'mode' => $this->isTestMode() ? 'TEST' : 'PROD',
            'terminal_id' => $terminalId,
            'merchant_id' => $merchantId,
            'provision_user' => $provisionUser,
            'hash_data' => $hashData,
            'card_number' => '',
            'expire_date' => '',
            'cvv' => '',
            'order_id' => $orderId,
            'amount' => (string) $amount,
            'currency_code' => $currencyCode,
            'customer_email' => $customerEmail,
            'customer_ip' => $customerIp,
            'transaction_type' => 'sales',
            'cardholder_present_code' => '13',
            'secure3d' => [
                'authenticationcode' => $cavv,
                'securitylevel' => $eci,
                'txnid' => $xid,
                'md' => $md,
            ],
        ]);

        $response = $this->callBankApi($this->apiUrl, $xml);
        $parsed = $this->parseGvpResponse($response);

        if ($parsed && ($parsed['ReasonCode'] ?? '') === '00') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.garantibank_payment_success'),
                'transaction_id' => $parsed['RetrefNum'] ?? $orderId,
                'auth_code' => $parsed['AuthCode'] ?? '',
                'data' => array_merge($parsed, [
                    'mdstatus' => $mdStatus,
                    'cavv' => $cavv,
                    'eci' => $eci,
                    'xid' => $xid,
                ]),
            ];
        }

        return [
            'status' => 'error',
            'message' => $parsed['ErrorMsg'] ?? $parsed['ReasonCode'] ?? __('admin/modules.garantibank_payment_failed'),
            'data' => $parsed,
        ];
    }

    /**
     * Odeme callback'ini isler
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        // Garanti 3D dönüşü DAİMA imzalı POST'tur (hash/hashparams). Callback'te
        // hash zorunlu; imza yoksa/geçersizse fail-closed reddet (WiseCP callback:218-224).
        if (! isset($params['hash']) || ! isset($params['hashparams'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_invalid_callback'),
                'data' => [],
            ];
        }

        if (! $this->verifyCallbackSignature($params)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_signature_invalid'),
                'data' => [],
            ];
        }

        // İmza geçerli → 3D provizyonunu tamamla (complete3DPayment içinde imza yeniden
        // doğrulanır; idempotent ve fail-closed).
        return $this->complete3DPayment($params);
    }

    /**
     * Banka 3D dönüş imzasını fail-CLOSED doğrular.
     * WiseCP check_hash (Garanti.php:290-310): hashparams ':' ile parçalanır, her
     * alanın değeri + store_key birleştirilir, base64(pack('H*', sha1(...))) hesaplanır
     * ve secureCompare (hash_equals) ile banka hash'iyle karşılaştırılır.
     */
    private function verifyCallbackSignature(array $params): bool
    {
        $storeKey = (string) $this->getConfig('store_key');
        $hashParams = $params['hashparams'] ?? $params['hashParams'] ?? '';
        $bankHash = $params['hash'] ?? '';

        if ($storeKey === '' || $hashParams === '' || $bankHash === '') {
            return false; // fail-closed: eksik imza asla geçerli sayılmaz
        }

        // Callback anahtarları büyük/küçük harf karışık gelebilir → küçük-harf indeksle.
        $lower = [];
        foreach ($params as $k => $v) {
            $lower[strtolower((string) $k)] = $v;
        }

        $digestData = '';
        foreach (explode(':', $hashParams) as $field) {
            $value = $lower[strtolower($field)] ?? '';
            $digestData .= is_scalar($value) ? (string) $value : '';
        }
        $digestData .= $storeKey;

        $calculated = base64_encode(pack('H*', sha1($digestData)));

        return $this->secureCompare($calculated, (string) $bankHash);
    }

    /**
     * Odeme iadesi yapar
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        $terminalId = $this->getConfig('terminal_id');
        $merchantId = $this->getConfig('merchant_id');
        $provisionUser = $this->getConfig('provision_user', 'PROVRFN');
        $currencyCode = $this->getConfig('currency_code', '949');

        $orderId = $params['order_id'];
        $amount = (int) round($params['amount'] * 100);
        $customerIp = $params['client_ip'] ?? $params['user_ip'] ?? request()->ip();

        $securityData = $this->generateSecurityData();
        $hashData = $this->generateHashData($orderId, $terminalId, '', (string) $amount, $securityData);

        $xml = $this->buildGvpXml([
            'mode' => $this->isTestMode() ? 'TEST' : 'PROD',
            'terminal_id' => $terminalId,
            'merchant_id' => $merchantId,
            'provision_user' => $provisionUser,
            'hash_data' => $hashData,
            'card_number' => '',
            'expire_date' => '',
            'cvv' => '',
            'order_id' => $orderId,
            'amount' => (string) $amount,
            'currency_code' => $currencyCode,
            'customer_email' => $params['customer_email'] ?? '',
            'customer_ip' => $customerIp,
            'transaction_type' => 'refund',
            'cardholder_present_code' => '0',
        ]);

        $response = $this->callBankApi($this->apiUrl, $xml);
        $parsed = $this->parseGvpResponse($response);

        if ($parsed && ($parsed['ReasonCode'] ?? '') === '00') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.garantibank_refund_success'),
                'refund_id' => $parsed['RetrefNum'] ?? '',
                'data' => $parsed,
            ];
        }

        return [
            'status' => 'error',
            'message' => $parsed['ErrorMsg'] ?? $parsed['ReasonCode'] ?? __('admin/modules.garantibank_refund_failed'),
            'data' => $parsed,
        ];
    }

    /**
     * Odeme linki olusturur - Garanti BBVA desteklemiyor
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.garantibank_payment_link_not_supported'),
        ];
    }

    /**
     * Fraud kontrolu yapar
     *
     * @return array
     */
    public function performPreFraudCheck(array $params)
    {
        // Gerçek risk-skorlama merkezî FraudModuleManager katmanının işi; burada yalnız
        // gateway'e gitmeden ANLAMSIZ payload'ı ele (malformed PAN) — ad-hoc tutar/IP
        // eşiği YOK (yanlış katman). Kart formatı bozuksa baştan reddet.
        if (isset($params['card_number'])) {
            $cardNumber = str_replace([' ', '-'], '', $params['card_number']);
            if (! $this->isValidLuhn($cardNumber)) {
                return [
                    'status' => 'error',
                    'risk_score' => 'critical',
                    'message' => __('admin/modules.garantibank_invalid_card_number'),
                    'reasons' => ['Invalid card number (Luhn check failed)'],
                ];
            }
        }

        return [
            'status' => 'success',
            'risk_score' => 'low',
            'message' => 'Fraud check passed',
            'reasons' => [],
        ];
    }

    /**
     * Modulun destekledigi ozellikleri dondurur
     */
    public function getFeatures(): array
    {
        return [
            'refund' => true,
            '3d_secure' => true,
            'save_card' => false,
            'recurring' => false,
            'fraud_check' => true,
            'installment' => true,
        ];
    }

    /**
     * Taksit planları — AbstractPayment base config-tabanlı üretici
     * (installment_enabled + installment_plans JSON). WiseCP Garanti installment
     * desteğinin procp karşılığı; komisyon/aralık base helper'da hesaplanır.
     */
    public function getInstallmentPlans(float $amount, ?string $currencyCode = null): array
    {
        return parent::getInstallmentPlans($amount, $currencyCode);
    }

    /**
     * Desteklenen ozellikleri dondurur
     */
    public function getSupportedFeatures(): array
    {
        return $this->getFeatures();
    }

    /**
     * Admin panel konfigürasyon alanlarini dondurur
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'security_level' => [
                'type' => 'select',
                'label' => __('admin/modules.garantibank_security_level'),
                'description' => __('admin/modules.garantibank_security_level_desc'),
                'required' => true,
                'value' => '3D_PAY',
                'options' => [
                    '3D_FULL' => '3D_FULL',
                    '3D_PAY' => '3D_PAY',
                    '3D_HALF' => '3D_HALF',
                ],
            ],
            'merchant_id' => [
                'type' => 'text',
                'label' => __('admin/modules.garantibank_merchant_id'),
                'description' => __('admin/modules.garantibank_merchant_id_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '7000679',
            ],
            'terminal_id' => [
                'type' => 'text',
                'label' => __('admin/modules.garantibank_terminal_id'),
                'description' => __('admin/modules.garantibank_terminal_id_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '30691298',
            ],
            'provision_user' => [
                'type' => 'text',
                'label' => __('admin/modules.garantibank_provision_user'),
                'description' => __('admin/modules.garantibank_provision_user_desc'),
                'required' => true,
                'value' => 'PROVAUT',
                'placeholder' => 'PROVAUT',
            ],
            'provision_password' => [
                'type' => 'password',
                'label' => __('admin/modules.garantibank_provision_password'),
                'description' => __('admin/modules.garantibank_provision_password_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '••••••••',
            ],
            'store_key' => [
                'type' => 'password',
                'label' => __('admin/modules.garantibank_store_key'),
                'description' => __('admin/modules.garantibank_store_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '••••••••',
            ],
            'currency_code' => [
                'type' => 'select',
                'label' => __('admin/modules.garantibank_currency_code'),
                'description' => __('admin/modules.garantibank_currency_code_desc'),
                'required' => true,
                'value' => '949',
                'options' => [
                    '949' => 'TRY - Turkish Lira',
                    '840' => 'USD - US Dollar',
                    '978' => 'EUR - Euro',
                ],
            ],
        ], $this->installmentConfigFields());
    }

    /**
     * Test baglantisi
     */
    public function testConnection(array $params = [])
    {
        $terminalId = $this->getConfig('terminal_id');
        $merchantId = $this->getConfig('merchant_id');
        $provisionUser = $this->getConfig('provision_user', 'PROVAUT');

        $testOrderId = 'TEST' . time();
        $securityData = $this->generateSecurityData();
        $hashData = $this->generateHashData($testOrderId, $terminalId, '', '100', $securityData);

        $xml = $this->buildGvpXml([
            'mode' => 'TEST',
            'terminal_id' => $terminalId,
            'merchant_id' => $merchantId,
            'provision_user' => $provisionUser,
            'hash_data' => $hashData,
            'card_number' => '',
            'expire_date' => '',
            'cvv' => '',
            'order_id' => $testOrderId,
            'amount' => '100',
            'currency_code' => $this->getConfig('currency_code', '949'),
            'customer_email' => 'test@test.com',
            'customer_ip' => '127.0.0.1',
            'transaction_type' => 'orderinq',
            'cardholder_present_code' => '0',
        ]);

        $response = $this->callBankApi($this->apiUrl, $xml);
        $parsed = $this->parseGvpResponse($response);

        // "XML parse oldu" YETMEZ (yanıltıcı-pozitif). ReasonCode/SysErrMsg ile
        // gerçek başarı vs. kimlik/hash-hatası ayır. orderinq sorgusunda '00' başarı;
        // olmayan test order için bank yine kimlik-doğru ise anlamlı ReasonCode döner —
        // hash/terminal hatası ise SysErrMsg dolu gelir → başarısız say.
        if ($parsed !== null) {
            $reasonCode = $parsed['ReasonCode'] ?? '';
            $sysErr = $parsed['SysErrMsg'] ?? '';

            // Kimlik/hash hatası: 3xx SysErr (ör. 3001 hash, 3002 terminal) → bağlantı OK ama config yanlış.
            if ($sysErr !== '') {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.garantibank_connection_auth_failed', ['error' => $sysErr]),
                    'data' => ['ReasonCode' => $reasonCode, 'SysErrMsg' => $sysErr],
                ];
            }

            // Kimlik doğru: banka anlamlı bir ReasonCode döndürebildi (00 veya order-not-found gibi).
            if ($reasonCode !== '') {
                return [
                    'status' => 'success',
                    'message' => __('admin/modules.garantibank_connection_success'),
                    'data' => ['ReasonCode' => $reasonCode],
                ];
            }
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.garantibank_connection_failed'),
            'data' => [],
        ];
    }

    /**
     * Konfigurasyon dogrulama
     */
    public function validateConfig()
    {
        $required = ['merchant_id', 'terminal_id', 'provision_user', 'provision_password', 'store_key'];
        $missing = [];

        foreach ($required as $field) {
            if (empty($this->config[$field])) {
                $missing[] = $field;
            }
        }

        if (! empty($missing)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_missing_config', ['fields' => implode(', ', $missing)]),
            ];
        }

        // Terminal ID 8 haneli olmali
        $terminalId = $this->getConfig('terminal_id');
        if (strlen($terminalId) > 9 || ! ctype_digit($terminalId)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantibank_invalid_terminal_id'),
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.garantibank_config_valid'),
        ];
    }

    // =========================================================================
    // Private Helper Methods
    // =========================================================================

    /**
     * SecurityData: strtoupper(SHA1(provision_password + 0-padded(terminal_id,9)))
     * Klasik GVPS + WiseCP Garanti.php:130. (SHA512 DEĞİL — banka reddeder.)
     */
    private function generateSecurityData(): string
    {
        $provisionPassword = $this->getConfig('provision_password');
        $terminalId = str_pad($this->getConfig('terminal_id'), 9, '0', STR_PAD_LEFT);

        return strtoupper(sha1($provisionPassword . $terminalId));
    }

    /**
     * Provizyon HashData: strtoupper(SHA1(orderId + terminalId + cardNumber + amount + securityData))
     * Klasik GVPS provizyon şeması (WiseCP capture SecurityData/HashData ile aynı SHA1 ailesi).
     */
    private function generateHashData(
        string $orderId,
        string $terminalId,
        string $cardNumber,
        string $amount,
        string $securityData
    ): string {
        $hashStr = $orderId . $terminalId . $cardNumber . $amount . $securityData;

        return strtoupper(sha1($hashStr));
    }

    /**
     * 3D HashData: strtoupper(SHA1(tid + orderid + amount + successurl + errorurl + 'sales'
     * + installment + storekey + SecurityData)). WiseCP Garanti.php:131 birebir.
     */
    private function build3DHash(
        string $terminalId,
        string $orderId,
        string $amount,
        string $successUrl,
        string $errorUrl,
        string $installment,
        string $storeKey,
        string $securityData
    ): string {
        $hashStr = $terminalId . $orderId . $amount . $successUrl . $errorUrl
            . 'sales' . $installment . $storeKey . $securityData;

        return strtoupper(sha1($hashStr));
    }

    /**
     * GVP XML istegi olusturur
     */
    private function buildGvpXml(array $data): string
    {
        $mode = $data['mode'];
        $terminalId = $data['terminal_id'];
        $merchantId = $data['merchant_id'];
        $provisionUser = $data['provision_user'];
        $hashData = $data['hash_data'];
        $cardNumber = $data['card_number'];
        $expireDate = $data['expire_date'];
        $cvv = $data['cvv'];
        $orderId = $data['order_id'];
        $amount = $data['amount'];
        $currencyCode = $data['currency_code'];
        $customerEmail = $data['customer_email'];
        $customerIp = $data['customer_ip'];
        $txnType = $data['transaction_type'];
        $cardholderPresentCode = $data['cardholder_present_code'];
        // GVPS 2D peşin/taksit: <Card><Installment>N</Installment>. Peşin (boş) → element atlanır.
        // Taksit sayısı provizyonda banka tarafça doğrulanır (2D hash'e ayrıca girmez — GVPS spec).
        $installment = (string) ($data['installment'] ?? '');

        $secure3dBlock = '';
        if (isset($data['secure3d'])) {
            $s3d = $data['secure3d'];
            $secure3dBlock = '<Secure3D>'
                . '<AuthenticationCode>' . $this->xmlEscape($s3d['authenticationcode']) . '</AuthenticationCode>'
                . '<SecurityLevel>' . $this->xmlEscape($s3d['securitylevel']) . '</SecurityLevel>'
                . '<TxnID>' . $this->xmlEscape($s3d['txnid']) . '</TxnID>'
                . '<Md>' . $this->xmlEscape($s3d['md']) . '</Md>'
                . '</Secure3D>';
        }

        $xml = '<?xml version="1.0" encoding="UTF-8"?>'
            . '<GVPSRequest>'
            . '<Mode>' . $this->xmlEscape($mode) . '</Mode>'
            . '<Version>v0.01</Version>'
            . '<Terminal>'
            . '<ProvUserID>' . $this->xmlEscape($provisionUser) . '</ProvUserID>'
            . '<HashData>' . $this->xmlEscape($hashData) . '</HashData>'
            . '<UserID>' . $this->xmlEscape($provisionUser) . '</UserID>'
            . '<ID>' . $this->xmlEscape($terminalId) . '</ID>'
            . '<MerchantID>' . $this->xmlEscape($merchantId) . '</MerchantID>'
            . '</Terminal>'
            . '<Customer>'
            . '<IPAddress>' . $this->xmlEscape($customerIp) . '</IPAddress>'
            . '<EmailAddress>' . $this->xmlEscape($customerEmail) . '</EmailAddress>'
            . '</Customer>'
            . '<Card>'
            . '<Number>' . $this->xmlEscape($cardNumber) . '</Number>'
            . '<ExpireDate>' . $this->xmlEscape($expireDate) . '</ExpireDate>'
            . '<CVV2>' . $this->xmlEscape($cvv) . '</CVV2>'
            . ($installment !== '' ? '<Installment>' . $this->xmlEscape($installment) . '</Installment>' : '')
            . '</Card>'
            . '<Order>'
            . '<OrderID>' . $this->xmlEscape($orderId) . '</OrderID>'
            . '<GroupID></GroupID>'
            . '</Order>'
            . '<Transaction>'
            . '<Type>' . $this->xmlEscape($txnType) . '</Type>'
            . '<Amount>' . $this->xmlEscape($amount) . '</Amount>'
            . '<CurrencyCode>' . $this->xmlEscape($currencyCode) . '</CurrencyCode>'
            . '<CardholderPresentCode>' . $this->xmlEscape($cardholderPresentCode) . '</CardholderPresentCode>'
            . $secure3dBlock
            . '</Transaction>'
            . '</GVPSRequest>';

        return $xml;
    }

    /**
     * GVP XML yanitini parse eder
     */
    private function parseGvpResponse(string $rawResponse): ?array
    {
        if (empty($rawResponse)) {
            return null;
        }

        libxml_use_internal_errors(true);
        $xml = simplexml_load_string($rawResponse);
        libxml_clear_errors();

        if ($xml === false) {
            return null;
        }

        $result = [];

        // Transaction bolumleri
        if (isset($xml->Transaction)) {
            $txn = $xml->Transaction;
            $result['ReasonCode'] = (string) ($txn->Response->ReasonCode ?? '');
            $result['Message'] = (string) ($txn->Response->Message ?? '');
            $result['ErrorMsg'] = (string) ($txn->Response->ErrorMsg ?? '');
            $result['SysErrMsg'] = (string) ($txn->Response->SysErrMsg ?? '');
            $result['RetrefNum'] = (string) ($txn->RetrefNum ?? '');
            $result['AuthCode'] = (string) ($txn->AuthCode ?? '');
            $result['BatchNum'] = (string) ($txn->BatchNum ?? '');
            $result['SequenceNum'] = (string) ($txn->SequenceNum ?? '');
            $result['ProvDate'] = (string) ($txn->ProvDate ?? '');
            $result['CardNumberMasked'] = (string) ($txn->CardNumberMasked ?? '');
            $result['HostMsgList'] = (string) ($txn->HostMsgList ?? '');
        }

        // Order bolumleri
        if (isset($xml->Order)) {
            $result['OrderID'] = (string) ($xml->Order->OrderID ?? '');
            $result['GroupID'] = (string) ($xml->Order->GroupID ?? '');
        }

        return $result;
    }

    /**
     * Banka API'sine XML istegi gonderir. GVPS gövdeyi form alanı `data=<xml>` bekler;
     * postForm ile gönderilir → XML urlencode edilir (&/+ içeren imza/alanlar bozulmaz).
     * PRC-888 transport helper'ı kullanır (TLS AÇIK + protokol kısıtı + timeout).
     * PCI: PAN/CVV/store_key/raw_response LOGLANMAZ.
     */
    private function callBankApi(string $url, string $xmlData): string
    {
        $result = $this->postForm($url, ['data' => $xmlData], [], [
            'timeout' => 60,
            'connect_timeout' => 30,
        ]);

        if (! empty($result['error'])) {
            \Illuminate\Support\Facades\Log::error('GarantiBank API Error', [
                'url' => $url,
                'error' => $result['error'],
            ]);

            return '';
        }

        return (string) ($result['raw_response'] ?? '');
    }

    /**
     * XML icin ozel karakterleri escape eder
     */
    private function xmlEscape(string $value): string
    {
        return htmlspecialchars($value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
    }

    /**
     * Luhn algoritmasiyla kart numarasi dogrulamasi
     */
    private function isValidLuhn(string $number): bool
    {
        $number = preg_replace('/\D/', '', $number);

        if (strlen($number) < 13 || strlen($number) > 19) {
            return false;
        }

        $sum = 0;
        $alternate = false;

        for ($i = strlen($number) - 1; $i >= 0; $i--) {
            $digit = (int) $number[$i];

            if ($alternate) {
                $digit *= 2;
                if ($digit > 9) {
                    $digit -= 9;
                }
            }

            $sum += $digit;
            $alternate = ! $alternate;
        }

        return $sum % 10 === 0;
    }
}
