<?php

namespace App\Modules\Payment\MoipApi;

use App\Core\Payment\AbstractPayment;

class MoipApi extends AbstractPayment
{
    protected $apiUrlTest = 'https://sandbox.moip.com.br/v2';

    protected $apiUrlLive = 'https://api.moip.com.br/v2';

    // Parcelamento: banka/emissor taksit farkını uygular (callback/moipapi.php:112 "em N parcelas");
    // procp surcharge=0 (çifte-tahsilat önlenir) — Iyzico.php:14 deseni.
    protected string $installmentSurchargeMode = 'gateway';

    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.moipapi_missing_param', ['param' => $param]),
                ];
            }
        }

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

    /**
     * Create an order + payment via Moip v2 REST API.
     *
     * Moip v2 flow (dev.moip.com.br/reference): POST /orders (ownId, amount.currency,
     * items[].price cents) → POST /orders/{id}/payments (fundingInstrument.creditCard.hash).
     * v2 /orders yanıtı hosted-checkout URL DÖNDÜRMEZ (legacy 1.0 Instrucao.do'da vardı);
     * bu yüzden checkout-link dalı YOK — card_hash tabanlı doğrudan ödeme veya
     * boleto/online-debit redirectHref (payment yanıtında) tek meşru akışlardır.
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $apiToken = $this->getConfig('api_token');
        $apiKey = $this->getConfig('api_key');

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

        // Step 1: Create order
        $orderResult = $this->createOrder($params);

        if ($orderResult['status'] === 'error') {
            return $orderResult;
        }

        $moipOrderId = $orderResult['moip_order_id'];

        // Step 2: Create payment on the order
        $paymentResult = $this->createPayment($moipOrderId, $params);

        if ($paymentResult['status'] === 'error') {
            return $paymentResult;
        }

        // Boleto / online-debit: gateway payment yanıtında redirectHref döner.
        if (! empty($paymentResult['redirect_url'])) {
            return [
                'status' => 'redirect',
                'message' => __('admin/modules.moipapi_redirect'),
                'redirect_url' => $paymentResult['redirect_url'],
                'transaction_id' => $paymentResult['payment_id'],
                'data' => [
                    'moip_order_id' => $moipOrderId,
                    'moip_payment_id' => $paymentResult['payment_id'],
                ],
            ];
        }

        // Direct payment (credit card via card_hash)
        $mappedStatus = $this->mapMoipStatus($paymentResult['moip_status']);

        return [
            'status' => $mappedStatus['status'],
            'message' => $mappedStatus['message'],
            'transaction_id' => $paymentResult['payment_id'],
            'data' => [
                'moip_order_id' => $moipOrderId,
                'moip_payment_id' => $paymentResult['payment_id'],
                'moip_status' => $paymentResult['moip_status'],
            ],
        ];
    }

    /**
     * Handle Moip webhook callback — FAIL-CLOSED (PRC-888).
     *
     * GÜVENLİK: webhook body'sindeki status'a ASLA güvenilmez. İki kapı:
     *  (1) İsteğe bağlı webhook_token config'i doldurulmuşsa, body'deki token ile
     *      secureCompare (hash_equals; eksik/yanlış → reddet).
     *  (2) DAİMA GET /payments/{id} ile durum KAYNAKTAN yeniden çekilir; başarı yalnız
     *      Moip'in kendi API'sinin döndürdüğü status'a göre kararlaştırılır.
     * paymentId çözülemezse veya re-fetch başarısızsa ödeme BAŞARILI SAYILMAZ.
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        $apiToken = $this->getConfig('api_token');
        $apiKey = $this->getConfig('api_key');

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

        $event = $params['event'] ?? null;
        $resource = $params['resource'] ?? [];

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

        // (1) İsteğe bağlı paylaşılan sır: yalnız config doluysa fail-closed doğrula.
        $expectedToken = (string) $this->getConfig('webhook_token', '');
        if ($expectedToken !== '') {
            $incomingToken = (string) ($params['token'] ?? $params['authentication'] ?? '');
            if (! $this->secureCompare($expectedToken, $incomingToken)) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.moipapi_webhook_token_invalid'),
                ];
            }
        }

        $paymentData = $resource['payment'] ?? $resource;
        $paymentId = $paymentData['id'] ?? null;

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

        // (2) Durumu KAYNAKTAN doğrula — body status'una güvenme. GET /payments/{id}.
        $apiUrl = $this->getApiUrl();
        $response = $this->callApi($apiUrl . '/payments/' . urlencode((string) $paymentId), 'GET');

        if ($response['http_code'] !== 200 || empty($response['response']['id'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.moipapi_callback_verify_failed'),
            ];
        }

        $verified = $response['response'];
        $moipStatus = $verified['status'] ?? null;
        $amountCents = $verified['amount']['total'] ?? null;

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

        $mappedStatus = $this->mapMoipStatus($moipStatus);

        return [
            'status' => $mappedStatus['status'],
            'message' => $mappedStatus['message'],
            'transaction_id' => $paymentId,
            'amount' => $amountCents !== null ? ($amountCents / 100) : null,
            'data' => [
                'event' => $event,
                'moip_payment_id' => $paymentId,
                'moip_status' => $moipStatus,
                'amount_cents' => $amountCents,
            ],
        ];
    }

    /**
     * Refund a payment via Moip v2 API.
     *
     * POST /payments/{id}/refunds — amount cents (partial), optional refundingInstrument.
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        $paymentId = $params['transaction_id'] ?? $params['moip_payment_id'] ?? null;

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

        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/payments/' . urlencode($paymentId) . '/refunds';

        $refundData = [];

        // Partial refund (minor unit: cents)
        if (! empty($params['amount'])) {
            $refundData['amount'] = (int) round((float) $params['amount'] * 100);
        }

        // Refunding instrument (bank account for refund)
        if (! empty($params['refunding_instrument'])) {
            $refundData['refundingInstrument'] = $params['refunding_instrument'];
        }

        $response = $this->postJson($endpoint, ($refundData === [] ? null : $refundData), $this->authHeaders(), ['verify' => true]);
        $decoded = json_decode($response['raw_response'] ?? '', true);

        if (in_array($response['http_code'], [200, 201], true)) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.moipapi_refund_success'),
                'refund_id' => $decoded['id'] ?? null,
                'data' => [
                    'refund_id' => $decoded['id'] ?? null,
                    'moip_status' => $decoded['status'] ?? null,
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->extractErrorMessage($decoded) ?? __('admin/modules.moipapi_refund_failed'),
        ];
    }

    /**
     * Payment-link üretimi Moip v2 /orders akışında DESTEKLENMEZ.
     *
     * Legacy Moip 1.0 hosted-checkout (Instrucao.do redirect token) v2'de yoktur;
     * v2 /orders yanıtı payment-link DÖNDÜRMEZ. Sahte/boş link yerine net "unsupported".
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('modules.payment_function_not_supported'),
        ];
    }

    /**
     * Test connection to Moip API by querying accounts endpoint.
     *
     * GET /accounts, Basic auth base64(api_token:api_key).
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $apiToken = $this->getConfig('api_token');
        $apiKey = $this->getConfig('api_key');

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

        $apiUrl = $this->getApiUrl();
        $response = $this->callApi($apiUrl . '/accounts', 'GET');
        $decoded = $response['response'] ?? null;

        if ($response['http_code'] === 200 && ! empty($decoded)) {
            return [
                'status' => 'success',
                'message' => __('admin/modules.moipapi_connection_success'),
                'data' => [
                    'account_id' => $decoded['id'] ?? null,
                ],
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->extractErrorMessage(is_array($decoded) ? $decoded : null) ?? __('admin/modules.moipapi_connection_failed'),
        ];
    }

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

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

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

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

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

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'api_token' => [
                'type' => 'text',
                'label' => __('admin/modules.moipapi_api_token'),
                'description' => __('admin/modules.moipapi_api_token_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            'api_key' => [
                'type' => 'password',
                'label' => __('admin/modules.moipapi_api_key'),
                'description' => __('admin/modules.moipapi_api_key_desc'),
                'required' => true,
                'value' => '',
                'placeholder' => '',
            ],
            'webhook_token' => [
                'type' => 'password',
                'label' => __('admin/modules.moipapi_webhook_token'),
                'description' => __('admin/modules.moipapi_webhook_token_desc'),
                'required' => false,
                'value' => '',
                'placeholder' => '',
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/modules.moipapi_currency'),
                'description' => __('admin/modules.moipapi_currency_desc'),
                'required' => true,
                'value' => 'BRL',
                'options' => [
                    'BRL' => 'BRL - Brazilian Real',
                ],
            ],
            'notification_url' => [
                'type' => 'text',
                'label' => __('admin/modules.moipapi_notification_url'),
                'description' => __('admin/modules.moipapi_notification_url_desc'),
                'required' => false,
                'value' => '',
                'placeholder' => 'https://yourdomain.com/payment/moipapi/callback',
            ],
        ], $this->installmentConfigFields());
    }

    /**
     * Override default headers for Basic Auth (token:key base64 encoded)
     */
    protected function getDefaultHeaders()
    {
        return $this->authHeaders();
    }

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

    /** Basic auth token — base64(api_token:api_key). Loglanmaz. */
    private function basicAuth(): string
    {
        return base64_encode($this->getConfig('api_token', '') . ':' . $this->getConfig('api_key', ''));
    }

    /**
     * Basic-auth + JSON header'ları (postJson transport TLS-verify + timeout için).
     */
    private function authHeaders(): array
    {
        return [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: ProCP/1.0 Payment Gateway Module',
            'Authorization: Basic ' . $this->basicAuth(),
        ];
    }

    /**
     * Create an order on Moip API.
     *
     * POST /orders with ownId, amount.currency (order total derives from items),
     * items[].price cents, optional customer.
     */
    private function createOrder(array $params): array
    {
        $apiUrl = $this->getApiUrl();

        $amountCents = (int) round((float) ($params['amount'] ?? 0) * 100);
        $orderId = $params['order_id'] ?? uniqid('order_');
        $description = $params['description'] ?? __('admin/modules.moipapi_payment_description');

        $orderData = [
            'ownId' => $orderId,
            'amount' => [
                'currency' => 'BRL',
            ],
            'items' => [
                [
                    'product' => $description,
                    'quantity' => 1,
                    'detail' => $description,
                    'price' => $amountCents,
                ],
            ],
        ];

        // Customer data
        $customer = [];
        if (! empty($params['customer_name'])) {
            $customer['fullname'] = $params['customer_name'];
        }
        if (! empty($params['customer_email'])) {
            $customer['email'] = $params['customer_email'];
        }
        if (! empty($params['customer_id'])) {
            $customer['ownId'] = $params['customer_id'];
        }
        if (! empty($params['customer_tax_document'])) {
            $customer['taxDocument'] = [
                'type' => 'CPF',
                'number' => $params['customer_tax_document'],
            ];
        }
        if (! empty($params['customer_phone'])) {
            $customer['phone'] = [
                'countryCode' => '55',
                'areaCode' => substr($params['customer_phone'], 0, 2),
                'number' => substr($params['customer_phone'], 2),
            ];
        }

        if (! empty($customer)) {
            if (empty($customer['ownId'])) {
                $customer['ownId'] = $orderId . '_customer';
            }
            $orderData['customer'] = $customer;
        }

        $response = $this->postJson($apiUrl . '/orders', $orderData, $this->authHeaders(), ['verify' => true]);
        $decoded = json_decode($response['raw_response'] ?? '', true);

        if (in_array($response['http_code'], [200, 201], true) && ! empty($decoded['id'])) {
            return [
                'status' => 'success',
                'moip_order_id' => $decoded['id'],
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->extractErrorMessage($decoded) ?? __('admin/modules.moipapi_order_creation_failed'),
        ];
    }

    /**
     * Create a payment on an existing Moip order.
     *
     * POST /orders/{id}/payments with fundingInstrument.
     * Kart yolu YALNIZ card_hash (Moip v2 client-side tokenization) — ham PAN/CVV
     * sunucuya girmez (PCI-DSS).
     */
    private function createPayment(string $moipOrderId, array $params): array
    {
        $apiUrl = $this->getApiUrl();
        $endpoint = $apiUrl . '/orders/' . urlencode($moipOrderId) . '/payments';

        $paymentData = [];

        $paymentMethod = $params['payment_method'] ?? 'credit_card';

        if ($paymentMethod === 'boleto') {
            $paymentData['fundingInstrument'] = [
                'method' => 'BOLETO',
                'boleto' => [
                    'expirationDate' => $params['boleto_expiration'] ?? date('Y-m-d', strtotime('+3 days')),
                    'instructionLines' => [
                        'first' => $params['boleto_instruction_1'] ?? __('admin/modules.moipapi_boleto_instruction_1'),
                        'second' => $params['boleto_instruction_2'] ?? __('admin/modules.moipapi_boleto_instruction_2'),
                        'third' => $params['boleto_instruction_3'] ?? __('admin/modules.moipapi_boleto_instruction_3'),
                    ],
                ],
            ];
        } elseif ($paymentMethod === 'online_debit') {
            $paymentData['fundingInstrument'] = [
                'method' => 'ONLINE_BANK_DEBIT',
                'onlineBankDebit' => [
                    'bankNumber' => $params['bank_number'] ?? '341',
                    'expirationDate' => $params['debit_expiration'] ?? date('Y-m-d', strtotime('+1 day')),
                ],
            ];
        } else {
            // Credit card — card_hash ONLY (PCI: ham PAN/CVV kabul edilmez).
            if (empty($params['card_hash'])) {
                return [
                    'status' => 'error',
                    'message' => __('admin/modules.moipapi_card_hash_required'),
                ];
            }

            $paymentData['fundingInstrument'] = [
                'method' => 'CREDIT_CARD',
                'creditCard' => [
                    'hash' => $params['card_hash'],
                    'holder' => [
                        'fullname' => $params['card_holder'] ?? $params['customer_name'] ?? '',
                        'birthdate' => $params['card_holder_birthdate'] ?? '',
                        'taxDocument' => [
                            'type' => 'CPF',
                            'number' => $params['card_holder_cpf'] ?? $params['customer_tax_document'] ?? '',
                        ],
                    ],
                ],
            ];
        }

        // Installment count
        if (! empty($params['installment_count']) && (int) $params['installment_count'] > 1) {
            $paymentData['installmentCount'] = (int) $params['installment_count'];
        }

        $response = $this->postJson($endpoint, $paymentData, $this->authHeaders(), ['verify' => true]);
        $decoded = json_decode($response['raw_response'] ?? '', true);

        if (in_array($response['http_code'], [200, 201], true) && ! empty($decoded['id'])) {
            $paymentId = $decoded['id'];
            $moipStatus = $decoded['status'] ?? 'CREATED';

            // Boleto / online-debit redirectHref (whitelisted).
            $redirectUrl = null;
            $links = $decoded['_links'] ?? [];
            if (isset($links['payBoleto']['redirectHref'])) {
                $redirectUrl = $links['payBoleto']['redirectHref'];
            } elseif (isset($links['payOnlineBankDebitItau']['redirectHref'])) {
                $redirectUrl = $links['payOnlineBankDebitItau']['redirectHref'];
            }

            return [
                'status' => 'success',
                'payment_id' => $paymentId,
                'moip_status' => $moipStatus,
                'redirect_url' => $redirectUrl,
            ];
        }

        return [
            'status' => 'error',
            'message' => $this->extractErrorMessage($decoded) ?? __('admin/modules.moipapi_payment_creation_failed'),
        ];
    }

    /**
     * Map Moip payment status to internal status
     *
     * Moip statuses: CREATED, WAITING, IN_ANALYSIS, PRE_AUTHORIZED, AUTHORIZED,
     *                CANCELLED, REFUNDED, REVERSED, SETTLED
     */
    private function mapMoipStatus(string $moipStatus): array
    {
        return match (strtoupper($moipStatus)) {
            'AUTHORIZED', 'SETTLED' => [
                'status' => 'success',
                'message' => __('admin/modules.moipapi_status_authorized'),
            ],
            'PRE_AUTHORIZED' => [
                'status' => 'success',
                'message' => __('admin/modules.moipapi_status_pre_authorized'),
            ],
            'CREATED', 'WAITING', 'IN_ANALYSIS' => [
                'status' => 'pending',
                'message' => __('admin/modules.moipapi_status_pending'),
            ],
            'CANCELLED' => [
                'status' => 'cancelled',
                'message' => __('admin/modules.moipapi_status_cancelled'),
            ],
            'REFUNDED', 'REVERSED' => [
                'status' => 'refunded',
                'message' => __('admin/modules.moipapi_status_refunded'),
            ],
            default => [
                'status' => 'error',
                'message' => __('admin/modules.moipapi_status_unknown'),
            ],
        };
    }

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

    /**
     * Extract error message from a decoded Moip API response body.
     */
    private function extractErrorMessage(?array $data): ?string
    {
        if (empty($data)) {
            return null;
        }

        // Moip returns errors array
        if (isset($data['errors']) && is_array($data['errors'])) {
            $messages = [];
            foreach ($data['errors'] as $error) {
                $messages[] = $error['description'] ?? $error['message'] ?? '';
            }

            return implode('; ', array_filter($messages));
        }

        if (isset($data['ERROR'])) {
            return $data['ERROR'];
        }

        if (isset($data['message'])) {
            return $data['message'];
        }

        return null;
    }

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