<?php

namespace App\Modules\Payment\SagePay;

use App\Core\Payment\AbstractPayment;

class SagePay extends AbstractPayment
{
    protected $apiUrl;

    protected $legacyUrl;

    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->apiUrl = $this->isTestMode()
            ? 'https://sandbox.opayo.eu.elavon.com/api/v1'
            : 'https://live.opayo.eu.elavon.com/api/v1';
        $this->legacyUrl = $this->isTestMode()
            ? 'https://test.sagepay.com/gateway/service'
            : 'https://live.sagepay.com/gateway/service';
    }

    /**
     * Validate card parameters, perform fraud check and return ready status
     *
     * @return array
     */
    public function attemptPayment(array $params)
    {
        $requiredParams = ['amount', 'currency', 'order_id'];

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

        // Validate card params if provided directly
        if (isset($params['card_number'])) {
            $cardParams = ['card_number', 'card_holder', 'expiry_month', 'expiry_year', 'cvv'];
            foreach ($cardParams as $cardParam) {
                if (empty($params[$cardParam])) {
                    return [
                        'status' => 'error',
                        'message' => __('admin/modules.sagepay_missing_card_param', ['param' => $cardParam]),
                    ];
                }
            }
        }

        // Perform pre-fraud check
        $fraudResult = $this->performPreFraudCheck($params);
        if ($fraudResult['status'] !== 'success') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_fraud_check_failed'),
                'data' => $fraudResult,
            ];
        }

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

    /**
     * Process payment via Opayo REST API
     * POST /transactions with transactionType=Payment
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        // Create merchant session key
        $sessionKeyResult = $this->createMerchantSessionKey();

        if ($sessionKeyResult['status'] !== 'success') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_session_key_failed'),
                'data' => $sessionKeyResult,
            ];
        }

        $merchantSessionKey = $sessionKeyResult['data']['merchantSessionKey'];

        // Build payment method - use saved card identifier or create new one
        $paymentMethod = [];
        if (! empty($params['card_identifier'])) {
            // Use existing card identifier (saved card or pre-tokenized)
            $paymentMethod = [
                'card' => [
                    'merchantSessionKey' => $merchantSessionKey,
                    'cardIdentifier' => $params['card_identifier'],
                ],
            ];
        } elseif (! empty($params['card_number'])) {
            // Create card identifier from raw card data
            $cardIdentifierResult = $this->createCardIdentifier($merchantSessionKey, $params);

            if ($cardIdentifierResult['status'] !== 'success') {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.sagepay_card_identifier_failed'),
                    'data' => $cardIdentifierResult,
                ];
            }

            $paymentMethod = [
                'card' => [
                    'merchantSessionKey' => $merchantSessionKey,
                    'cardIdentifier' => $cardIdentifierResult['data']['cardIdentifier'],
                    'reusable' => ! empty($params['save_card']),
                ],
            ];
        } else {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_no_payment_method'),
            ];
        }

        $currency = $params['currency'] ?? $this->getConfig('currency', 'GBP');
        $vendorTxCode = $params['order_id'] . '-' . time();

        $transactionData = [
            'transactionType' => 'Payment',
            'paymentMethod' => $paymentMethod,
            'vendorTxCode' => $vendorTxCode,
            'amount' => $this->formatAmount($params['amount']),
            'currency' => $currency,
            'description' => $params['description'] ?? __('admin/modules.sagepay_default_description', ['order_id' => $params['order_id']]),
            'customerFirstName' => $params['first_name'] ?? '',
            'customerLastName' => $params['last_name'] ?? '',
            'billingAddress' => [
                'address1' => $params['address1'] ?? '',
                'city' => $params['city'] ?? '',
                'postalCode' => $params['postal_code'] ?? '',
                'country' => $params['country'] ?? 'GB',
            ],
            'entryMethod' => 'Ecommerce',
        ];

        // Add optional billing address fields
        if (! empty($params['address2'])) {
            $transactionData['billingAddress']['address2'] = $params['address2'];
        }
        if (! empty($params['state'])) {
            $transactionData['billingAddress']['state'] = $params['state'];
        }

        // Apply 3D Secure settings
        $securityType = $this->getConfig('security_type', '3d');
        if ($securityType === '3d' || $securityType === 'auto') {
            $transactionData['apply3DSecure'] = 'UseMSPSetting';
        } else {
            $transactionData['apply3DSecure'] = 'Disable';
        }

        $response = $this->callApi($this->apiUrl . '/transactions', 'POST', $transactionData);

        // Opayo Pi returns 201 for a resolved transaction (Ok/declined) and 202
        // when a 3DS challenge is required (status=3DAuth + acsUrl/cReq redirect data).
        if (in_array($response['http_code'], [201, 202], true) && isset($response['response']['status'])) {
            $status = $response['response']['status'];

            // Payment successful
            if ($status === 'Ok') {
                return [
                    'status' => 'success',
                    'message' => __('admin/modules.sagepay_payment_success'),
                    'transaction_id' => $response['response']['transactionId'] ?? '',
                    'auth_code' => $response['response']['bankAuthorisationCode'] ?? '',
                    'data' => [
                        'transaction_id' => $response['response']['transactionId'] ?? '',
                        'vendor_tx_code' => $vendorTxCode,
                        'bank_authorisation_code' => $response['response']['bankAuthorisationCode'] ?? '',
                        'status' => $status,
                        'amount' => $params['amount'],
                        'currency' => $currency,
                    ],
                ];
            }

            // 3D Secure authentication required
            if ($status === '3DAuth') {
                return [
                    'status' => '3d_redirect',
                    'message' => __('admin/modules.sagepay_3d_required'),
                    'data' => [
                        'transaction_id' => $response['response']['transactionId'] ?? '',
                        'vendor_tx_code' => $vendorTxCode,
                        'acs_url' => $response['response']['acsUrl'] ?? '',
                        'pa_req' => $response['response']['paReq'] ?? '',
                        'md' => $response['response']['md'] ?? '',
                        'c_req' => $response['response']['cReq'] ?? '',
                        'status' => $status,
                    ],
                ];
            }

            // Payment not authorised
            return [
                'status' => 'failed',
                'message' => __('admin/modules.sagepay_payment_declined'),
                'data' => [
                    'status' => $status,
                    'status_detail' => $response['response']['statusDetail'] ?? '',
                    'transaction_id' => $response['response']['transactionId'] ?? '',
                    'vendor_tx_code' => $vendorTxCode,
                ],
            ];
        }

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

    /**
     * Start 3D Secure payment - if 3DAuth status returned, provide redirect data
     *
     * @return array
     */
    public function start3DPayment(array $params)
    {
        $result = $this->makePayment($params);

        // If 3D redirect needed, format the redirect response
        if ($result['status'] === '3d_redirect') {
            return [
                'status' => 'redirect',
                'message' => __('admin/modules.sagepay_3d_redirect'),
                'redirect_url' => $result['data']['acs_url'],
                'redirect_method' => 'POST',
                'redirect_params' => [
                    'PaReq' => $result['data']['pa_req'],
                    'MD' => $result['data']['md'],
                    'TermUrl' => $params['return_url'] ?? '',
                    'CReq' => $result['data']['c_req'] ?? '',
                ],
                'data' => [
                    'transaction_id' => $result['data']['transaction_id'],
                    'vendor_tx_code' => $result['data']['vendor_tx_code'],
                ],
            ];
        }

        return $result;
    }

    /**
     * Complete 3D Secure payment - POST paRes to /transactions/{transactionId}/3d-secure
     *
     * @return array
     */
    public function complete3DPayment(array $params)
    {
        if (empty($params['transaction_id'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_missing_transaction_id'),
            ];
        }

        $threeDSecureData = [];

        // Support both 3DS v1 (paRes) and 3DS v2 (cRes)
        if (! empty($params['pa_res'])) {
            $threeDSecureData['paRes'] = $params['pa_res'];
        } elseif (! empty($params['PaRes'])) {
            $threeDSecureData['paRes'] = $params['PaRes'];
        }

        if (! empty($params['c_res'])) {
            $threeDSecureData['cRes'] = $params['c_res'];
        } elseif (! empty($params['CRes'])) {
            $threeDSecureData['cRes'] = $params['CRes'];
        }

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

        $endpoint = $this->apiUrl . '/transactions/' . $params['transaction_id'] . '/3d-secure';
        $response = $this->callApi($endpoint, 'POST', $threeDSecureData);

        if ($response['http_code'] === 201 && isset($response['response']['status'])) {
            $status = $response['response']['status'];

            if ($status === 'Ok') {
                return [
                    'status' => 'success',
                    'message' => __('admin/modules.sagepay_3d_payment_success'),
                    'transaction_id' => $response['response']['transactionId'] ?? $params['transaction_id'],
                    'auth_code' => $response['response']['bankAuthorisationCode'] ?? '',
                    'data' => [
                        'transaction_id' => $response['response']['transactionId'] ?? $params['transaction_id'],
                        'bank_authorisation_code' => $response['response']['bankAuthorisationCode'] ?? '',
                        'threed_secure_status' => $response['response']['3DSecureStatus'] ?? '',
                        'status' => $status,
                    ],
                ];
            }

            return [
                'status' => 'failed',
                'message' => __('admin/modules.sagepay_3d_payment_failed'),
                'data' => [
                    'status' => $status,
                    'status_detail' => $response['response']['statusDetail'] ?? '',
                    'transaction_id' => $params['transaction_id'],
                ],
            ];
        }

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

    /**
     * Browser callback — FAIL-CLOSED (PRC-888).
     *
     * Bu bir Opayo Pi (REST) modülüdür. Pi akışında 3DS dönüşü tarayıcı-callback'i
     * ile DEĞİL, complete3DPayment (server-to-server POST /transactions/{id}/3d-secure,
     * TLS + Basic-Auth otoritesi) ile tamamlanır. Pi'de imzalı VPSSignature/Crypt zarfı
     * YOKTUR; dolayısıyla tarayıcı POST'u otorite değildir ve doğrulanamaz.
     * Legacy Form/Server modeli (VPSForm Crypt AES-decrypt — WHMCS callback/protxvspform.php:16-26 /
     * VPSServer VPSSignature MD5 recompute) bu modülde implemente edilmemiştir.
     * Bu yüzden imzasız `status=Ok`'u ASLA "success" saymayız (fail-open açığı); tarayıcı
     * callback'i doğrulanamaz olarak reddedilir. Otorite complete3DPayment / makePayment yanıtıdır.
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.sagepay_callback_not_authoritative'),
            'data' => [
                'reason' => 'browser_callback_unverifiable',
                'use_instead' => 'complete3DPayment',
            ],
        ];
    }

    /**
     * Refund payment via Opayo REST API
     * POST /transactions with transactionType=Refund
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        if (empty($params['transaction_id'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_refund_missing_transaction'),
            ];
        }

        if (empty($params['amount'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_refund_missing_amount'),
            ];
        }

        $vendorTxCode = 'refund-' . $params['transaction_id'] . '-' . time();
        $currency = $params['currency'] ?? $this->getConfig('currency', 'GBP');

        $refundData = [
            'transactionType' => 'Refund',
            'referenceTransactionId' => $params['transaction_id'],
            'vendorTxCode' => $vendorTxCode,
            'amount' => $this->formatAmount($params['amount']),
            'currency' => $currency,
            'description' => $params['description'] ?? __('admin/modules.sagepay_refund_description', ['transaction_id' => $params['transaction_id']]),
        ];

        $response = $this->callApi($this->apiUrl . '/transactions', 'POST', $refundData);

        if ($response['http_code'] === 201 && isset($response['response']['status'])) {
            if ($response['response']['status'] === 'Ok') {
                return [
                    'status' => 'success',
                    'message' => __('admin/modules.sagepay_refund_success'),
                    'refund_id' => $response['response']['transactionId'] ?? '',
                    'data' => [
                        'refund_transaction_id' => $response['response']['transactionId'] ?? '',
                        'vendor_tx_code' => $vendorTxCode,
                        'amount' => $params['amount'],
                        'currency' => $currency,
                    ],
                ];
            }

            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_refund_declined'),
                'data' => [
                    'status' => $response['response']['status'],
                    'status_detail' => $response['response']['statusDetail'] ?? '',
                ],
            ];
        }

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

    /**
     * Payment link generation is not directly supported by Sage Pay
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.sagepay_payment_link_not_supported'),
        ];
    }

    /**
     * Save card using reusable card identifier
     *
     * @return array
     */
    public function saveCard(array $params)
    {
        // Create merchant session key
        $sessionKeyResult = $this->createMerchantSessionKey();

        if ($sessionKeyResult['status'] !== 'success') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_session_key_failed'),
                'data' => $sessionKeyResult,
            ];
        }

        $merchantSessionKey = $sessionKeyResult['data']['merchantSessionKey'];

        // Create a reusable card identifier
        $cardIdentifierResult = $this->createCardIdentifier($merchantSessionKey, array_merge($params, [
            'save_card' => true,
        ]));

        if ($cardIdentifierResult['status'] !== 'success') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_card_save_failed'),
                'data' => $cardIdentifierResult,
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.sagepay_card_saved'),
            'data' => [
                'card_identifier' => $cardIdentifierResult['data']['cardIdentifier'],
                'card_type' => $cardIdentifierResult['data']['cardType'] ?? '',
                'last_four' => substr(preg_replace('/\s+/', '', $params['card_number'] ?? ''), -4),
                'expiry_month' => $params['expiry_month'] ?? '',
                'expiry_year' => $params['expiry_year'] ?? '',
            ],
        ];
    }

    /**
     * Pay with a previously saved card identifier
     *
     * @return array
     */
    public function payWithSavedCard(array $params)
    {
        if (empty($params['card_identifier'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_missing_card_identifier'),
            ];
        }

        // Use stored card identifier in payment
        return $this->makePayment(array_merge($params, [
            'card_identifier' => $params['card_identifier'],
        ]));
    }

    /**
     * Delete a saved card identifier
     *
     * @return array
     */
    public function deleteCard(array $params)
    {
        if (empty($params['card_identifier'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.sagepay_missing_card_identifier'),
            ];
        }

        $endpoint = $this->apiUrl . '/card-identifiers/' . $params['card_identifier'];
        $response = $this->callApi($endpoint, 'DELETE');

        if ($response['http_code'] === 204 || $response['http_code'] === 200) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.sagepay_card_deleted'),
            ];
        }

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

    /**
     * Test API connection by creating a merchant session key
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $configValidation = $this->validateConfig();

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

        // Test by creating a merchant session key
        $sessionKeyResult = $this->createMerchantSessionKey();

        if ($sessionKeyResult['status'] === 'success') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.sagepay_connection_success'),
                'data' => [
                    'vendor_name' => $this->getConfig('vendor_name'),
                    'test_mode' => $this->isTestMode(),
                    'api_url' => $this->apiUrl,
                ],
            ];
        }

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

    /**
     * Validate required configuration fields
     *
     * @return array
     */
    public function validateConfig()
    {
        $required = ['vendor_name', 'integration_key', 'integration_password'];
        $missing = [];

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

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

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

    /**
     * Return supported features
     */
    public function getFeatures(): array
    {
        return [
            'refund' => true,
            '3d_secure' => true,
            'save_card' => true,
            'recurring' => true,
            'fraud_check' => true,
        ];
    }

    /**
     * Return supported features (alias)
     */
    public function getSupportedFeatures(): array
    {
        return $this->getFeatures();
    }

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'vendor_name' => [
                'type' => 'text',
                'label' => __('admin/modules.sagepay_vendor_name'),
                'description' => __('admin/modules.sagepay_vendor_name_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.sagepay_vendor_name_placeholder'),
            ],
            'integration_key' => [
                'type' => 'text',
                'label' => __('admin/modules.sagepay_integration_key'),
                'description' => __('admin/modules.sagepay_integration_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.sagepay_integration_key_placeholder'),
            ],
            'integration_password' => [
                'type' => 'password',
                'label' => __('admin/modules.sagepay_integration_password'),
                'description' => __('admin/modules.sagepay_integration_password_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.sagepay_integration_password_placeholder'),
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/modules.sagepay_currency'),
                'description' => __('admin/modules.sagepay_currency_desc'),
                'required' => true,
                'value' => 'GBP',
                'options' => [
                    'GBP' => 'British Pound (GBP)',
                    'EUR' => 'Euro (EUR)',
                    'USD' => 'US Dollar (USD)',
                ],
            ],
            'security_type' => [
                'type' => 'select',
                'label' => __('admin/modules.sagepay_security_type'),
                'description' => __('admin/modules.sagepay_security_type_desc'),
                'required' => true,
                'value' => '3d',
                'options' => [
                    '3d' => __('admin/modules.sagepay_security_type_3d'),
                    '2d' => __('admin/modules.sagepay_security_type_2d'),
                ],
            ],
        ]);
    }

    /**
     * Override default headers for Basic Auth with Integration Key/Password
     */
    protected function getDefaultHeaders()
    {
        $credentials = base64_encode(
            $this->getConfig('integration_key', '') . ':' . $this->getConfig('integration_password', '')
        );

        return [
            'Content-Type: application/json',
            'Accept: application/json',
            'Authorization: Basic ' . $credentials,
            'User-Agent: Payment Gateway Module',
        ];
    }

    // -------------------------------------------------------------------------
    // Private Helpers
    // -------------------------------------------------------------------------

    /**
     * Create a merchant session key for tokenization
     * POST /merchant-session-keys
     */
    private function createMerchantSessionKey(): array
    {
        $data = [
            'vendorName' => $this->getConfig('vendor_name', ''),
        ];

        $response = $this->callApi($this->apiUrl . '/merchant-session-keys', 'POST', $data);

        if ($response['http_code'] === 201 && isset($response['response']['merchantSessionKey'])) {
            return [
                'status' => 'success',
                'data' => [
                    'merchantSessionKey' => $response['response']['merchantSessionKey'],
                    'expiry' => $response['response']['expiry'] ?? '',
                ],
            ];
        }

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

    /**
     * Create a card identifier for use in transactions
     * POST /card-identifiers
     */
    private function createCardIdentifier(string $merchantSessionKey, array $params): array
    {
        $cardData = [
            'cardDetails' => [
                'cardholderName' => $params['card_holder'] ?? '',
                'cardNumber' => preg_replace('/\s+/', '', $params['card_number'] ?? ''),
                'expiryDate' => str_pad($params['expiry_month'] ?? '', 2, '0', STR_PAD_LEFT)
                    . substr($params['expiry_year'] ?? '', -2),
                'securityCode' => $params['cvv'] ?? '',
            ],
        ];

        // Card-identifier uses Bearer=merchantSessionKey (not the module's Basic-Auth
        // default), so route through postJson with explicit TLS-verify + timeout (PRC-888)
        // instead of the JSON callApi default.
        $headers = [
            'Accept: application/json',
            'Authorization: Bearer ' . $merchantSessionKey,
            'User-Agent: Payment Gateway Module',
        ];

        $response = $this->postJson($this->apiUrl . '/card-identifiers', $cardData, $headers, ['timeout' => 30]);
        $httpCode = $response['http_code'];
        $responseData = $response['response'];

        if ($httpCode === 201 && isset($responseData['cardIdentifier'])) {
            return [
                'status' => 'success',
                'data' => [
                    'cardIdentifier' => $responseData['cardIdentifier'],
                    'cardType' => $responseData['cardType'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.sagepay_card_identifier_failed'),
            'data' => [
                'http_code' => $httpCode,
                'response' => $responseData,
            ],
        ];
    }

    /**
     * Format amount to minor units (pence/cents)
     * Sage Pay expects amounts in the smallest currency unit
     */
    private function formatAmount($amount): int
    {
        return (int) round((float) $amount * 100);
    }
}
