<?php

namespace App\Modules\Payment\PagSeguro;

use App\Core\Payment\AbstractPayment;

class PagSeguro extends AbstractPayment
{
    protected $apiUrlTest = 'https://ws.sandbox.pagseguro.uol.com.br/v2';

    protected $apiUrlLive = 'https://ws.pagseguro.uol.com.br/v2';

    protected $redirectUrlTest = 'https://sandbox.pagseguro.uol.com.br/v2/checkout/payment.html';

    protected $redirectUrlLive = 'https://pagseguro.uol.com.br/v2/checkout/payment.html';

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

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

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

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

    /**
     * Create checkout via PagSeguro API and return redirect URL
     *
     * POST /checkout?email={email}&token={token} with XML body
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $email = $this->getConfig('email');
        $token = $this->getConfig('token');

        if (empty($email) || empty($token)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.pagseguro_missing_credentials'),
            ];
        }

        $xmlBody = $this->buildCheckoutXml($params);

        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/checkout?email=' . urlencode($email) . '&token=' . urlencode($token);

        $response = $this->callPagSeguroApi($endpoint, 'POST', $xmlBody);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => $response['error'] ?? __('admin/modules.pagseguro_checkout_failed'),
            ];
        }

        $checkoutCode = $this->parseXmlResponse($response['raw_response'], 'code');

        if (empty($checkoutCode)) {
            $errorMessage = $this->parseXmlResponse($response['raw_response'], 'message');

            return [
                'status' => 'error',
                'message' => $errorMessage ?? __('admin/modules.pagseguro_checkout_failed'),
            ];
        }

        $redirectUrl = $this->getRedirectUrl() . '?code=' . $checkoutCode;

        return [
            'status' => 'redirect',
            'message' => __('admin/modules.pagseguro_redirect'),
            'redirect_url' => $redirectUrl,
            'data' => [
                'checkout_code' => $checkoutCode,
            ],
        ];
    }

    /**
     * Handle PagSeguro notification callback
     *
     * Receives notification code, queries transaction details via API.
     * PagSeguro status codes:
     *   1 = Aguardando pagamento (Waiting)
     *   2 = Em analise (Under review)
     *   3 = Paga (Paid - success)
     *   4 = Disponivel (Available)
     *   5 = Em disputa (In dispute)
     *   6 = Devolvida (Refunded)
     *   7 = Cancelada (Cancelled)
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        $notificationCode = $params['notificationCode'] ?? null;

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

        $email = $this->getConfig('email');
        $token = $this->getConfig('token');

        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/transactions/notifications/' . urlencode($notificationCode)
            . '?email=' . urlencode($email) . '&token=' . urlencode($token);

        $response = $this->callPagSeguroApi($endpoint, 'GET');

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => $response['error'] ?? __('admin/modules.pagseguro_notification_failed'),
            ];
        }

        $rawResponse = $response['raw_response'];
        $transactionCode = $this->parseXmlResponse($rawResponse, 'code');
        $statusCode = $this->parseXmlResponse($rawResponse, 'status');
        $grossAmount = $this->parseXmlResponse($rawResponse, 'grossAmount');
        $reference = $this->parseXmlResponse($rawResponse, 'reference');

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

        $mappedStatus = $this->mapPagSeguroStatus((int) $statusCode);

        // Ödeme kararı POST alanlarından DEĞİL, email+token authenticated lookup'tan
        // gelen status=3'ten belirleniyor (fail-closed authenticity, WHMCS NPI v2 eşdeğeri).
        // raw_response envelope'a taşınmaz — müşteri PII (sender ad/email/tel) sızmasın.
        return [
            'status' => $mappedStatus['status'],
            'message' => $mappedStatus['message'],
            'transaction_id' => $transactionCode ?? '',
            'amount' => $grossAmount ?? '',
            'order_id' => $reference ?? '',
            'data' => [
                'notification_code' => $notificationCode,
                'transaction_code' => $transactionCode,
                'status_code' => $statusCode,
                'gross_amount' => $grossAmount,
                'reference' => $reference,
            ],
        ];
    }

    /**
     * Refund a PagSeguro transaction
     *
     * POST /transactions/refunds?email={email}&token={token}
     * with transactionCode and refundValue
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        $transactionCode = $params['transaction_id'] ?? $params['transactionCode'] ?? null;

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

        $email = $this->getConfig('email');
        $token = $this->getConfig('token');

        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/transactions/refunds?email=' . urlencode($email) . '&token=' . urlencode($token);

        $postData = [
            'transactionCode' => $transactionCode,
        ];

        if (! empty($params['amount'])) {
            $postData['refundValue'] = number_format($params['amount'], 2, '.', '');
        }

        $response = $this->callPagSeguroApi($endpoint, 'POST', null, $postData);

        if (! $response['success'] || $response['http_code'] !== 200) {
            $errorMessage = $this->parseXmlResponse($response['raw_response'] ?? '', 'message');

            return [
                'status' => 'error',
                'message' => $errorMessage ?? __('admin/modules.pagseguro_refund_failed'),
            ];
        }

        return [
            'status' => 'success',
            'message' => __('admin/modules.pagseguro_refund_success'),
            'refund_id' => $transactionCode,
        ];
    }

    /**
     * Generate a PagSeguro payment link by creating a checkout session
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        $email = $this->getConfig('email');
        $token = $this->getConfig('token');

        if (empty($email) || empty($token)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.pagseguro_missing_credentials'),
            ];
        }

        $xmlBody = $this->buildCheckoutXml($params);

        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/checkout?email=' . urlencode($email) . '&token=' . urlencode($token);

        $response = $this->callPagSeguroApi($endpoint, 'POST', $xmlBody);

        if (! $response['success']) {
            return [
                'status' => 'error',
                'message' => $response['error'] ?? __('admin/modules.pagseguro_checkout_failed'),
            ];
        }

        $checkoutCode = $this->parseXmlResponse($response['raw_response'], 'code');

        if (empty($checkoutCode)) {
            $errorMessage = $this->parseXmlResponse($response['raw_response'], 'message');

            return [
                'status' => 'error',
                'message' => $errorMessage ?? __('admin/modules.pagseguro_checkout_failed'),
            ];
        }

        $paymentUrl = $this->getRedirectUrl() . '?code=' . $checkoutCode;

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

    /**
     * Test connection to PagSeguro API
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $email = $this->getConfig('email');
        $token = $this->getConfig('token');

        if (empty($email) || empty($token)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.pagseguro_missing_credentials'),
            ];
        }

        // Test by querying transactions endpoint with a date range
        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/transactions?email=' . urlencode($email)
            . '&token=' . urlencode($token)
            . '&initialDate=' . urlencode(date('Y-m-d\TH:i', strtotime('-1 hour')))
            . '&finalDate=' . urlencode(date('Y-m-d\TH:i'))
            . '&maxPageResults=1';

        $response = $this->callPagSeguroApi($endpoint, 'GET');

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

        $errorMessage = $this->parseXmlResponse($response['raw_response'] ?? '', 'message');

        return [
            'status' => 'error',
            'message' => $errorMessage ?? __('admin/modules.pagseguro_connection_failed'),
        ];
    }

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

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

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

        // Validate email format
        if (! filter_var($this->getConfig('email'), FILTER_VALIDATE_EMAIL)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.pagseguro_invalid_email'),
            ];
        }

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

    /**
     * PagSeguro 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(), [
            'email' => [
                'type' => 'text',
                'label' => __('admin/modules.pagseguro_email'),
                'description' => __('admin/modules.pagseguro_email_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => 'seller@example.com',
            ],
            'token' => [
                'type' => 'password',
                'label' => __('admin/modules.pagseguro_token'),
                'description' => __('admin/modules.pagseguro_token_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/modules.pagseguro_currency'),
                'description' => __('admin/modules.pagseguro_currency_desc'),
                'required' => true,
                'value' => 'BRL',
                'options' => [
                    'BRL' => 'BRL - Brazilian Real',
                ],
            ],
            'notification_url' => [
                'type' => 'text',
                'label' => __('admin/modules.pagseguro_notification_url'),
                'description' => __('admin/modules.pagseguro_notification_url_desc'),
                'required' => false,
                'value' => '',
                'placeholder' => 'https://yourdomain.com/payment/pagseguro/callback',
            ],
        ]);
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    /**
     * Build XML body for PagSeguro checkout request
     */
    private function buildCheckoutXml(array $params): string
    {
        $amount = number_format((float) ($params['amount'] ?? 0), 2, '.', '');
        $orderId = htmlspecialchars($params['order_id'] ?? '', ENT_XML1, 'UTF-8');
        $description = htmlspecialchars(
            $params['description'] ?? __('admin/modules.pagseguro_payment_description'),
            ENT_XML1,
            'UTF-8'
        );

        $senderEmail = htmlspecialchars($params['customer_email'] ?? '', ENT_XML1, 'UTF-8');
        $senderName = htmlspecialchars($params['customer_name'] ?? '', ENT_XML1, 'UTF-8');
        $redirectUrl = htmlspecialchars($params['success_url'] ?? '', ENT_XML1, 'UTF-8');
        $notificationUrl = htmlspecialchars(
            $params['callback_url'] ?? $this->getConfig('notification_url', ''),
            ENT_XML1,
            'UTF-8'
        );

        $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
        $xml .= '<checkout>';
        $xml .= '<currency>BRL</currency>';
        $xml .= '<reference>' . $orderId . '</reference>';
        $xml .= '<items>';
        $xml .= '<item>';
        $xml .= '<id>' . $orderId . '</id>';
        $xml .= '<description>' . $description . '</description>';
        $xml .= '<amount>' . $amount . '</amount>';
        $xml .= '<quantity>1</quantity>';
        $xml .= '</item>';
        $xml .= '</items>';

        if (! empty($senderEmail) || ! empty($senderName)) {
            $xml .= '<sender>';
            if (! empty($senderName)) {
                $xml .= '<name>' . $senderName . '</name>';
            }
            if (! empty($senderEmail)) {
                $xml .= '<email>' . $senderEmail . '</email>';
            }
            $xml .= '</sender>';
        }

        if (! empty($redirectUrl)) {
            $xml .= '<redirectURL>' . $redirectUrl . '</redirectURL>';
        }

        if (! empty($notificationUrl)) {
            $xml .= '<notificationURL>' . $notificationUrl . '</notificationURL>';
        }

        $xml .= '</checkout>';

        return $xml;
    }

