<?php

namespace App\Modules\Payment\Revolut;

use App\Core\Payment\AbstractPayment;

class Revolut extends AbstractPayment
{
    protected $apiUrl;

    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->apiUrl = $this->getApiUrl();
    }

    /**
     * Validate payment parameters and return ready status
     */
    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.revolut_missing_parameter', ['param' => $param]),
                ];
            }
        }

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

    /**
     * Create a payment order via Revolut Merchant API and return redirect URL
     */
    public function makePayment(array $params)
    {
        $currency = $params['currency'] ?? $this->getConfig('currency', 'GBP');

        // HostBill class.revolut.php:70 createOrder ({amount,currency,email,merchant_order_ext_ref,description})
        $orderData = [
            'amount' => (int) round($params['amount'] * 100),
            'currency' => strtoupper($currency),
            'description' => $params['description'] ?? __('admin/modules.revolut_payment_description', ['order_id' => $params['order_id']]),
            'merchant_order_ext_ref' => (string) $params['order_id'],
        ];

        if (! empty($params['email'])) {
            $orderData['email'] = $params['email'];
        }

        if (isset($params['success_url']) || isset($params['redirect_url'])) {
            $orderData['redirect_url'] = $params['success_url'] ?? $params['redirect_url'];
        }

        if (isset($params['webhook_url']) || isset($params['callback_url'])) {
            $orderData['webhook_url'] = $params['webhook_url'] ?? $params['callback_url'];
        }

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

        if (in_array($response['http_code'], [200, 201]) && isset($response['response']['checkout_url'])) {
            $order = $response['response'];

            return [
                'status' => 'redirect',
                'message' => __('admin/modules.revolut_payment_created'),
                'transaction_id' => $order['id'],
                'redirect_url' => $order['checkout_url'],
                'data' => [
                    'revolut_order_id' => $order['id'],
                    'checkout_url' => $order['checkout_url'],
                    'amount' => $params['amount'],
                    'currency' => $currency,
                ],
            ];
        }

        $errorMessage = $response['response']['message'] ?? $response['response']['error'] ?? __('admin/modules.revolut_payment_failed');

        return [
            'status' => 'error',
            'message' => $errorMessage,
            'data' => $this->sanitizeResponse($response),
        ];
    }

    /**
     * Handle payment callback - check order status via Revolut API and verify webhook signature
     */
    public function handlePaymentCallback(array $params)
    {
        $orderId = $params['id'] ?? $params['transaction_id'] ?? $params['revolut_order_id'] ?? null;

        if (! $orderId) {
            return [
                'status' => 'failed',
                'message' => __('admin/modules.revolut_missing_order_id'),
            ];
        }

        // FAIL-CLOSED webhook signature (official Revolut-Signature v1). When a
        // webhook_secret is configured AND a raw request body is delivered (genuine
        // signed POST webhook), a valid signature is REQUIRED — reject on missing/
        // invalid. The return-URL flow carries no raw body; its authenticity comes
        // from the authenticated GET /orders/{id} re-fetch below (HostBill pattern,
        // class.revolut.php:171 — real state read from Revolut's API, never the request).
        if (! empty($this->getConfig('webhook_secret')) && $this->hasRawWebhookBody($params)) {
            if (! $this->verifyWebhookSignature($params)) {
                return [
                    'status' => 'failed',
                    'message' => __('admin/modules.revolut_invalid_signature'),
                ];
            }
        }

        $response = $this->callApi($this->apiUrl . '/orders/' . $orderId, 'GET');

        if ($response['http_code'] === 200 && isset($response['response']['state'])) {
            $revolutStatus = $response['response']['state'];
            $mappedStatus = $this->mapRevolutStatus($revolutStatus);

            return [
                'status' => $mappedStatus,
                'message' => __('admin/modules.revolut_payment_status_' . $mappedStatus),
                'transaction_id' => $orderId,
                'data' => [
                    'revolut_status' => $revolutStatus,
                    'amount' => isset($response['response']['order_amount']) ? $response['response']['order_amount']['value'] / 100 : null,
                    'currency' => $response['response']['order_amount']['currency'] ?? null,
                    'merchant_order_ext_ref' => $response['response']['merchant_order_ext_ref'] ?? null,
                    'metadata' => $response['response']['metadata'] ?? [],
                ],
            ];
        }

        return [
            'status' => 'failed',
            'message' => __('admin/modules.revolut_status_check_failed'),
            'data' => $this->sanitizeResponse($response),
        ];
    }

    /**
     * Refund a payment via Revolut API
     */
    public function refundPayment(array $params)
    {
        $orderId = $params['transaction_id'] ?? $params['revolut_order_id'] ?? null;

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

        $refundData = [
            'amount' => (int) round($params['amount'] * 100),
        ];

        if (isset($params['currency'])) {
            $refundData['currency'] = strtoupper($params['currency']);
        }

        if (isset($params['description'])) {
            $refundData['description'] = $params['description'];
        }

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

        // HostBill class.revolut.php:216 treats a refund as successful when the
        // returned state is COMPLETED or AUTHORISED (refund settles async within
        // ~5 min). Accept an explicit COMPLETED/AUTHORISED state, or — when the API
        // returns 2xx without a terminal state yet — treat 2xx as accepted-pending.
        if (in_array($response['http_code'], [200, 201])) {
            $state = strtoupper($response['response']['state'] ?? '');
            $refundId = $response['response']['id'] ?? $orderId;

            // HostBill class.revolut.php:216 paritesi: başarıyı terminal state'e
            // bağla, çıplak 2xx'e değil. FAILED/CANCELLED/DECLINED → hata; refund
            // async (~5 dk) settle olduğu için PENDING/PROCESSING → kabul-bekliyor.
            if (in_array($state, ['FAILED', 'CANCELLED', 'DECLINED'], true)) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.revolut_refund_failed'),
                    'data' => ['revolut_state' => $state, 'refund_id' => $refundId],
                ];
            }

            $isTerminal = in_array($state, ['COMPLETED', 'AUTHORISED'], true);

            return [
                'status' => $isTerminal ? 'success' : 'pending',
                'message' => __($isTerminal ? 'admin/modules.revolut_refund_success' : 'admin/modules.revolut_refund_pending'),
                'refund_id' => $refundId,
                'data' => [
                    'revolut_state' => $state !== '' ? $state : null,
                    'refund_id' => $refundId,
                ],
            ];
        }

        $errorMessage = $response['response']['message'] ?? $response['response']['error'] ?? __('admin/modules.revolut_refund_failed');

        return [
            'status' => 'error',
            'message' => $errorMessage,
            'data' => $this->sanitizeResponse($response),
        ];
    }

    /**
     * Generate a payment link via Revolut API
     */
    public function generatePaymentLink(array $params)
    {
        $currency = $params['currency'] ?? $this->getConfig('currency', 'GBP');

        $orderData = [
            'amount' => (int) round($params['amount'] * 100),
            'currency' => strtoupper($currency),
            'description' => $params['description'] ?? __('admin/modules.revolut_payment_description', ['order_id' => $params['order_id']]),
            'merchant_order_ext_ref' => (string) $params['order_id'],
        ];

        if (! empty($params['email'])) {
            $orderData['email'] = $params['email'];
        }

        if (isset($params['success_url']) || isset($params['redirect_url'])) {
            $orderData['redirect_url'] = $params['success_url'] ?? $params['redirect_url'];
        }

        if (isset($params['webhook_url'])) {
            $orderData['webhook_url'] = $params['webhook_url'];
        }

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

        if (in_array($response['http_code'], [200, 201]) && isset($response['response']['checkout_url'])) {
            return [
                'status' => 'success',
                'payment_url' => $response['response']['checkout_url'],
                'transaction_id' => $response['response']['id'],
            ];
        }

        $errorMessage = $response['response']['message'] ?? $response['response']['error'] ?? __('admin/modules.revolut_payment_link_failed');

        return [
            'status' => 'error',
            'message' => $errorMessage,
            'data' => $this->sanitizeResponse($response),
        ];
    }

    /**
     * Test API connection by fetching orders with limit=1
     */
    public function testConnection(array $params = [])
    {
        $response = $this->callApi($this->apiUrl . '/orders?limit=1', 'GET');

        if ($response['http_code'] === 200) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.revolut_connection_success'),
                'data' => [
                    'connected' => true,
                ],
            ];
        }

        $errorMessage = $response['response']['message'] ?? $response['response']['error'] ?? __('admin/modules.revolut_connection_failed');

        return [
            'status' => 'error',
            'message' => $errorMessage,
            'data' => $this->sanitizeResponse($response),
        ];
    }

    /**
     * Validate module configuration
     */
    public function validateConfig()
    {
        if (empty($this->config['api_key'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.revolut_api_key_required'),
            ];
        }

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

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

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'api_key' => [
                'type' => 'password',
                'label' => __('admin/modules.revolut_api_key'),
                'description' => __('admin/modules.revolut_api_key_description'),
                'required' => true,
                'value' => '',
                'placeholder' => 'sk_live_...',
            ],
            'webhook_secret' => [
                'type' => 'password',
                'label' => __('admin/modules.revolut_webhook_secret'),
                'description' => __('admin/modules.revolut_webhook_secret_description'),
                'required' => false,
                'value' => '',
                'placeholder' => 'whsec_...',
            ],
        ]);
    }

    /**
     * Override default headers for Bearer token authentication
     */
    protected function getDefaultHeaders()
    {
        return [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: ProCP/1.0 Payment Gateway Module',
            'Authorization: Bearer ' . $this->getConfig('api_key', ''),
        ];
    }

    /**
     * Get the appropriate API URL based on test mode
     */
    protected function getApiUrl(): string
    {
        if ($this->isTestMode()) {
            return 'https://sandbox-merchant.revolut.com/api/1.0';
        }

        return 'https://merchant.revolut.com/api/1.0';
    }

    /**
     * Is a raw signed-webhook body present on the callback params?
     *
     * The return-URL flow (query/form bag) carries no raw body — only a genuine
     * signed POST webhook does. Signature enforcement applies only in that case.
     */
    private function hasRawWebhookBody(array $params): bool
    {
        return ! empty($params['raw_body']) || ! empty($params['revolut_raw_body']);
    }

    /**
     * Verify Revolut webhook signature — FAIL-CLOSED (official Revolut-Signature v1).
     *
     * Spec: payload = 'v1.' + Revolut-Timestamp + '.' + rawBody; header value
     * 'v1=' + HMAC-SHA256(payload, signingSecret) (hex). Header may carry multiple
     * space-separated values (rotation) — any match passes. Missing secret/signature/
     * timestamp/body → reject (secureCompare returns false on null/empty).
     */
    private function verifyWebhookSignature(array $params): bool
    {
        $webhookSecret = (string) $this->getConfig('webhook_secret', '');
        $rawBody = (string) ($params['raw_body'] ?? $params['revolut_raw_body'] ?? '');
        $timestamp = (string) ($params['revolut_timestamp'] ?? $params['Revolut-Timestamp'] ?? '');
        $header = (string) ($params['revolut_signature'] ?? $params['Revolut-Signature'] ?? '');

        if ($webhookSecret === '' || $rawBody === '' || $timestamp === '' || $header === '') {
            return false;
        }

        $expected = 'v1=' . $this->hmac('sha256', 'v1.' . $timestamp . '.' . $rawBody, $webhookSecret);

        foreach (preg_split('/\s+/', trim($header)) as $candidate) {
            if ($this->secureCompare($expected, $candidate)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Strip raw_response before returning transport data to callers (PCI defense-in-depth).
     */
    private function sanitizeResponse(array $response): array
    {
        unset($response['raw_response']);

        return $response;
    }

    /**
     * Map Revolut order status to internal status
     *
     * Revolut states: PENDING, PROCESSING, AUTHORISED, COMPLETED, CANCELLED, FAILED
     * Internal statuses: success, failed, pending
     * AUTHORISED is treated as success per HostBill class.revolut.php:179.
     */
    private function mapRevolutStatus(string $revolutStatus): string
    {
        return match (strtoupper($revolutStatus)) {
            'COMPLETED', 'AUTHORISED' => 'success',
            'PENDING', 'PROCESSING' => 'pending',
            'CANCELLED', 'FAILED', 'DECLINED' => 'failed',
            default => 'failed',
        };
    }

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