<?php

namespace App\Modules\Payment\PerfectMoney;

use App\Core\Payment\AbstractPayment;

class PerfectMoney extends AbstractPayment
{
    protected $paymentUrl = 'https://perfectmoney.com/api/step1.asp';

    protected $apiUrl = 'https://perfectmoney.com/acct';

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

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

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

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

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

    /**
     * Build Perfect Money SCI form parameters and return redirect data
     *
     * The caller should render a self-submitting HTML form that POSTs to the
     * payment URL with the returned form fields.
     */
    public function makePayment(array $params)
    {
        $currency = $params['currency'] ?? $this->getConfig('currency', 'USD');

        $formFields = [
            'PAYEE_ACCOUNT' => $this->getConfig('payee_account'),
            'PAYEE_NAME' => $params['payee_name'] ?? $this->getConfig('display_name', 'ProCP'),
            'PAYMENT_AMOUNT' => number_format((float) $params['amount'], 2, '.', ''),
            'PAYMENT_UNITS' => $currency,
            'PAYMENT_ID' => $params['payment_id'],
            'PAYMENT_URL' => $params['success_url'] ?? '',
            'PAYMENT_URL_METHOD' => 'POST',
            'NOPAYMENT_URL' => $params['cancel_url'] ?? '',
            'NOPAYMENT_URL_METHOD' => 'POST',
            'STATUS_URL' => $params['callback_url'] ?? $params['status_url'] ?? '',
            'SUGGESTED_MEMO' => $params['description'] ?? __('admin/modules.perfectmoney_payment_description', ['payment_id' => $params['payment_id']]),
        ];

        return [
            'status' => 'redirect',
            'message' => __('admin/modules.perfectmoney_redirect'),
            'redirect_url' => $this->paymentUrl,
            'data' => $formFields,
        ];
    }

    /**
     * Verify Perfect Money callback using V2_HASH
     *
     * V2_HASH = MD5(PAYMENT_ID:PAYEE_ACCOUNT:PAYMENT_AMOUNT:PAYMENT_UNITS:PAYMENT_BATCH_NUM:PAYER_ACCOUNT:TIMESTAMPGMT:uppercase(MD5(alternate_passphrase)))
     */
    public function handlePaymentCallback(array $params)
    {
        $alternatePassphrase = (string) $this->getConfig('alternate_passphrase');

        // Verify the V2_HASH signature
        if (! $this->verifyCallback($params, $alternatePassphrase)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.perfectmoney_invalid_hash'),
            ];
        }

        // Verify the payment amount matches
        $expectedAmount = $params['expected_amount'] ?? null;
        if ($expectedAmount !== null) {
            $receivedAmount = (float) ($params['PAYMENT_AMOUNT'] ?? 0);
            if (abs($receivedAmount - (float) $expectedAmount) > 0.01) {
                return [
                    'status' => 'failed',
                    'message' => __('admin/modules.perfectmoney_amount_mismatch'),
                    'data' => [
                        'expected' => $expectedAmount,
                        'received' => $receivedAmount,
                    ],
                ];
            }
        }