    /**
     * Parse a value from PagSeguro XML response
     */
    private function parseXmlResponse(string $response, string $key): ?string
    {
        if (empty($response)) {
            return null;
        }

        $previousValue = libxml_use_internal_errors(true);
        $xml = simplexml_load_string($response);
        libxml_use_internal_errors($previousValue);

        if ($xml !== false && isset($xml->{$key})) {
            return trim((string) $xml->{$key});
        }

        // Fallback: regex for nested or simple elements
        if (preg_match('/<' . preg_quote($key, '/') . '>(.*?)<\/' . preg_quote($key, '/') . '>/s', $response, $matches)) {
            return trim($matches[1]);
        }

        return null;
    }

    /**
     * Map PagSeguro status code to standard payment status
     *
     * 1 = Aguardando pagamento (Waiting for payment)
     * 2 = Em analise (Under analysis)
     * 3 = Paga (Paid - success)
     * 4 = Disponivel (Available - funds cleared)
     * 5 = Em disputa (In dispute)
     * 6 = Devolvida (Refunded)
     * 7 = Cancelada (Cancelled)
     */
    private function mapPagSeguroStatus(int $statusCode): array
    {
        return match ($statusCode) {
            1 => [
                'status' => 'pending',
                'message' => __('admin/modules.pagseguro_status_waiting'),
            ],
            2 => [
                'status' => 'pending',
                'message' => __('admin/modules.pagseguro_status_under_review'),
            ],
            3 => [
                'status' => 'success',
                'message' => __('admin/modules.pagseguro_status_paid'),
            ],
            4 => [
                'status' => 'success',
                'message' => __('admin/modules.pagseguro_status_available'),
            ],
            5 => [
                'status' => 'dispute',
                'message' => __('admin/modules.pagseguro_status_dispute'),
            ],
            6 => [
                'status' => 'refunded',
                'message' => __('admin/modules.pagseguro_status_refunded'),
            ],
            7 => [
                'status' => 'cancelled',
                'message' => __('admin/modules.pagseguro_status_cancelled'),
            ],
            default => [
                'status' => 'error',
                'message' => __('admin/modules.pagseguro_status_unknown'),
            ],
        };
    }

