<?php

namespace App\Modules\Payment\SecureTrading;

use App\Core\Payment\AbstractPayment;

class SecureTrading extends AbstractPayment
{
    protected $apiUrl;

    protected $restApiUrl = 'https://webservices.securetrading.net/json/';

    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->apiUrl = $this->isTestMode()
            ? 'https://webservices.securetrading.net:6443/xml/'
            : 'https://webservices.securetrading.net:6443/xml/';
    }

    /**
     * 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.securetrading_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.securetrading_missing_card_param', ['param' => $cardParam]),
                    ];
                }
            }
        }

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

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

    /**
     * Process payment via Trust Payments REST API
     * POST JSON with requesttypedescriptions=AUTH
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $currency = $params['currency'] ?? $this->getConfig('currency', 'USD');
        $baseAmount = (int) round((float) $params['amount'] * 100);

        $expiryDate = str_pad($params['expiry_month'] ?? '', 2, '0', STR_PAD_LEFT)
            . '/' . ($params['expiry_year'] ?? '');

        $requestData = [
            'alias' => $this->getConfig('username', ''),
            'version' => '1.00',
            'request' => [
                [
                    'requesttypedescriptions' => ['AUTH'],
                    'sitereference' => $this->getConfig('site_reference', ''),
                    'accounttypedescription' => 'ECOM',
                    'baseamount' => $baseAmount,
                    'currencyiso3a' => strtoupper($currency),
                    'pan' => str_replace([' ', '-'], '', $params['card_number'] ?? ''),
                    'expirydate' => $expiryDate,
                    'securitycode' => $params['cvv'] ?? $params['security_code'] ?? '',
                    'billingfirstname' => $params['first_name'] ?? $params['billing_first_name'] ?? '',
                    'billinglastname' => $params['last_name'] ?? $params['billing_last_name'] ?? '',
                    'billingemail' => $params['email'] ?? $params['customer_email'] ?? '',
                    'orderreference' => (string) ($params['order_id'] ?? ''),
                ],
            ],
        ];

        // PRC-888: postJson helper (TLS verify default-on) instead of global callApi.
        $response = $this->postJson($this->restApiUrl, $requestData, $this->buildApiHeaders());

        $result = $this->parseSingleResponse($response);

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

        $errorCode = (int) ($result['errorcode'] ?? -1);
        $settleStatus = (int) ($result['settlestatus'] ?? -1);

        if ($errorCode === 0 && $settleStatus >= 0) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.securetrading_payment_success'),
                'transaction_id' => $result['transactionreference'] ?? '',
                'auth_code' => $result['authcode'] ?? '',
                'data' => [
                    'transaction_id' => $result['transactionreference'] ?? '',
                    'auth_code' => $result['authcode'] ?? '',
                    'settle_status' => $settleStatus,
                    'amount' => $params['amount'],
                    'currency' => $currency,
                    'order_reference' => $params['order_id'] ?? '',
                    'security_response' => $result['securityresponseaddress'] ?? '',
                    'security_response_postcode' => $result['securityresponsepostcode'] ?? '',
                    'security_response_security_code' => $result['securityresponsesecuritycode'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'failed',
            'message' => __('admin/modules.securetrading_payment_failed'),
            'data' => [
                'error_code' => $errorCode,
                'error_message' => $result['errormessage'] ?? '',
                'settle_status' => $settleStatus,
                'transaction_reference' => $result['transactionreference'] ?? '',
            ],
        ];
    }

    /**
     * Start 3D Secure payment - send THREEDQUERY request
     * If enrolled=Y, return redirect with acsurl, md, pareq
     *
     * @return array
     */
    public function start3DPayment(array $params)
    {
        $currency = $params['currency'] ?? $this->getConfig('currency', 'USD');
        $baseAmount = (int) round((float) $params['amount'] * 100);

        $expiryDate = str_pad($params['expiry_month'] ?? '', 2, '0', STR_PAD_LEFT)
            . '/' . ($params['expiry_year'] ?? '');

        $requestData = [
            'alias' => $this->getConfig('username', ''),
            'version' => '1.00',
            'request' => [
                [
                    'requesttypedescriptions' => ['THREEDQUERY'],
                    'sitereference' => $this->getConfig('site_reference', ''),
                    'accounttypedescription' => 'ECOM',
                    'baseamount' => $baseAmount,
                    'currencyiso3a' => strtoupper($currency),
                    'pan' => str_replace([' ', '-'], '', $params['card_number'] ?? ''),
                    'expirydate' => $expiryDate,
                    'securitycode' => $params['cvv'] ?? $params['security_code'] ?? '',
                    'billingfirstname' => $params['first_name'] ?? $params['billing_first_name'] ?? '',
                    'billinglastname' => $params['last_name'] ?? $params['billing_last_name'] ?? '',
                    'billingemail' => $params['email'] ?? $params['customer_email'] ?? '',
                    'orderreference' => (string) ($params['order_id'] ?? ''),
                    'termurl' => $params['return_url'] ?? '',
                ],
            ],
        ];

        $response = $this->postJson($this->restApiUrl, $requestData, $this->buildApiHeaders());

        $result = $this->parseSingleResponse($response);

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

        $errorCode = (int) ($result['errorcode'] ?? -1);
        $enrolled = $result['enrolled'] ?? '';

        if ($errorCode !== 0) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.securetrading_3d_query_failed'),
                'data' => [
                    'error_code' => $errorCode,
                    'error_message' => $result['errormessage'] ?? '',
                ],
            ];
        }

        // Card enrolled in 3D Secure - redirect to ACS
        if (strtoupper($enrolled) === 'Y') {
            $acsUrl = $result['acsurl'] ?? '';
            $paReq = $result['pareq'] ?? '';

            // Guard: missing ACS URL / PaReq → silent broken redirect; fail explicitly.
            if ($acsUrl === '' || $paReq === '') {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.securetrading_3d_query_failed'),
                    'data' => [
                        'error_code' => $errorCode,
                        'transaction_reference' => $result['transactionreference'] ?? '',
                    ],
                ];
            }

            return [
                'status' => 'redirect',
                'message' => __('admin/modules.securetrading_3d_redirect'),
                'redirect_url' => $acsUrl,
                'redirect_method' => 'POST',
                'redirect_params' => [
                    'PaReq' => $paReq,
                    'MD' => $result['md'] ?? '',
                    'TermUrl' => $params['return_url'] ?? '',
                ],
                'data' => [
                    'transaction_id' => $result['transactionreference'] ?? '',
                    'enrolled' => $enrolled,
                    'md' => $result['md'] ?? '',
                ],
            ];
        }

        // Card not enrolled - proceed with standard AUTH
        return $this->makePayment($params);
    }

    /**
     * Complete 3D Secure payment - send AUTH request with pares from 3D callback
     *
     * @return array
     */
    public function complete3DPayment(array $params)
    {
        if (empty($params['pares']) && empty($params['PaRes'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.securetrading_missing_pares'),
            ];
        }

        $pares = $params['pares'] ?? $params['PaRes'] ?? '';
        $md = $params['md'] ?? $params['MD'] ?? '';

        $requestData = [
            'alias' => $this->getConfig('username', ''),
            'version' => '1.00',
            'request' => [
                [
                    'requesttypedescriptions' => ['AUTH'],
                    'sitereference' => $this->getConfig('site_reference', ''),
                    'pares' => $pares,
                    'md' => $md,
                ],
            ],
        ];

        $response = $this->postJson($this->restApiUrl, $requestData, $this->buildApiHeaders());

        $result = $this->parseSingleResponse($response);

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

        // Bind the 3DS AUTH response to the transaction we started (session-tracked),
        // so a returned PaRes cannot be replayed against a different transaction.
        $expectedRef = $params['transaction_id'] ?? $params['expected_transaction_reference'] ?? '';
        $returnedRef = $result['transactionreference'] ?? '';
        if ($expectedRef !== '' && $returnedRef !== '' && ! $this->secureCompare((string) $expectedRef, (string) $returnedRef)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.securetrading_3d_payment_failed'),
                'data' => ['error_code' => -1],
            ];
        }

        $errorCode = (int) ($result['errorcode'] ?? -1);
        $settleStatus = (int) ($result['settlestatus'] ?? -1);

        // Evaluate 3DS liability FAIL-CLOSED: only an explicit authenticated/attempted
        // 3DS result (Y/A) is accepted. Empty/missing threedresult means the 3DS context
        // was lost and must NOT be treated as paid; N/U/E (failed/unavailable/error) likewise.
        $threedResult = strtoupper((string) ($result['threedresult'] ?? $result['status'] ?? ''));
        $threedOk = in_array($threedResult, ['Y', 'A'], true);

        if ($errorCode === 0 && $settleStatus >= 0 && $threedOk) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.securetrading_3d_payment_success'),
                'transaction_id' => $result['transactionreference'] ?? '',
                'auth_code' => $result['authcode'] ?? '',
                'data' => [
                    'transaction_id' => $result['transactionreference'] ?? '',
                    'auth_code' => $result['authcode'] ?? '',
                    'settle_status' => $settleStatus,
                    'threed_secure_status' => $result['threedresult'] ?? '',
                    'enrolled' => $result['enrolled'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'failed',
            'message' => __('admin/modules.securetrading_3d_payment_failed'),
            'data' => [
                'error_code' => $errorCode,
                'error_message' => $result['errormessage'] ?? '',
                'settle_status' => $settleStatus,
                'transaction_reference' => $result['transactionreference'] ?? '',
            ],
        ];
    }

    /**
     * Handle payment callback - parse response, check errorcode and settlestatus
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        // FAIL-CLOSED: Trust Payments URL-notification bütünlüğü `responsesitesecurity`
        // (SHA256, alfabetik alan-değerleri + notification password) ile doğrulanır;
        // hash yok/yanlışsa ödeme ASLA "paid" sayılmaz (secureCompare, PRC-888).
        // Ref: help.trustpayments.com/.../Response-site-security.
        if (! $this->verifyNotificationSignature($params)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.securetrading_invalid_callback'),
            ];
        }

        $errorCode = isset($params['errorcode']) ? (int) $params['errorcode'] : -1;
        $settleStatus = isset($params['settlestatus']) ? (int) $params['settlestatus'] : -1;
        $transactionReference = $params['transactionreference'] ?? '';

        if ($errorCode === 0 && $settleStatus >= 0) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.securetrading_payment_success'),
                'transaction_id' => $transactionReference,
                'auth_code' => $params['authcode'] ?? '',
                'data' => [
                    'transaction_id' => $transactionReference,
                    'auth_code' => $params['authcode'] ?? '',
                    'settle_status' => $settleStatus,
                    'order_reference' => $params['orderreference'] ?? '',
                    'amount' => $params['baseamount'] ?? '',
                    'currency' => $params['currencyiso3a'] ?? '',
                ],
            ];
        }

        if ($errorCode === 0 && $settleStatus === -1) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.securetrading_payment_declined'),
                'data' => [
                    'error_code' => $errorCode,
                    'settle_status' => $settleStatus,
                    'transaction_reference' => $transactionReference,
                    'error_message' => $params['errormessage'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'failed',
            'message' => __('admin/modules.securetrading_payment_failed'),
            'data' => [
                'error_code' => $errorCode,
                'error_message' => $params['errormessage'] ?? '',
                'settle_status' => $settleStatus,
                'transaction_reference' => $transactionReference,
            ],
        ];
    }

    /**
     * Refund payment via Trust Payments REST API
     * POST JSON with requesttypedescriptions=REFUND and parenttransactionreference
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        if (empty($params['transaction_id'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.securetrading_refund_missing_transaction'),
            ];
        }

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

        $currency = $params['currency'] ?? $this->getConfig('currency', 'USD');
        $baseAmount = (int) round((float) $params['amount'] * 100);

        $requestData = [
            'alias' => $this->getConfig('username', ''),
            'version' => '1.00',
            'request' => [
                [
                    'requesttypedescriptions' => ['REFUND'],
                    'sitereference' => $this->getConfig('site_reference', ''),
                    'parenttransactionreference' => $params['transaction_id'],
                    'baseamount' => $baseAmount,
                    'currencyiso3a' => strtoupper($currency),
                ],
            ],
        ];

        $response = $this->postJson($this->restApiUrl, $requestData, $this->buildApiHeaders());

        $result = $this->parseSingleResponse($response);

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

        $errorCode = (int) ($result['errorcode'] ?? -1);

        if ($errorCode === 0) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.securetrading_refund_success'),
                'refund_id' => $result['transactionreference'] ?? '',
                'data' => [
                    'refund_transaction_id' => $result['transactionreference'] ?? '',
                    'parent_transaction_id' => $params['transaction_id'],
                    'amount' => $params['amount'],
                    'currency' => $currency,
                    'settle_status' => $result['settlestatus'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.securetrading_refund_failed'),
            'data' => [
                'error_code' => $errorCode,
                'error_message' => $result['errormessage'] ?? '',
                'transaction_reference' => $result['transactionreference'] ?? '',
            ],
        ];
    }

    /**
     * Payment link generation is not supported by Trust Payments direct integration
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.securetrading_payment_link_not_supported'),
        ];
    }

    /**
     * Test API connection by sending ACCOUNTCHECK request
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $configValidation = $this->validateConfig();

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

        $requestData = [
            'alias' => $this->getConfig('username', ''),
            'version' => '1.00',
            'request' => [
                [
                    'requesttypedescriptions' => ['ACCOUNTCHECK'],
                    'sitereference' => $this->getConfig('site_reference', ''),
                    'accounttypedescription' => 'ECOM',
                    'pan' => '4111111111111111',
                    'expirydate' => '12/2099',
                    'securitycode' => '123',
                    'baseamount' => 0,
                    'currencyiso3a' => $this->getConfig('currency', 'USD'),
                ],
            ],
        ];

        $response = $this->postJson($this->restApiUrl, $requestData, $this->buildApiHeaders());

        $result = $this->parseSingleResponse($response);

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

        $errorCode = (int) ($result['errorcode'] ?? -1);

        // errorcode 0 means successful connection (even if card is declined for ACCOUNTCHECK)
        if ($errorCode === 0) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.securetrading_connection_success'),
                'data' => [
                    'site_reference' => $this->getConfig('site_reference'),
                    'test_mode' => $this->isTestMode(),
                    'api_url' => $this->restApiUrl,
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => __('admin/modules.securetrading_connection_failed'),
            'data' => [
                'error_code' => $errorCode,
                'error_message' => $result['errormessage'] ?? '',
            ],
        ];
    }

    /**
     * Validate required configuration fields
     *
     * @return array
     */
    public function validateConfig()
    {
        $required = ['site_reference', 'username', 'password', 'site_security_password'];
        $missing = [];

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

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

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

    /**
     * Return supported features
     */
    public function getFeatures(): array
    {
        // 3DS is on by default; an admin can force 2D-only per gateway via the
        // 3d_secure_enabled toggle (accounts without a 3DS profile). The config
        // value is the single decision source PaymentController reads.
        $threeDEnabled = $this->getConfig('3d_secure_enabled', true);
        $threeDEnabled = ! in_array($threeDEnabled, [false, 0, '0', '', 'false', 'off', 'no'], true);

        return [
            'refund' => true,
            '3d_secure' => $threeDEnabled,
            'save_card' => false,
            'recurring' => false,
            '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(), [
            'site_reference' => [
                'type' => 'text',
                'label' => __('admin/modules.securetrading_site_reference'),
                'description' => __('admin/modules.securetrading_site_reference_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.securetrading_site_reference_placeholder'),
            ],
            'username' => [
                'type' => 'text',
                'label' => __('admin/modules.securetrading_username'),
                'description' => __('admin/modules.securetrading_username_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.securetrading_username_placeholder'),
            ],
            'password' => [
                'type' => 'password',
                'label' => __('admin/modules.securetrading_password'),
                'description' => __('admin/modules.securetrading_password_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.securetrading_password_placeholder'),
            ],
            'site_security_password' => [
                'type' => 'password',
                'label' => __('admin/modules.securetrading_site_security_password'),
                'description' => __('admin/modules.securetrading_site_security_password_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.securetrading_site_security_password_placeholder'),
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/modules.securetrading_currency'),
                'description' => __('admin/modules.securetrading_currency_desc'),
                'required' => true,
                'value' => 'USD',
                'options' => [
                    'USD' => 'US Dollar (USD)',
                    'EUR' => 'Euro (EUR)',
                    'GBP' => 'British Pound (GBP)',
                ],
            ],
            '3d_secure_enabled' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.securetrading_3d_secure_enabled'),
                'description' => __('admin/modules.securetrading_3d_secure_enabled_desc'),
                'required' => false,
                'value' => true,
            ],
        ]);
    }

    /**
     * Override default headers for Basic Auth with username/password
     */
    protected function getDefaultHeaders()
    {
        return array_merge(
            $this->buildApiHeaders(),
            ['User-Agent: Payment Gateway Module']
        );
    }

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

    /**
     * Extract the single response block from a Trust Payments JSON envelope.
     * Returns null on malformed/empty response (never leaks raw body upstream).
     */
    private function parseSingleResponse(array $httpResult): ?array
    {
        $decoded = $httpResult['response'] ?? null;
        if (! is_array($decoded)) {
            return null;
        }

        $result = $decoded['response'][0] ?? null;

        return is_array($result) ? $result : null;
    }

    /**
     * FAIL-CLOSED Trust Payments URL-notification signature verification.
     *
     * `responsesitesecurity` = sha256( concat(all returned field VALUES in ASCII
     * field-name order, excluding notificationreference + responsesitesecurity)
     * . notification_password ). Ref: help.trustpayments.com/.../Response-site-security.
     * Missing config/hash or mismatch → false (payment must not be trusted).
     */
    private function verifyNotificationSignature(array $params): bool
    {
        $password = (string) $this->getConfig('site_security_password', '');
        $received = (string) ($params['responsesitesecurity'] ?? $params['sitesecurity'] ?? '');

        // Fail-closed: no shared secret configured or no hash supplied → reject.
        if ($password === '' || $received === '') {
            return false;
        }

        $fields = $params;
        unset($fields['notificationreference'], $fields['responsesitesecurity'], $fields['sitesecurity']);
        ksort($fields, SORT_STRING);

        $concat = '';
        foreach ($fields as $value) {
            $concat .= is_array($value) ? implode('', array_map('strval', $value)) : (string) $value;
        }

        $expected = hash('sha256', $concat . $password);

        return $this->secureCompare($expected, strtolower($received));
    }

    /**
     * Build API headers with Basic Auth credentials
     */
    private function buildApiHeaders(): array
    {
        $credentials = base64_encode(
            $this->getConfig('username', '') . ':' . $this->getConfig('password', '')
        );

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