<?php

namespace App\Modules\Payment\GarantiSwt;

use App\Core\Payment\AbstractPayment;
use Illuminate\Support\Facades\Log;

/**
 * Garanti BBVA — Kart Saklama & Switch (MODERN API).
 *
 * Bu modül Garanti'nin **Kart Saklama & Switch** API'sini konuşur: JSON/REST,
 * **SHA256 UPPERCASE** imza, `kartsaklamabackend.garanti.com.tr` + `/swt3dengine`,
 * **token tabanlı kart saklama** ve akıllı switch (çok bankalı) yönlendirme.
 *
 * ⚠️ Bu, klasik GVPS `GarantiBank` (VPServlet + XML + SHA1 + kuruş-integer) DEĞİLDİR.
 * İki API kimlik setleri örtüşmez; ayrı modül olarak tutulur (PRC-1110).
 *
 * Referanslar:
 *  - docs/integration/garanti/01-kart-saklama-switch.md (worked-example + Kriweb satır ref)
 *  - Çalışan üretim kodu: kriweb/libraries/custompaymentgateways/garanti.php
 *
 * En kritik iki fark (sessiz üretim bug'ı riski):
 *  1. amount = ONDALIKLI (12.50) — GVPS'in `*100` kuruş-integer'ı Switch'te 100× hata.
 *  2. Hash = SHA256 (GVPS SHA1 değil); JSON ödeme hash'i kart/tutar/order İÇERMEZ,
 *     yalnız header (requestId+swtId+userId+timestamp+password).
 *
 * 🔎 CANLI-DOĞRULAMA (test creds + IP-allowlist gelince): 3D hash alan sırası,
 * checkToken vs hashedData form-alanı, timestamp string formatı, response-hash'te
 * timestamp'in dahil olup olmadığı, originalRequestId persist akışı. Kod Kriweb'in
 * çalışan varyantına göre yazıldı; response doğrulama timestamp'li VE timestamp'siz
 * iki varyantı da dener (fail-closed).
 */
class GarantiSwt extends AbstractPayment
{
    /** Ödeme/token JSON endpoint base host (path'ler ayrı). */
    protected string $host;

    /** 3D form POST hedefi. */
    protected string $threeDUrl;

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

        $this->host = $this->isTestMode()
            ? 'https://gbtaksimtunel-t.garantibbva.com.tr'
            : 'https://kartsaklamabackend.garanti.com.tr';