        // Verify payee account matches configured wallet
        $payeeAccount = $params['PAYEE_ACCOUNT'] ?? '';
        if (! empty($payeeAccount) && $payeeAccount !== $this->getConfig('payee_account')) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.perfectmoney_account_mismatch'),
            ];
        }

        // Verify currency matches configured wallet currency (ref: WiseCP:123, HostBill:44 PAYMENT_UNITS check)
        $configuredCurrency = $this->getConfig('currency', 'USD');
        $receivedCurrency = $params['PAYMENT_UNITS'] ?? '';
        if (! empty($receivedCurrency) && ! empty($configuredCurrency)
            && strtoupper((string) $receivedCurrency) !== strtoupper((string) $configuredCurrency)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.perfectmoney_currency_mismatch'),
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.perfectmoney_payment_success'),
            'transaction_id' => $params['PAYMENT_BATCH_NUM'] ?? '',
            'data' => [
                'payment_id' => $params['PAYMENT_ID'] ?? '',
                'payment_amount' => $params['PAYMENT_AMOUNT'] ?? '',
                'payment_units' => $params['PAYMENT_UNITS'] ?? '',
                'payment_batch_num' => $params['PAYMENT_BATCH_NUM'] ?? '',
                'payer_account' => $params['PAYER_ACCOUNT'] ?? '',
                'timestampgmt' => $params['TIMESTAMPGMT'] ?? '',
            ],
        ];
    }

    /**
     * Perfect Money does not support automated refunds.
     * Manual transfer can be initiated via /confirm.asp but requires account holder action.
     */
    public function refundPayment(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.perfectmoney_refund_not_supported'),
            'data' => [
                'note' => 'Perfect Money does not support automated refunds. Please process the refund manually through your Perfect Money account (/acct/confirm.asp) or transfer funds directly to the customer wallet.',
                'transaction_id' => $params['transaction_id'] ?? '',
                'amount' => $params['amount'] ?? 0,
            ],
        ];
    }

    /**
     * Generate a payment link using the same form-redirect approach
     */
    public function generatePaymentLink(array $params)
    {
        $result = $this->makePayment($params);

        if ($result['status'] === 'redirect') {
            $paymentUrl = $this->paymentUrl . '?' . http_build_query($result['data']);

            return [
                'status' => 'success',
                'payment_url' => $paymentUrl,
                'data' => $result['data'],
            ];
        }

        return $result;
    }

    /**
     * Test connection by checking account balance via Perfect Money API
     */
    public function testConnection(array $params = [])
    {
        $postData = [
            'AccountID' => $this->getConfig('payee_account'),
            'PassPhrase' => $this->getConfig('passphrase'),
        ];

        // Foundation transport (PRC-888): TLS verify + protocol restriction on.
        $response = $this->postForm($this->apiUrl . '/balance.asp', $postData);

        if ($response['error'] === null && $response['http_code'] === 200) {
            // Parse the HTML response for account balances
            $balances = $this->parseBalanceResponse($response['raw_response']);

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

            // Check for error in response
            $error = $this->parseErrorResponse($response['raw_response']);
            if ($error) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.perfectmoney_connection_failed'),
                    'data' => ['error' => $error],
                ];
            }
        }

        // Never leak raw_response (account id/balance HTML) upstream.
        return [
            'status' => 'error',
            'message' => __('admin/modules.perfectmoney_connection_failed'),
            'data' => ['http_code' => $response['http_code']],
        ];
    }

    /**
     * Validate required configuration fields
     */
    public function validateConfig()
    {
        $required = ['payee_account', 'passphrase', 'alternate_passphrase'];
        $missing = [];

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

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

        // Validate payee account format (U followed by digits)
        $payeeAccount = $this->getConfig('payee_account');
        if (! preg_match('/^[UEG]\d+$/', $payeeAccount)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.perfectmoney_invalid_account_format'),
            ];
        }

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

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

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

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'payee_account' => [
                'type' => 'text',
                'label' => __('admin/modules.perfectmoney_field_payee_account'),
                'description' => __('admin/modules.perfectmoney_field_payee_account_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => 'U1234567',
            ],
            'passphrase' => [
                'type' => 'password',
                'label' => __('admin/modules.perfectmoney_field_passphrase'),
                'description' => __('admin/modules.perfectmoney_field_passphrase_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            'alternate_passphrase' => [
                'type' => 'password',
                'label' => __('admin/modules.perfectmoney_field_alternate_passphrase'),
                'description' => __('admin/modules.perfectmoney_field_alternate_passphrase_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/modules.perfectmoney_field_currency'),
                'description' => __('admin/modules.perfectmoney_field_currency_desc'),
                'required' => true,
                'value' => 'USD',
                'options' => [
                    'USD' => 'USD - US Dollar',
                    'EUR' => 'EUR - Euro',
                    'OAU' => 'OAU - Gold (Troy Ounce)',
                ],
            ],
        ]);
    }

    /**
     * Generate V2_HASH for Perfect Money callback verification
     *
     * Hash = MD5(PAYMENT_ID:PAYEE_ACCOUNT:PAYMENT_AMOUNT:PAYMENT_UNITS:PAYMENT_BATCH_NUM:PAYER_ACCOUNT:TIMESTAMPGMT:uppercase(MD5(alternate_passphrase)))
     */
    protected function generateV2Hash(array $params, string $alternatePassphrase): string
    {
        $alternatePassphraseHash = strtoupper(md5($alternatePassphrase));

        // Ref: HostBill class.perfectmoney.php:38 + WiseCP PerfectMoney.php:86-87 + PM step1.asp spec
        // md5(alternate_passphrase) MUST precede TIMESTAMPGMT.
        $hashString = implode(':', [
            $params['PAYMENT_ID'] ?? '',
            $params['PAYEE_ACCOUNT'] ?? '',
            $params['PAYMENT_AMOUNT'] ?? '',
            $params['PAYMENT_UNITS'] ?? '',
            $params['PAYMENT_BATCH_NUM'] ?? '',
            $params['PAYER_ACCOUNT'] ?? '',
            $alternatePassphraseHash,
            $params['TIMESTAMPGMT'] ?? '',
        ]);

        return strtoupper(md5($hashString));
    }

    /**
     * Verify callback V2_HASH signature
     */
    protected function verifyCallback(array $params, string $alternatePassphrase): bool
    {
        if (empty($params['V2_HASH']) || $alternatePassphrase === '') {
            return false;
        }

        $expectedHash = $this->generateV2Hash($params, $alternatePassphrase);

        // Fail-closed timing-safe compare (AbstractPayment PRC-888).
        return $this->secureCompare($expectedHash, strtoupper((string) $params['V2_HASH']));
    }

    /**
     * Parse balance response from Perfect Money HTML output
     *
     * Perfect Money returns balances in hidden input fields:
     * <input name='AccountName' type='hidden' value='123.45'>
     */
    private function parseBalanceResponse(string $response): array
    {
        $balances = [];

        if (preg_match_all('/<input[^>]+name=[\'"]([^"\']+)[\'"][^>]+value=[\'"]([^"\']+)[\'"][^>]*>/i', $response, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $name = $match[1];
                $value = $match[2];

                // Balance fields are typically account IDs like U1234567
                if (preg_match('/^[UEG]\d+$/', $name)) {
                    $balances[$name] = $value;
                }
            }
        }

        return $balances;
    }

    /**
     * Parse error message from Perfect Money HTML response
     */
    private function parseErrorResponse(string $response): ?string
    {
        if (preg_match('/<input[^>]+name=[\'"]ERROR[\'"][^>]+value=[\'"]([^"\']+)[\'"][^>]*>/i', $response, $match)) {
            return $match[1];
        }

        return null;
    }
}
