<?php

namespace App\Modules\Payment\CoinPayments;

use App\Core\Payment\AbstractPayment;

class CoinPayments extends AbstractPayment
{
    protected $apiUrl = 'https://www.coinpayments.net/api.php';

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

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

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

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

        return [
            'status' => 'ready',
            'message' => __('admin/modules.coinpayments_ready'),
            'data' => [
                'amount' => $params['amount'],
                'currency' => $params['currency'],
                'buyer_email' => $params['buyer_email'],
            ],
        ];
    }

    /**
     * Create CoinPayments transaction and return checkout URL
     */
    public function makePayment(array $params)
    {
        $currency1 = $params['currency'] ?? $this->getConfig('currency', 'USD');
        $currency2 = $params['currency2'] ?? $this->getConfig('accepted_coins', 'BTC');

        $postData = [
            'cmd' => 'create_transaction',
            'amount' => (float) $params['amount'],
            'currency1' => $currency1,
            'currency2' => $currency2,
            'buyer_email' => $params['buyer_email'],
            'item_name' => $params['description'] ?? __('admin/modules.coinpayments_order_description', ['order_id' => $params['order_id'] ?? '']),
            'item_number' => $params['order_id'] ?? '',
            'ipn_url' => $params['notification_url'] ?? $this->getConfig('ipn_url', ''),
            'success_url' => $params['success_url'] ?? '',
            'cancel_url' => $params['cancel_url'] ?? '',
        ];

        $response = $this->callCoinPaymentsApi($postData);

        if (isset($response['response']['error']) && $response['response']['error'] === 'ok' && isset($response['response']['result'])) {
            $result = $response['response']['result'];

            return [
                'status' => 'redirect',
                'message' => __('admin/modules.coinpayments_transaction_created'),
                'transaction_id' => $result['txn_id'] ?? '',
                'redirect_url' => $result['checkout_url'] ?? $result['status_url'] ?? '',
                'data' => [
                    'txn_id' => $result['txn_id'] ?? '',
                    'checkout_url' => $result['checkout_url'] ?? '',
                    'status_url' => $result['status_url'] ?? '',
                    'amount' => $result['amount'] ?? $params['amount'],
                    'address' => $result['address'] ?? '',
                    'qrcode_url' => $result['qrcode_url'] ?? '',
                    'timeout' => $result['timeout'] ?? null,
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['response']['error'] ?? __('admin/modules.coinpayments_transaction_create_failed'),
            // PCI/privacy: never surface raw_response (may echo request incl. key) to upper-layer logs.
            'data' => ['http_code' => $response['http_code'] ?? 0],
        ];
    }

    /**
     * Handle CoinPayments IPN (Instant Payment Notification) callback
     */
    public function handlePaymentCallback(array $params)
    {
        // IPN signs the RAW POST body with ipn_secret and sends HMAC-SHA512 in the `HMAC` HTTP header
        // (WiseCP CoinPayments.php:126 file_get_contents('php://input') + :117 $_SERVER['HTTP_HMAC'];
        // HostBill:50). PaymentController forwards only $request->all() (parsed form), so fall back to
        // reading the raw body + HMAC header directly when the caller did not supply them.
        $rawBody = $params['raw_body'] ?? '';
        if ($rawBody === '') {
            $rawBody = (string) @file_get_contents('php://input');
        }
        $hmacHeader = $params['hmac'] ?? $params['HTTP_HMAC'] ?? ($_SERVER['HTTP_HMAC'] ?? '');
        $ipnSecret = $this->getConfig('ipn_secret', '');

        // Verify IPN HMAC signature (fail-closed — no signature/body/secret => reject).
        if (! $this->verifyIPN($hmacHeader, $rawBody, $ipnSecret)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_invalid_ipn_signature'),
            ];
        }

        // Trust the HMAC-verified raw body over unverified caller-parsed params where both exist.
        $verified = $params;
        if ($rawBody !== '') {
            parse_str($rawBody, $parsed);
            if (is_array($parsed) && ! empty($parsed)) {
                $verified = $parsed;
            }
        }

        // Verify merchant ID matches (HostBill:167, WiseCP:159).
        $merchantId = $verified['merchant'] ?? '';
        if (! $this->secureCompare((string) $this->getConfig('merchant_id', ''), (string) $merchantId)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_merchant_mismatch'),
            ];
        }

        // Only merchant IPN types carry a payment status (HostBill:150, WiseCP:145).
        $ipnType = $verified['ipn_type'] ?? '';
        if ($ipnType !== '' && ! in_array($ipnType, ['button', 'simple'], true)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_invalid_ipn_type', ['type' => $ipnType]),
            ];
        }

        $txnId = $verified['txn_id'] ?? '';
        $status = (int) ($verified['status'] ?? 0);
        $statusText = $verified['status_text'] ?? '';
        $amount1 = $verified['amount1'] ?? 0;
        $currency1 = $verified['currency1'] ?? '';

        // Reject non-positive / malformed amount (HostBill:158, WiseCP:172).
        if ((float) $amount1 <= 0) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_invalid_ipn_amount'),
            ];
        }

        // Currency + amount reconciliation against caller-supplied expected context (HostBill:174-176
        // currency1==invoice currency; WiseCP:183 amount_paid>=total). Only enforced when the caller
        // passes the expected invoice figures — the module has no invoice access.
        $expectedCurrency = $params['expected_currency'] ?? $params['invoice_currency'] ?? '';
        if ($expectedCurrency !== '' && strtoupper((string) $currency1) !== strtoupper((string) $expectedCurrency)) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_currency_mismatch'),
            ];
        }
        $expectedAmount = $params['expected_amount'] ?? $params['invoice_amount'] ?? null;
        if ($expectedAmount !== null && $expectedAmount !== '' && (float) $amount1 + 0.00001 < (float) $expectedAmount) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_amount_underpaid'),
            ];
        }

        // Map IPN status codes: >=100 (or 2 = queued for nightly payout) = complete, <0 = error, 0-99 = pending
        if ($status >= 100 || $status === 2) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.coinpayments_payment_completed'),
                'transaction_id' => $txnId,
                'data' => [
                    'txn_id' => $txnId,
                    'coinpayments_status' => $status,
                    'status_text' => $statusText,
                    'amount' => $amount1,
                    'currency' => $currency1,
                    'received_amount' => $verified['received_amount'] ?? 0,
                    'received_confirms' => $verified['received_confirms'] ?? 0,
                ],
            ];
        }

        if ($status < 0) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.coinpayments_payment_failed', ['status_text' => $statusText]),
                'transaction_id' => $txnId,
                'data' => [
                    'txn_id' => $txnId,
                    'coinpayments_status' => $status,
                    'status_text' => $statusText,
                    'amount' => $amount1,
                    'currency' => $currency1,
                ],
            ];
        }

        // Status 0-99: pending/waiting for confirms
        return [
            'status' => 'pending',
            'message' => __('admin/modules.coinpayments_payment_pending', ['status_text' => $statusText]),
            'transaction_id' => $txnId,
            'data' => [
                'txn_id' => $txnId,
                'coinpayments_status' => $status,
                'status_text' => $statusText,
                'amount' => $amount1,
                'currency' => $currency1,
                'received_amount' => $params['received_amount'] ?? 0,
                'received_confirms' => $params['received_confirms'] ?? 0,
            ],
        ];
    }

    /**
     * CoinPayments does not support automated refunds
     */
    public function refundPayment(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.coinpayments_refund_not_supported'),
            'data' => [
                'note' => 'CoinPayments does not support automated refunds. Please process the refund manually through the CoinPayments dashboard or send crypto directly to the customer wallet.',
                'txn_id' => $params['transaction_id'] ?? '',
                'amount' => $params['amount'] ?? 0,
            ],
        ];
    }

    /**
     * Generate a payment link by creating a CoinPayments transaction
     */
    public function generatePaymentLink(array $params)
    {
        $currency1 = $params['currency'] ?? $this->getConfig('currency', 'USD');
        $currency2 = $params['currency2'] ?? $this->getConfig('accepted_coins', 'BTC');

        $postData = [
            'cmd' => 'create_transaction',
            'amount' => (float) $params['amount'],
            'currency1' => $currency1,
            'currency2' => $currency2,
            'buyer_email' => $params['buyer_email'] ?? '',
            'item_name' => $params['description'] ?? __('admin/modules.coinpayments_order_description', ['order_id' => $params['order_id'] ?? '']),
            'item_number' => $params['order_id'] ?? '',
            'ipn_url' => $params['notification_url'] ?? $this->getConfig('ipn_url', ''),
            'success_url' => $params['success_url'] ?? '',
            'cancel_url' => $params['cancel_url'] ?? '',
        ];

        $response = $this->callCoinPaymentsApi($postData);

        if (isset($response['response']['error']) && $response['response']['error'] === 'ok' && isset($response['response']['result'])) {
            $result = $response['response']['result'];

            return [
                'status' => 'success',
                'payment_url' => $result['checkout_url'] ?? $result['status_url'] ?? '',
                'transaction_id' => $result['txn_id'] ?? '',
                'data' => [
                    'txn_id' => $result['txn_id'] ?? '',
                    'checkout_url' => $result['checkout_url'] ?? '',
                    'status_url' => $result['status_url'] ?? '',
                    'amount' => $result['amount'] ?? $params['amount'],
                    'address' => $result['address'] ?? '',
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => $response['response']['error'] ?? __('admin/modules.coinpayments_payment_link_failed'),
            // PCI/privacy: never surface raw_response to upper-layer logs.
            'data' => ['http_code' => $response['http_code'] ?? 0],
        ];
    }

    /**
     * Test API connection using get_basic_info command
     */
    public function testConnection(array $params = [])
    {
        $postData = [
            'cmd' => 'get_basic_info',
        ];

        $response = $this->callCoinPaymentsApi($postData);

        if (isset($response['response']['error']) && $response['response']['error'] === 'ok') {
            return [
                'status' => 'success',
                'message' => __('admin/modules.coinpayments_connection_success'),
                'data' => [
                    'username' => $response['response']['result']['username'] ?? '',
                    'merchant_id' => $response['response']['result']['merchant_id'] ?? '',
                    'email' => $response['response']['result']['email'] ?? '',
                    'public_name' => $response['response']['result']['public_name'] ?? '',
                ],
            ];
        }

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

    /**
     * Validate module configuration
     */
    public function validateConfig()
    {
        $required = ['merchant_id', 'public_key', 'private_key', 'ipn_secret'];
        $missing = [];

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

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

        return [
            'status' => 'success',
            'message' => __('admin/modules.coinpayments_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)
     */
    public function getSupportedFeatures(): array
    {
        return $this->getFeatures();
    }

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'merchant_id' => [
                'type' => 'text',
                'label' => __('admin/modules.coinpayments_field_merchant_id'),
                'description' => __('admin/modules.coinpayments_field_merchant_id_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.coinpayments_field_merchant_id_placeholder'),
            ],
            'public_key' => [
                'type' => 'text',
                'label' => __('admin/modules.coinpayments_field_public_key'),
                'description' => __('admin/modules.coinpayments_field_public_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.coinpayments_field_public_key_placeholder'),
            ],
            'private_key' => [
                'type' => 'password',
                'label' => __('admin/modules.coinpayments_field_private_key'),
                'description' => __('admin/modules.coinpayments_field_private_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.coinpayments_field_private_key_placeholder'),
            ],
            'ipn_secret' => [
                'type' => 'password',
                'label' => __('admin/modules.coinpayments_field_ipn_secret'),
                'description' => __('admin/modules.coinpayments_field_ipn_secret_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => __('admin/modules.coinpayments_field_ipn_secret_placeholder'),
            ],
            'accepted_coins' => [
                'type' => 'text',
                'label' => __('admin/modules.coinpayments_field_accepted_coins'),
                'description' => __('admin/modules.coinpayments_field_accepted_coins_desc'),
                'required' => false,
                'value' => 'BTC',
                'placeholder' => 'BTC, LTC, ETH',
            ],
        ]);
    }

    /**
     * Verify IPN callback HMAC signature
     */
    protected function verifyIPN(string $hmacHeader, string $body, string $ipnSecret): bool
    {
        if ($hmacHeader === '' || $body === '' || $ipnSecret === '') {
            return false; // fail-closed
        }

        // hash_hmac('sha512', raw_body, ipn_secret) — HostBill:50, WiseCP:126. secureCompare = timing-safe + fail-closed.
        return $this->secureCompare($this->hmac('sha512', $body, $ipnSecret), $hmacHeader);
    }

    /**
     * Call CoinPayments API with HMAC authentication.
     * Official auth: version=1/key=public_key/format=json + HMAC-SHA512(post_body, private_key) in HMAC header.
     * Transport via PRC-888 postForm() (TLS verify + timeout + HTTP(S)-only guaranteed). HMAC is computed over
     * the SAME body postForm serializes (http_build_query, default enc) so the signature matches byte-for-byte.
     */
    private function callCoinPaymentsApi(array $data = [])
    {
        $publicKey = $this->getConfig('public_key', '');
        $privateKey = $this->getConfig('private_key', '');

        // Add required API fields
        $data['version'] = 1;
        $data['key'] = $publicKey;
        $data['format'] = 'json';

        // Body MUST match postForm's serialization exactly, or the HMAC won't validate server-side.
        $postBody = http_build_query($data);
        $hmac = $this->hmac('sha512', $postBody, (string) $privateKey);

        $response = $this->postForm($this->apiUrl, $data, ['HMAC: ' . $hmac]);

        return [
            'http_code' => $response['http_code'] ?? 0,
            'response' => is_array($response['response'] ?? null)
                ? $response['response']
                : json_decode((string) ($response['raw_response'] ?? ''), true),
            'raw_response' => $response['raw_response'] ?? '',
        ];
    }
}
