<?php

namespace App\Modules\Payment\IPay88;

use App\Core\Payment\AbstractPayment;

class IPay88 extends AbstractPayment
{
    protected $paymentUrl = 'https://payment.ipay88.com.my/epayment/entry.asp';

    protected $requeryUrl = 'https://payment.ipay88.com.my/epayment/enquiry.asp';

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

    /**
     * Validate payment parameters and return ready status
     *
     * @return array
     */
    public function attemptPayment(array $params)
    {
        $requiredParams = ['amount', 'ref_no'];

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

        return [
            'status' => 'success',
            'message' => __('admin/modules.ipay88_ready'),
            'data' => [
                'payment_method' => 'ipay88',
                'amount' => $params['amount'],
                'ref_no' => $params['ref_no'],
                'redirect_url' => $this->paymentUrl,
            ],
        ];
    }

    /**
     * Build redirect form params for iPay88 hosted payment page
     *
     * Displayed Amount = number_format(amount, 2, '.', ',') for ALL currencies
     * (HostBill class.ipay88.php:44 drawForm). Signature strips [',','.'] from
     * amount for ALL currencies (HostBill generateSignature type=1, line 95).
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $merchantCode = $this->getConfig('merchant_code');
        $merchantKey = $this->getConfig('merchant_key');
        $currency = $params['currency'] ?? 'MYR';
        $amount = $params['amount'];
        $refNo = $params['ref_no'] ?? $params['order_id'] ?? '';

        // iPay88 displayed Amount: thousands-separated 2dp (HostBill drawForm).
        $displayAmount = number_format((float) $amount, 2, '.', ',');

        // Request signature (type=1): MerchantKey+MerchantCode+RefNo+strip(Amount)+Currency
        $signature = $this->generateRequestSignature(
            $merchantKey,
            $merchantCode,
            $refNo,
            $displayAmount,
            $currency
        );

        $redirectParams = [
            'MerchantCode' => $merchantCode,
            'PaymentId' => $params['payment_id'] ?? '',
            'RefNo' => $refNo,
            'Amount' => $displayAmount,
            'Currency' => $currency,
            'ProdDesc' => $params['description'] ?? '',
            'UserName' => $params['customer_name'] ?? '',
            'UserEmail' => $params['customer_email'] ?? '',
            'UserContact' => $params['customer_phone'] ?? '',
            'Remark' => $params['remark'] ?? '',
            'SignatureType' => 'SHA256',
            'Signature' => $signature,
            'ResponseURL' => $params['response_url'] ?? $params['success_url'] ?? '',
            'BackendURL' => $params['backend_url'] ?? $params['callback_url'] ?? '',
        ];

        return [
            'status' => 'redirect',
            'message' => __('admin/modules.ipay88_redirect'),
            'redirect_url' => $this->paymentUrl,
            'redirect_method' => 'POST',
            'redirect_params' => $redirectParams,
            'data' => [
                'ref_no' => $refNo,
                'amount' => $displayAmount,
                'currency' => $currency,
            ],
        ];
    }

    /**
     * Handle iPay88 payment response callback
     *
     * Status=1 means success, Status=0 means failed
     * Performs requery to double-check the payment status
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        $merchantCode = $this->getConfig('merchant_code');
        $merchantKey = $this->getConfig('merchant_key');

        // iPay88 callback fields (HostBill class.ipay88.php:70-77 callback()).
        $refNo = $params['RefNo'] ?? '';
        $amount = $params['Amount'] ?? '';
        $currency = $params['Currency'] ?? 'MYR';
        $paymentId = $params['PaymentId'] ?? '';
        $status = (string) ($params['Status'] ?? '');

        // Response signature (type=2): MerchantKey+MerchantCode+PaymentId+RefNo
        // +strip(Amount)+Currency+Status (HostBill generateSignature type=2, line 97).
        $expectedSignature = $this->generateCallbackSignature(
            $merchantKey,
            $merchantCode,
            $paymentId,
            $refNo,
            $amount,
            $currency,
            $status
        );

        $receivedSignature = $params['Signature'] ?? '';

        // Fail-CLOSED: invalid/missing signature is never treated as paid (PRC-888).
        if (! $this->secureCompare($expectedSignature, $receivedSignature)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.ipay88_invalid_signature'),
            ];
        }

        // Amount tamper guard: returned Amount must match the expected invoice amount.
        if (isset($params['expected_amount'])
            && $this->normalizeAmount($amount) !== $this->normalizeAmount($params['expected_amount'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.ipay88_amount_mismatch'),
            ];
        }

        if ($status === '1') {
            // Requery to double-check the payment status
            $requeryResult = $this->requeryPayment($merchantCode, $refNo, $amount);

            if ($requeryResult !== '00') {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.ipay88_requery_failed'),
                    'data' => [
                        'ref_no' => $refNo,
                    ],
                ];
            }

            return [
                'status' => 'success',
                'message' => __('admin/modules.ipay88_payment_success'),
                'transaction_id' => $params['TransId'] ?? '',
                'data' => [
                    'trans_id' => $params['TransId'] ?? '',
                    'ref_no' => $refNo,
                    'amount' => $amount,
                    'currency' => $currency,
                    'payment_id' => $params['PaymentId'] ?? '',
                    'remark' => $params['Remark'] ?? '',
                    'auth_code' => $params['AuthCode'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'failed',
            'message' => __('admin/modules.ipay88_payment_failed'),
            'data' => [
                'ref_no' => $refNo,
                'amount' => $amount,
                'currency' => $currency,
                'err_desc' => $params['ErrDesc'] ?? '',
            ],
        ];
    }

    /**
     * Refund is not supported via iPay88 API - must be done manually
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.ipay88_refund_not_supported'),
        ];
    }

    /**
     * Generate an iPay88 payment link.
     *
     * entry.asp is a POST endpoint (HostBill drawForm auto-POST form) — a bare
     * GET query string is not the documented flow, so return the POST target +
     * signed form fields for a self-posting form instead of a leaky GET URL.
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        $makePaymentResult = $this->makePayment($params);

        if ($makePaymentResult['status'] !== 'redirect') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.ipay88_payment_link_failed'),
            ];
        }

        return [
            'status' => 'success',
            'redirect_method' => 'POST',
            'payment_url' => $this->paymentUrl,
            'redirect_params' => $makePaymentResult['redirect_params'],
            'data' => [
                'ref_no' => $params['ref_no'] ?? $params['order_id'] ?? '',
                'amount' => $makePaymentResult['data']['amount'],
                'redirect_url' => $this->paymentUrl,
            ],
        ];
    }

    /**
     * Test connection - iPay88 has no test API endpoint,
     * return success if configuration is valid
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $configValidation = $this->validateConfig();

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

        // Do NOT echo merchant_code (credential-adjacent id) back in the response.
        return [
            'status' => 'success',
            'message' => __('admin/modules.ipay88_connection_success'),
            'data' => [
                'test_mode' => $this->isTestMode(),
                'payment_url' => $this->paymentUrl,
            ],
        ];
    }

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

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

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

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

    /**
     * iPay88 supported features
     */
    public function getFeatures(): array
    {
        return [
            'refund' => false,
            '3d_secure' => false,
            'save_card' => false,
            'recurring' => false,
            'fraud_check' => false,
            // PRC-1057 — iPay88 taksidi PaymentId-tabanlı (banka metodu), numeric-count değil.
            'installment' => true,
        ];
    }