        $this->threeDUrl = $this->host . '/swt3dengine';
    }

    // =========================================================================
    // Ödeme akışı
    // =========================================================================

    /**
     * Ödeme girişimi başlatır (validasyon + fraud ön-eleme).
     *
     * @return array
     */
    public function attemptPayment(array $params)
    {
        $required = ['amount', 'order_id'];
        foreach ($required as $param) {
            if (! isset($params[$param]) || $params[$param] === '') {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.garantiswt_missing_param', ['param' => $param]),
                ];
            }
        }

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

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

    /**
     * Non-3D satış — açık kart VEYA token ile `/api/payment/(token/)auth`.
     * amount ONDALIKLI; başarı = acquirerResponse.returnCode == "00".
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $hasToken = ! empty($params['card_token']);

        return $hasToken
            ? $this->authWithToken($params, $params['card_token'])
            : $this->authWithOpenCard($params);
    }

    /**
     * Açık kart ile satış (`/api/payment/auth`).
     */
    protected function authWithOpenCard(array $params): array
    {
        $requestId = $this->buildRequestId('AUTH', $params['order_id'] ?? '');
        $timestamp = (string) time();
        $amountStr = $this->formatAmount($params['amount']);

        $body = [
            'header' => $this->buildHeader($requestId, $timestamp),
            'card' => [
                'number' => str_replace([' ', '-'], '', (string) ($params['card_number'] ?? '')),
                'expireMonth' => str_pad((string) ($params['exp_month'] ?? ''), 2, '0', STR_PAD_LEFT),
                'expireYear' => substr((string) ($params['exp_year'] ?? ''), -2),
                'cvv' => (string) ($params['cvv'] ?? ''),
                'holderName' => (string) ($params['card_holder'] ?? ''),
                'first6' => '',
                'last4' => '',
                'token' => '',
            ],
            'cardHolder' => $this->buildCardHolder($params),
            'installmentCount' => $this->installmentCount($params),
            'currencyNumber' => $this->getConfig('currency_code', '949'),
            'txnAmount' => (float) $amountStr,
            'orderId' => (string) $params['order_id'],
        ];

        $this->applyAcquirer($body);

        return $this->postAuth($this->host . '/api/payment/auth', $body, $requestId, $timestamp);
    }

    /**
     * Token ile satış (`/api/payment/token/auth`). Kayıtlı kart ödemesi.
     */
    protected function authWithToken(array $params, string $token): array
    {
        $requestId = $this->buildRequestId('TAUTH', $params['order_id'] ?? '');
        $timestamp = (string) time();
        $amountStr = $this->formatAmount($params['amount']);

        $body = [
            'header' => $this->buildHeader($requestId, $timestamp),
            'card' => [
                'number' => '', 'expireMonth' => '', 'expireYear' => '', 'cvv' => '',
                'holderName' => '', 'first6' => '', 'last4' => '',
                'token' => $token,
            ],
            'cardHolder' => $this->buildCardHolder($params),
            'installmentCount' => $this->installmentCount($params),
            'currencyNumber' => $this->getConfig('currency_code', '949'),
            'txnAmount' => (float) $amountStr,
            'orderId' => (string) ($params['order_id'] ?? ''),
        ];

        $this->applyAcquirer($body);

        return $this->postAuth($this->host . '/api/payment/token/auth', $body, $requestId, $timestamp);
    }

    /**
     * Kayıtlı kart ile ödeme (PaymentInterface). Token'ı `card_token` olarak alır.
     *
     * @return array
     */
    public function payWithSavedCard(array $params)
    {
        $token = $params['card_token'] ?? $params['token'] ?? '';
        if ($token === '') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_missing_token'),
            ];
        }

        return $this->authWithToken($params, $token);
    }

    /**
     * JSON auth POST + response parse (ortak). Başarı iki-katmanlı:
     * dış header "Switch OK", asıl onay acquirerResponse.returnCode.
     */
    protected function postAuth(string $url, array $body, string $requestId, string $timestamp): array
    {
        $result = $this->postJson($url, $body, [], ['timeout' => 60, 'connect_timeout' => 30]);

        if (! empty($result['error']) || ! is_array($result['response'] ?? null)) {
            Log::error('GarantiSwt auth transport error', ['url' => $url, 'error' => $result['error'] ?? 'no-json']);

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

        return $this->parseAuthResponse($result['response'], $requestId);
    }

    /**
     * Switch auth cevabını değerlendirir. Başarı = acquirerResponse.returnCode == "00".
     * transaction_id = retRefNum-authCode. requestId sonuca eklenir (caller void/refund
     * için transactions.metadata->garanti_request_id olarak saklamalı).
     */
    protected function parseAuthResponse(array $response, string $requestId): array
    {
        $acq = $response['acquirerResponse'] ?? [];
        $acqReturn = (string) ($acq['returnCode'] ?? '');

        if ($acqReturn === '00') {
            $retRef = (string) ($acq['retRefNum'] ?? $acq['retrefNum'] ?? '');
            $authCode = (string) ($acq['authCode'] ?? '');

            return [
                'status' => 'success',
                'message' => __('admin/modules.garantiswt_payment_success'),
                'transaction_id' => trim($retRef . '-' . $authCode, '-'),
                'auth_code' => $authCode,
                'request_id' => $requestId,
                'data' => [
                    'retref_num' => $retRef,
                    'acquirer_id' => (string) ($response['acquirerId'] ?? ''),
                    'token' => (string) ($response['card']['token'] ?? ''),
                    'masked_number' => (string) ($response['card']['maskedNumber'] ?? ''),
                    'request_id' => $requestId,
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->mapErrorMessage($acq, $response),
            'data' => [
                'return_code' => $acqReturn,
                'reason_code' => (string) ($acq['reasonCode'] ?? ''),
                'header_return_code' => (string) ($response['header']['returnCode'] ?? ''),
            ],
        ];
    }

    /**
     * 3D Secure ödeme başlatır — `/swt3dengine`'e auto-submit HTML form.
     * Hash §3.3 (swtId+orderId+txnType+txnAmount+installment+successUrl+failureUrl+timestamp+password).
     *
     * @return array
     */
    public function start3DPayment(array $params)
    {
        $swtId = (string) $this->getConfig('swid');
        $password = (string) $this->getConfig('switch_password');
        $orderId = (string) $params['order_id'];
        $amountStr = $this->formatAmount($params['amount']);
        $installment = (string) $this->installmentCount($params);
        $successUrl = (string) $params['success_url'];
        $failureUrl = (string) ($params['fail_url'] ?? $params['error_url'] ?? $params['success_url']);
        $timestamp = (string) time();
        $txnType = 'sales';
        $token = (string) ($params['card_token'] ?? '');

        $hash = $this->threeDHash(
            $swtId, $orderId, $txnType, $amountStr, $installment,
            $successUrl, $failureUrl, $timestamp, $password
        );

        $formData = [
            'swtId' => $swtId,
            'requestId' => $this->buildRequestId('3D', $orderId),
            'level' => $this->getConfig('security_level', '3D_PAY'),
            'orderId' => $orderId,
            'userId' => $this->getConfig('provision_user', 'PROVAUT'),
            'txnType' => $txnType,
            'txnAmount' => $amountStr,
            'txnCurrencyCode' => $this->getConfig('currency_code', '949'),
            'txnInstallmentCount' => $installment,
            'txnTimestamp' => $timestamp,
            'successUrl' => $successUrl,
            'failureUrl' => $failureUrl,
            'customerIp' => $params['client_ip'] ?? $params['user_ip'] ?? request()->ip(),
            'emailAddress' => (string) ($params['customer_email'] ?? ''),
            'lang' => 'tr',
            'txnMotoInd' => 'n',
            'acqId' => (string) $this->getConfig('acquirer_bank_id', ''),
            'hashedData' => $hash,
            'hash' => $hash, // Kriweb secureCapture form'da hem hashedData hem hash yollar
            'checkToken' => '',
        ];

        // Token varsa saklı kartla 3D; yoksa açık kart alanları.
        if ($token !== '') {
            $formData['cardToken'] = $token;
        } else {
            $formData['cardNumber'] = str_replace([' ', '-'], '', (string) ($params['card_number'] ?? ''));
            $formData['cardExpireMonth'] = str_pad((string) ($params['exp_month'] ?? ''), 2, '0', STR_PAD_LEFT);
            $formData['cardExpireYear'] = substr((string) ($params['exp_year'] ?? ''), -2);
            $formData['cardCvv'] = (string) ($params['cvv'] ?? '');
        }

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

    /**
     * 3D Secure callback tamamlar — **TEK ADIM**: `/swt3dengine` dönüşü ZATEN provizyon
     * sonucudur (klasik GVPS'teki ikinci VPServlet çağrısı YOK). Fail-closed:
     * response-hash + mdStatus + returnCode/reasonCode + swtId + expected_amount.
     *
     * @return array
     */
    public function complete3DPayment(array $params)
    {
        // 1) İmza doğrula (fail-closed) — banka POST'unu store_password ile yeniden hashle.
        if (! $this->verifyResponseHash($params)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_signature_invalid'),
                'data' => ['mdstatus' => $params['mdStatus'] ?? $params['mdstatus'] ?? ''],
            ];
        }

        $mdStatus = (string) ($params['mdStatus'] ?? $params['mdstatus'] ?? '');
        $returnCode = (string) ($params['returnCode'] ?? $params['returncode'] ?? '');
        $reasonCode = (string) ($params['reasonCode'] ?? $params['reasoncode'] ?? '');

        // 2) 3D + provizyon sonucu tek dönüşte: hepsi geçmeli.
        if ($returnCode !== '00' || $reasonCode !== '00' || $mdStatus !== '1') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_3d_auth_failed'),
                'data' => [
                    'mdstatus' => $mdStatus,
                    'return_code' => $returnCode,
                    'reason_code' => $reasonCode,
                    'message' => (string) ($params['message'] ?? ''),
                ],
            ];
        }

        // 3) swtId self-check.
        $swtId = (string) $this->getConfig('swid');
        $respSwtId = (string) ($params['swtId'] ?? $params['swtid'] ?? '');
        if ($swtId !== '' && $respSwtId !== '' && ! $this->secureCompare($swtId, $respSwtId)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_signature_invalid'),
                'data' => ['reason' => 'swtId mismatch'],
            ];
        }

        // 4) Tutar bütünlüğü: banka POST'una GÜVENME, kendi kaydından (expected_amount) doğrula.
        if (isset($params['expected_amount'])) {
            $expected = $this->formatAmount($params['expected_amount']);
            $got = $this->formatAmount($params['txnAmount'] ?? $params['txnamount'] ?? '0');
            if ($expected !== $got) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.garantiswt_amount_mismatch'),
                    'data' => ['expected' => $expected, 'got' => $got],
                ];
            }
        }

        $retRef = (string) ($params['retrefNum'] ?? $params['retrefnum'] ?? '');
        $authCode = (string) ($params['authCode'] ?? $params['authcode'] ?? '');

        return [
            'status' => 'success',
            'message' => __('admin/modules.garantiswt_payment_success'),
            'transaction_id' => trim($retRef . '-' . $authCode, '-'),
            'auth_code' => $authCode,
            'request_id' => (string) ($params['requestId'] ?? $params['requestid'] ?? ''),
            'data' => [
                'mdstatus' => $mdStatus,
                'retref_num' => $retRef,
                'request_id' => (string) ($params['requestId'] ?? $params['requestid'] ?? ''),
            ],
        ];
    }

    /**
     * 3D callback girişi (route'tan). İmza + sonuç doğrulaması complete3DPayment içinde.
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        return $this->complete3DPayment($params);
    }

    // =========================================================================
    // İade / iptal
    // =========================================================================

    /**
     * İade (`/api/payment/refund`) — günsonu SONRASI, kısmi/tam. `originalRequestId`
     * (ödeme anındaki requestId) ile bağlanır; retrefNum ile DEĞİL.
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        $originalRequestId = $params['original_request_id']
            ?? $params['request_id']
            ?? ($params['metadata']['garanti_request_id'] ?? '');

        if ($originalRequestId === '') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_missing_original_request'),
            ];
        }

        return $this->postReversal('/api/payment/refund', $params, $originalRequestId, [
            'txnAmount' => (float) $this->formatAmount($params['amount'] ?? 0),
        ], 'garantiswt_refund_success');
    }

    /**
     * İptal (`/api/payment/void`) — günsonu ÖNCESİ tam iptal, tutarsız.
     *
     * @return array
     */
    public function voidPayment(array $params)
    {
        $originalRequestId = $params['original_request_id']
            ?? $params['request_id']
            ?? ($params['metadata']['garanti_request_id'] ?? '');

        if ($originalRequestId === '') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_missing_original_request'),
            ];
        }

        return $this->postReversal('/api/payment/void', $params, $originalRequestId, [], 'garantiswt_void_success');
    }

    /**
     * void/refund ortak POST'u.
     */
    protected function postReversal(string $path, array $params, string $originalRequestId, array $extra, string $successKey): array
    {
        $requestId = $this->buildRequestId('REV', $params['order_id'] ?? '');
        $timestamp = (string) time();

        $body = array_merge([
            'header' => $this->buildHeader($requestId, $timestamp, 'PROVRFN'),
            'cardHolder' => $this->buildCardHolder($params),
            'originalRequestId' => $originalRequestId,
        ], $extra);

        $result = $this->postJson($this->host . $path, $body, [], ['timeout' => 60, 'connect_timeout' => 30]);

        if (! empty($result['error']) || ! is_array($result['response'] ?? null)) {
            Log::error('GarantiSwt reversal transport error', ['path' => $path, 'error' => $result['error'] ?? 'no-json']);

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

        $acq = $result['response']['acquirerResponse'] ?? [];
        if ((string) ($acq['returnCode'] ?? '') === '00') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.' . $successKey),
                'refund_id' => (string) ($acq['retRefNum'] ?? $acq['retrefNum'] ?? ''),
                'data' => ['request_id' => $requestId],
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->mapErrorMessage($acq, $result['response']),
            'data' => ['return_code' => (string) ($acq['returnCode'] ?? '')],
        ];
    }

    // =========================================================================
    // Kart saklama / tokenizasyon
    // =========================================================================

    /**
     * Kart saklar (`/api/token/generate`) → 32-hex token döner. Token↔client eşlemesi
     * CALLER sorumluluğudur (saved_payment_methods). PAN/CVV asla saklanmaz.
     *
     * @return array
     */
    public function saveCard(array $params)
    {
        $requestId = $this->buildRequestId('TGEN', '');
        $timestamp = (string) time();

        $body = [
            'header' => $this->buildHeader($requestId, $timestamp),
            'registrationType' => 'F', // full kart no + SKT
            'additionalData' => (string) ($params['additional_data'] ?? ''),
            'card' => [
                'number' => str_replace([' ', '-'], '', (string) ($params['card_number'] ?? '')),
                'expireMonth' => str_pad((string) ($params['exp_month'] ?? ''), 2, '0', STR_PAD_LEFT),
                'expireYear' => substr((string) ($params['exp_year'] ?? ''), -2),
                'cvv' => (string) ($params['cvv'] ?? ''),
                'holderName' => (string) ($params['card_holder'] ?? ''),
                'first6' => '', 'last4' => '', 'token' => '',
            ],
            'cardHolder' => $this->buildCardHolder($params),
        ];

        $result = $this->postJson($this->host . '/api/token/generate', $body, [], ['timeout' => 60, 'connect_timeout' => 30]);

        if (! empty($result['error']) || ! is_array($result['response'] ?? null)) {
            Log::error('GarantiSwt saveCard transport error', ['error' => $result['error'] ?? 'no-json']);

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

        $resp = $result['response'];
        $header = $resp['header'] ?? [];
        if ((string) ($header['returnCode'] ?? '') === '00' && (string) ($header['reasonCode'] ?? '') === '00') {
            $card = $resp['card'] ?? [];

            return [
                'status' => 'success',
                'message' => __('admin/modules.garantiswt_save_card_success'),
                'token' => (string) ($card['token'] ?? ''),
                'data' => [
                    'masked_number' => (string) ($card['maskedNumber'] ?? ''),
                    'bin_number' => (string) ($card['binNumber'] ?? ''),
                    'bank_name' => (string) ($card['bankName'] ?? ''),
                    'bank_id' => (string) ($card['bankId'] ?? ''),
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => (string) ($header['message'] ?? '') !== ''
                ? (string) $header['message']
                : __('admin/modules.garantiswt_save_card_failed'),
            'data' => [
                'return_code' => (string) ($header['returnCode'] ?? ''),
                'reason_code' => (string) ($header['reasonCode'] ?? ''),
            ],
        ];
    }

    /**
     * Saklı kartı siler (`/api/token/removetoken`).
     *
     * @return array
     */
    public function deleteCard(array $params)
    {
        $token = $params['card_token'] ?? $params['token'] ?? '';
        if ($token === '') {
            return ['status' => 'error', 'message' => __('admin/modules.garantiswt_missing_token')];
        }

        $requestId = $this->buildRequestId('TDEL', '');
        $timestamp = (string) time();

        $body = [
            'header' => $this->buildHeader($requestId, $timestamp),
            'card' => ['token' => (string) $token],
        ];

        $result = $this->postJson($this->host . '/api/token/removetoken', $body, [], ['timeout' => 60, 'connect_timeout' => 30]);

        if (! empty($result['error']) || ! is_array($result['response'] ?? null)) {
            Log::error('GarantiSwt deleteCard transport error', ['error' => $result['error'] ?? 'no-json']);

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

        $header = $result['response']['header'] ?? [];
        if ((string) ($header['returnCode'] ?? '') === '00') {
            return ['status' => 'success', 'message' => __('admin/modules.garantiswt_delete_card_success')];
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.garantiswt_delete_card_failed'),
            'data' => ['return_code' => (string) ($header['returnCode'] ?? '')],
        ];
    }

    // =========================================================================
    // Diğer PaymentInterface metodları
    // =========================================================================

    /**
     * Ödeme linki — Switch API'sinde yerleşik link/hosted-token akışı ayrı; MVP'de yok.
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.garantiswt_payment_link_not_supported'),
        ];
    }

    /**
     * Fraud ön-eleme — yalnız malformed PAN elenir (risk-skorlama merkezî katmanın işi).
     *
     * @return array
     */
    public function performPreFraudCheck(array $params)
    {
        if (! empty($params['card_number'])) {
            $cardNumber = str_replace([' ', '-'], '', (string) $params['card_number']);
            if (! $this->isValidLuhn($cardNumber)) {
                return [
                    'status' => 'error',
                    'risk_score' => 'critical',
                    'message' => __('admin/modules.garantiswt_invalid_card_number'),
                    'reasons' => ['Invalid card number (Luhn check failed)'],
                ];
            }
        }

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

    public function getFeatures(): array
    {
        return [
            'refund' => true,
            '3d_secure' => true,
            'save_card' => true,   // token tabanlı kart saklama — Switch çekirdek özelliği
            'recurring' => true,   // token ile tekrar eden ödeme
            'fraud_check' => true,
            'installment' => true,
        ];
    }

    public function getSupportedFeatures(): array
    {
        return $this->getFeatures();
    }

    public function getInstallmentPlans(float $amount, ?string $currencyCode = null): array
    {
        return parent::getInstallmentPlans($amount, $currencyCode);
    }

    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'swid' => [
                'type' => 'text',
                'label' => __('admin/modules.garantiswt_swid'),
                'description' => __('admin/modules.garantiswt_swid_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => 'CC82C381E078482AB328943FCCB7100C',
            ],
            'switch_password' => [
                'type' => 'password',
                'label' => __('admin/modules.garantiswt_switch_password'),
                'description' => __('admin/modules.garantiswt_switch_password_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '••••••••',
            ],
            'provision_user' => [
                'type' => 'text',
                'label' => __('admin/modules.garantiswt_provision_user'),
                'description' => __('admin/modules.garantiswt_provision_user_desc'),
                'required' => true,
                'value' => 'PROVAUT',
                'placeholder' => 'PROVAUT',
            ],
            'currency_code' => [
                'type' => 'select',
                'label' => __('admin/modules.garantiswt_currency_code'),
                'description' => __('admin/modules.garantiswt_currency_code_desc'),
                'required' => true,
                'value' => '949',
                'options' => [
                    '949' => 'TRY - Turkish Lira',
                    '840' => 'USD - US Dollar',
                    '978' => 'EUR - Euro',
                ],
            ],
            'security_level' => [
                'type' => 'select',
                'label' => __('admin/modules.garantiswt_security_level'),
                'description' => __('admin/modules.garantiswt_security_level_desc'),
                'required' => true,
                'value' => '3D_PAY',
                'options' => [
                    '3D_PAY' => '3D_PAY',
                    '3D_FULL' => '3D_FULL',
                ],
            ],
            'acquirer_bank_id' => [
                'type' => 'text',
                'label' => __('admin/modules.garantiswt_acquirer_bank_id'),
                'description' => __('admin/modules.garantiswt_acquirer_bank_id_desc'),
                'required' => false,
                'value' => '',
                'placeholder' => '62 (Garanti) — boş: akıllı switch',
            ],
        ], $this->installmentConfigFields());
    }

    /**
     * Bağlantı testi — swtId/switch_password ile bir token/inquiry benzeri hafif çağrı
     * yerine, kimlik/hash kurgusunun doğruluğunu bir açık-uçlu removetoken (dummy token)
     * ile dener; kimlik yanlışsa header.returnCode kimlik hatası döner.
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $requestId = $this->buildRequestId('TEST', '');
        $timestamp = (string) time();

        // Dummy token ile inquiry — kimlik/hash doğruysa banka anlamlı returnCode döner
        // (token yok → reasonCode hata olur ama header kimlik-doğrulaması geçer).
        $body = [
            'header' => $this->buildHeader($requestId, $timestamp),
            'originalRequestId' => 'PROCP-TEST-' . $timestamp,
        ];

        $result = $this->postJson($this->host . '/api/token/inquiry', $body, [], ['timeout' => 30, 'connect_timeout' => 15]);

        if (! empty($result['error']) || ! is_array($result['response'] ?? null)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.garantiswt_connection_failed'),
                'data' => ['error' => $result['error'] ?? 'no-json'],
            ];
        }

        $header = $result['response']['header'] ?? [];
        $returnCode = (string) ($header['returnCode'] ?? '');

        // Kimlik/hash hatası tipik olarak özel bir kod + mesajla döner; banka HERHANGİ bir
        // yapılandırılmış header döndüyse TLS+kimlik kurgusu ayakta demektir.
        if ($returnCode !== '') {
            // 78/5xx (parametre/hash tanımı) → config yanlış.
            $reason = (string) ($header['reasonCode'] ?? '');
            if ($returnCode === '78' && in_array($reason, ['500', '99'], true)) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.garantiswt_connection_auth_failed', ['error' => $reason]),
                    'data' => ['returnCode' => $returnCode, 'reasonCode' => $reason],
                ];
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.garantiswt_connection_success'),
                'data' => ['returnCode' => $returnCode],
            ];
        }

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

    public function validateConfig()
    {
        $required = ['swid', 'switch_password', 'provision_user'];
        $missing = [];
        foreach ($required as $field) {
            if (empty($this->config[$field])) {
                $missing[] = $field;
            }
        }

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

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

    // =========================================================================
    // Private helpers
    // =========================================================================

    /**
     * JSON ödeme header'ı. hashedData §3.2:
     * strtoupper(SHA256(requestId + swtId + userId + timestamp + SwitchPassword)).
     * Kart/tutar/order İÇERMEZ.
     */
    protected function buildHeader(string $requestId, string $timestamp, ?string $userIdOverride = null): array
    {
        $swtId = (string) $this->getConfig('swid');
        $userId = $userIdOverride ?? (string) $this->getConfig('provision_user', 'PROVAUT');
        $password = (string) $this->getConfig('switch_password');

        return [
            'requestId' => $requestId,
            'swtId' => $swtId,
            'userId' => $userId,
            'timestamp' => $timestamp,
            'hashedData' => $this->apiHash($requestId, $swtId, $userId, $timestamp, $password),
        ];
    }

    /**
     * cardHolder objesi (ödeme/token istekleri ortak).
     */
    protected function buildCardHolder(array $params): array
    {
        return [
            'name' => (string) ($params['first_name'] ?? $params['customer_name'] ?? ''),
            'lastName' => (string) ($params['last_name'] ?? ''),
            'email' => (string) ($params['customer_email'] ?? ''),
            'gsm' => (string) ($params['phone'] ?? ''),
            'ip' => $params['client_ip'] ?? $params['user_ip'] ?? request()->ip(),
            'nationalNumber' => (string) ($params['national_number'] ?? ''),
            'customerNumber' => (string) ($params['customer_number'] ?? ''),
        ];
    }

    /**
     * acqId (banka zorlama) — config'te set ise body'ye ekle; boş = akıllı switch.
     */
    protected function applyAcquirer(array &$body): void
    {
        $bankId = (string) $this->getConfig('acquirer_bank_id', '');
        if ($bankId !== '') {
            $body['acquirer'] = ['bankId' => $bankId];
        }
    }

    /**
     * JSON ödeme/token API hash (§3.2). Kart/tutar/order İÇERMEZ.
     */
    protected function apiHash(string $requestId, string $swtId, string $userId, string $timestamp, string $password): string
    {
        return strtoupper(hash('sha256', $requestId . $swtId . $userId . $timestamp . $password));
    }

    /**
     * 3D form hash (§3.3): swtId+orderId+txnType+txnAmount+installment+successUrl+failureUrl+timestamp+password.
     */
    protected function threeDHash(
        string $swtId,
        string $orderId,
        string $txnType,
        string $txnAmount,
        string $installment,
        string $successUrl,
        string $failureUrl,
        string $timestamp,
        string $password
    ): string {
        return strtoupper(hash('sha256',
            $swtId . $orderId . $txnType . $txnAmount . $installment
            . $successUrl . $failureUrl . $timestamp . $password
        ));
    }

    /**
     * 3D callback response-hash doğrulama (fail-closed). Kriweb varyantı timestamp'siz
     * (requestId+swtId+returnCode+reasonCode+message+password); doküman §10.3 JSON cevabı
     * timestamp'i de sayar. İKİSİNİ de dener (banka üretim varyantı belirsiz — CANLI-DOĞRULA).
     */
    protected function verifyResponseHash(array $params): bool
    {
        $bankHash = (string) ($params['hashedData'] ?? $params['hasheddata'] ?? $params['hash'] ?? '');
        $password = (string) $this->getConfig('switch_password');

        if ($bankHash === '' || $password === '') {
            return false; // fail-closed: imza yoksa asla geçerli sayma
        }

        $requestId = (string) ($params['requestId'] ?? $params['requestid'] ?? '');
        $swtId = (string) ($params['swtId'] ?? $params['swtid'] ?? '');
        $returnCode = (string) ($params['returnCode'] ?? $params['returncode'] ?? '');
        $reasonCode = (string) ($params['reasonCode'] ?? $params['reasoncode'] ?? '');
        $message = (string) ($params['message'] ?? '');
        $timestamp = (string) ($params['timestamp'] ?? $params['txnTimestamp'] ?? $params['txntimestamp'] ?? '');

        // Varyant A — Kriweb (timestamp yok)
        $calcA = strtoupper(hash('sha256', $requestId . $swtId . $returnCode . $reasonCode . $message . $password));
        // Varyant B — doküman §10.3 (timestamp dahil)
        $calcB = strtoupper(hash('sha256', $requestId . $swtId . $returnCode . $reasonCode . $message . $timestamp . $password));

        return $this->secureCompare($calcA, $bankHash) || $this->secureCompare($calcB, $bankHash);
    }

    /**
     * Tutarı Switch formatına çevirir: ONDALIKLI (12.50) — kuruş-integer DEĞİL.
     * Hash ve request'te AYNI string kullanılmalı (3D form) → tek kaynak.
     */
    protected function formatAmount($amount): string
    {
        return number_format((float) $amount, 2, '.', '');
    }

    /**
     * Taksit sayısı: peşin = 0; taksit = N. Platform 'installment_count'/'installment' fallback.
     */
    protected function installmentCount(array $params): int
    {
        $count = (int) ($params['installment'] ?? $params['installment_count'] ?? 0);

        return $count < 2 ? 0 : $count;
    }

    /**
     * Benzersiz requestId üretir. void/refund için PERSIST edilmeli
     * (transactions.metadata->garanti_request_id).
     */
    protected function buildRequestId(string $prefix, string $orderId): string
    {
        $suffix = $orderId !== '' ? $orderId : substr(bin2hex(random_bytes(4)), 0, 8);

        return 'PROCP' . $prefix . $suffix . date('YmdHis');
    }

    /**
     * acquirerResponse hata mesajını okunabilir hâle getirir.
     */
    protected function mapErrorMessage(array $acq, array $response): string
    {
        $err = (string) ($acq['errorMessage'] ?? '');
        if ($err !== '') {
            return $err;
        }

        $headerMsg = (string) ($response['header']['message'] ?? '');
        if ($headerMsg !== '') {
            return $headerMsg;
        }

        return __('admin/modules.garantiswt_payment_failed');
    }

    /**
     * Luhn kart doğrulaması.
     */
    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;
    }
}