    /**
     * Get the API base URL based on test/live mode
     */
    private function getApiUrl(): string
    {
        return $this->isTestMode ? $this->apiUrlTest : $this->apiUrlLive;
    }

    /**
     * Get the redirect URL based on test/live mode
     */
    private function getRedirectUrl(): string
    {
        return $this->isTestMode ? $this->redirectUrlTest : $this->redirectUrlLive;
    }

    /**
     * Call PagSeguro API endpoint
     *
     * POST paths route through PRC-888 postXml()/postForm() (TLS verify default-on,
     * connect_timeout, SSRF protocol allow-list). GET path (notification/transaction
     * lookup + testConnection) uses a locally-hardened curl mirroring those options.
     * PagSeguro v2 uses query-param auth (email+token), not headers.
     */
    private function callPagSeguroApi(string $url, string $method = 'POST', ?string $xmlBody = null, ?array $postData = null): array
    {
        $accept = ['Accept: application/xml'];

        if ($method === 'POST') {
            if ($xmlBody !== null) {
                // application/xml (PagSeguro v2 checkout); postXml() emits text/xml,
                // override Content-Type to match PagSeguro's documented header.
                $result = $this->postXml($url, $xmlBody, array_merge($accept, [
                    'Content-Type: application/xml; charset=UTF-8',
                ]));
            } else {
                $result = $this->postForm($url, $postData ?? [], $accept);
            }

            return [
                'success' => $result['error'] === null,
                'http_code' => $result['http_code'],
                'error' => $result['error'],
                'raw_response' => $result['raw_response'],
            ];
        }

        // GET — hardened locally (postForm/postXml are POST-only; no GET helper in foundation).
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL             => $url,
            CURLOPT_RETURNTRANSFER  => true,
            CURLOPT_HTTPGET         => true,
            CURLOPT_HTTPHEADER      => $accept,
            CURLOPT_SSL_VERIFYPEER  => true,
            CURLOPT_SSL_VERIFYHOST  => 2,
            CURLOPT_TIMEOUT         => 30,
            CURLOPT_CONNECTTIMEOUT  => 10,
            CURLOPT_PROTOCOLS       => CURLPROTO_HTTPS | CURLPROTO_HTTP,
            CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP,
        ]);

        $response = curl_exec($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        if ($curlError !== '') {
            return [
                'success' => false,
                'http_code' => $httpCode,
                'error' => $curlError,
                'raw_response' => $response === false ? '' : (string) $response,
            ];
        }

        return [
            'success' => true,
            'http_code' => $httpCode,
            'raw_response' => $response === false ? '' : (string) $response,
        ];
    }

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