    /**
     * PRC-1057 — PaymentId-tabanlı taksit planları.
     *
     * iPay88'de taksit ayrı ödeme-metodu ID'leridir (Maybank EzyPay=112, CIMB Easy Pay,
     * Public Bank ZIIP, Citibank FlexiPayment — 111..235). Numeric-count DEĞİL. Her plan
     * `payment_id` carrier'ı taşır; `count` yalnız gösterim/aylık (tenure). Taksit faizini
     * BANKA uygular → PROCP surcharge EKLEMEZ (surcharge_applied_by='gateway', surcharge=0).
     * Ref: HostBill ipay88/class.ipay88.php:26 PaymentId method map.
     */
    public function getInstallmentPlans(float $amount, ?string $currencyCode = null): array
    {
        // Tek çekim daima ilk (payment_id=null → iPay88 metod-seçim sayfası / default).
        $plans = [[
            'count' => 1, 'rate' => 0.0, 'fee' => 0.0, 'surcharge' => 0.0,
            'total' => round($amount, 2), 'monthly' => round($amount, 2),
            'surcharge_applied_by' => 'gateway', 'label' => (string) 1, 'payment_id' => null,
        ]];

        if (! filter_var($this->getConfig('installment_enabled', false), FILTER_VALIDATE_BOOLEAN)) {
            return $plans;
        }

        $methods = json_decode((string) $this->getConfig('installment_methods', '[]'), true);
        if (! is_array($methods)) {
            return $plans;
        }

        foreach ($methods as $m) {
            $paymentId = trim((string) ($m['payment_id'] ?? ''));
            if ($paymentId === '') {
                continue;
            }

            // Banka metodu tutar-aralığı filtresi (0/eksik = sınırsız).
            $minAmount = isset($m['min_amount']) ? (float) $m['min_amount'] : 0.0;
            $maxAmount = isset($m['max_amount']) ? (float) $m['max_amount'] : 0.0;
            if ($minAmount > 0 && $amount < $minAmount) {
                continue;
            }
            if ($maxAmount > 0 && $amount > $maxAmount) {
                continue;
            }

            $tenure = max(1, (int) ($m['tenure'] ?? 1)); // ay — yalnız gösterim/monthly

            $plans[] = [
                'count' => $tenure,
                'rate' => 0.0, 'fee' => 0.0, 'surcharge' => 0.0,
                'total' => round($amount, 2),
                'monthly' => round($amount / $tenure, 2),
                'surcharge_applied_by' => 'gateway',
                'label' => (string) ($m['label'] ?? ($paymentId . ' — ' . $tenure)),
                'payment_id' => $paymentId,
            ];
        }

        return $plans;
    }

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'merchant_code' => [
                'type' => 'text',
                'label' => __('admin/modules.ipay88_merchant_code'),
                'description' => __('admin/modules.ipay88_merchant_code_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.ipay88_merchant_code_placeholder'),
            ],
            'merchant_key' => [
                'type' => 'password',
                'label' => __('admin/modules.ipay88_merchant_key'),
                'description' => __('admin/modules.ipay88_merchant_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            // PRC-1057 — PaymentId-tabanlı taksit (numeric-count değil).
            'installment_enabled' => [
                'type' => 'checkbox',
                'label' => __('admin/modules.ipay88_installment_enabled'),
                'description' => __('admin/modules.ipay88_installment_enabled_desc'),
                'value' => 'false',
            ],
            'installment_methods' => [
                'type' => 'textarea',
                'label' => __('admin/modules.ipay88_installment_methods'),
                'description' => __('admin/modules.ipay88_installment_methods_desc'),
                'value' => '[]',
                'placeholder' => '[{"payment_id":"112","label":"Maybank EzyPay 6 ay","tenure":6,"min_amount":500}]',
            ],
        ]);
    }

    /**
     * Request signature — HostBill generateSignature type=1 (class.ipay88.php:95).
     * MerchantKey + MerchantCode + RefNo + strip([',','.'], Amount) + Currency.
     */
    protected function generateRequestSignature(string $merchantKey, string $merchantCode, string $refNo, string $amount, string $currency): string
    {
        $source = $merchantKey . $merchantCode . $refNo . $this->stripAmount($amount) . $currency;

        return hash('sha256', $source);
    }

    /**
     * Callback signature — HostBill generateSignature type=2 (class.ipay88.php:97).
     * MerchantKey + MerchantCode + PaymentId + RefNo + strip([',','.'], Amount)
     * + Currency + Status.
     */
    protected function generateCallbackSignature(string $merchantKey, string $merchantCode, string $paymentId, string $refNo, string $amount, string $currency, string $status): string
    {
        $source = $merchantKey . $merchantCode . $paymentId . $refNo . $this->stripAmount($amount) . $currency . $status;

        return hash('sha256', $source);
    }

    /**
     * Requery iPay88 to verify payment status
     *
     * POST to requery URL with MerchantCode, RefNo, Amount
     * Returns "00" if payment is confirmed successful. Routes through
     * postForm() for TLS verify + SSRF protocol allow-list (PRC-888).
     *
     * @return string Requery response body
     */
    protected function requeryPayment(string $merchantCode, string $refNo, string $amount): string
    {
        $result = $this->postForm($this->requeryUrl, [
            'MerchantCode' => $merchantCode,
            'RefNo' => $refNo,
            'Amount' => $amount,
        ]);

        // Do not surface transport internals to the client on error.
        if ($result['error'] !== null) {
            return '';
        }

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

    /**
     * Strip thousands/decimal separators from amount for signature hashing.
     * (HostBill str_replace([',','.'], '', $amount), ALL currencies.)
     */
    private function stripAmount(string $amount): string
    {
        return str_replace([',', '.'], '', $amount);
    }

    /**
     * Normalize an amount to minor-unit integer string for tamper comparison
     * (e.g. "1,000.00" and "1000" both → "100000").
     */
    private function normalizeAmount($amount): string
    {
        return number_format((float) str_replace(',', '', (string) $amount), 2, '', '');
    }

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