<?php

namespace App\Modules\Payment\OfflineCc;

use App\Core\Payment\AbstractPayment;

class OfflineCc extends AbstractPayment
{
    /**
     * Validate card parameters and perform pre-fraud check before payment
     *
     * @return array
     */
    public function attemptPayment(array $params)
    {
        $requiredParams = ['amount', 'currency', 'order_id', 'card_number', 'exp_month', 'exp_year'];

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

        // Check CVV if required
        if ($this->getConfig('require_cvv', true) && (! isset($params['cvv']) || empty($params['cvv']))) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_cvv_required'),
            ];
        }

        // Luhn check on card number
        $cardNumber = preg_replace('/[\s\-]/', '', $params['card_number']);
        if (! $this->luhnCheck($cardNumber)) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_invalid_card_number'),
            ];
        }

        // Check card expiry
        if ($this->isCardExpired((int) $params['exp_month'], (int) $params['exp_year'])) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_card_expired'),
            ];
        }

        // Fraud check
        $fraudCheck = $this->performPreFraudCheck($params);
        if ($fraudCheck['status'] === 'error') {
            return $fraudCheck;
        }

        return [
            'status' => 'pending',
            'stage' => 'ready',
            'message' => __('admin/modules.offlinecc_ready'),
            'data' => [
                'payment_method' => 'offlinecc',
                'amount' => $params['amount'],
                'currency' => $params['currency'],
                'order_id' => $params['order_id'],
                'fraud_check' => $fraudCheck,
            ],
        ];
    }

    /**
     * Record a manual-processing intent. Ref: WHMCS offlinecc (RemoteStorage=>true,
     * capture returns false) — the module NEVER holds/returns the PAN; the merchant
     * runs the card manually on a physical POS. We return only masked card + reference;
     * full PAN/CVV are never persisted, encrypted, or handed back (PCI DSS 3.4 scope).
     *
     * @return array
     */
    public function makePayment(array $params)
    {
        $cardNumber = preg_replace('/[\s\-]/', '', $params['card_number'] ?? '');

        $maskedInfo = $this->maskCardInfo($cardNumber);
        $referenceId = 'OCC_' . strtoupper(uniqid()) . '_' . time();
        $instructions = $this->getConfig('instructions', '');

        // Canonical envelope: 'pending' (awaiting merchant manual processing on POS).
        return [
            'status' => 'pending',
            'stage' => 'awaiting_payment',
            'message' => __('admin/modules.offlinecc_awaiting_processing'),
            'payment_method' => 'offlinecc',
            'requires_manual_approval' => true,
            'no_immediate_payment' => true,
            'data' => [
                'payment_method' => 'offlinecc',
                'reference_id' => $referenceId,
                'masked_card' => $maskedInfo,
                'card_holder' => $params['card_holder'] ?? '',
                'expiry' => ($params['exp_month'] ?? '') . '/' . ($params['exp_year'] ?? ''),
                'amount' => $params['amount'] ?? 0,
                'currency' => $params['currency'] ?? '',
                'order_id' => $params['order_id'] ?? '',
                'instructions' => $instructions,
                'customer_info' => [
                    'message' => __('admin/modules.offlinecc_customer_message'),
                    'next_step' => __('admin/modules.offlinecc_customer_next_step'),
                ],
            ],
        ];
    }

    /**
     * Ödeme callback'i (public return/webhook yolu).
     *
     * OFFLINE gateway — dış gateway imzası YOKTUR (WHMCS offlinecc'de callback/return
     * hiç bulunmaz; ödeme fiziksel POS'ta manuel işlenir). Bu metod güvenilmeyen public
     * callback yolundan `$request->all()` ile tetiklenir, dolayısıyla caller-supplied
     * hiçbir bayrak (manual_approval vb.) onay olarak KABUL EDİLEMEZ — aksi hâlde
     * authenticated müşteri kendi faturasını admin-onayı olmadan 'paid' yapardı
     * (ödeme-bypass forgery). Onay yalnız admin-authz yolundan (PaymentService::
     * addPaymentToInvoice) yapılır; burada DAİMA pending döner. FAIL-CLOSED, sahte-success YASAK.
     *
     * @return array
     */
    public function handlePaymentCallback(array $params)
    {
        return [
            'status' => 'pending',
            'message' => __('admin/modules.offlinecc_pending_processing'),
        ];
    }

    /**
     * Refund not supported via module - must be processed on physical terminal
     *
     * @return array
     */
    public function refundPayment(array $params)
    {
        return [
            'status' => 'pending',
            'stage' => 'info_required',
            'message' => __('admin/modules.offlinecc_refund_manual'),
            'data' => [
                'refund_method' => 'manual_pos_terminal',
                'original_transaction_id' => $params['transaction_id'] ?? '',
                'refund_amount' => $params['amount'] ?? 0,
                'instructions' => __('admin/modules.offlinecc_refund_instructions'),
            ],
        ];
    }

    /**
     * Payment link generation is not supported for offline CC processing
     *
     * @return array
     */
    public function generatePaymentLink(array $params)
    {
        return [
            'status' => 'error',
            'message' => __('admin/modules.offlinecc_payment_link_not_supported'),
        ];
    }

    /**
     * Test connection - verify encryption key is valid
     *
     * @return array
     */
    public function testConnection(array $params = [])
    {
        $encryptionKey = $this->getConfig('encryption_key', '');

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

        if (strlen($encryptionKey) < 16) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_encryption_key_too_short'),
            ];
        }

        // Verify encryption works by encrypting and decrypting test data
        $testData = ['test' => 'connection_check'];
        $encrypted = $this->encryptCardData($testData, $encryptionKey);

        if ($encrypted === false) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_encryption_test_failed'),
            ];
        }

        $decrypted = $this->decryptCardData($encrypted, $encryptionKey);

        if ($decrypted === false || ($decrypted['test'] ?? '') !== 'connection_check') {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_decryption_test_failed'),
            ];
        }

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

    /**
     * Validate configuration fields
     */
    public function validateConfig()
    {
        $encryptionKey = $this->config['encryption_key'] ?? '';

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

        if (strlen($encryptionKey) < 16) {
            return [
                'status' => 'error',
                'message' => __('admin/modules.offlinecc_config_encryption_key_too_short'),
            ];
        }

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

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

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

    /**
     * Serialize card info - store only masked card number, expiry, cardholder name
     * NEVER store CVV
     */
    public function serializeCardInfo(array $cardInfo)
    {
        $cardNumber = preg_replace('/[\s\-]/', '', $cardInfo['card_number'] ?? '');

        return [
            'card_number' => $this->maskCardInfo($cardNumber),
            'card_holder' => $cardInfo['card_holder'] ?? '',
            'expiry_month' => $cardInfo['exp_month'] ?? $cardInfo['expiry_month'] ?? '',
            'expiry_year' => $cardInfo['exp_year'] ?? $cardInfo['expiry_year'] ?? '',
            'card_token' => null,
        ];
    }

    /**
     * Get configuration fields for admin panel
     */
    public function getConfigFields(): array
    {
        return array_merge(parent::getConfigFields(), [
            'encryption_key' => [
                'type' => 'password',
                'label' => 'Encryption Key',
                'description' => 'AES-256-CBC encryption key for storing card data (minimum 16 characters)',
                'required' => true,
                'value' => '',
                'placeholder' => 'Enter encryption key',
            ],
            'require_cvv' => [
                'type' => 'checkbox',
                'label' => 'Require CVV',
                'description' => 'Require CVV code during card entry',
                'value' => 'true',
            ],
            'instructions' => [
                'type' => 'textarea',
                'label' => 'Processing Instructions',
                'description' => 'Admin instructions for processing payments on physical terminal',
                'required' => false,
                'value' => '',
                'placeholder' => 'Enter instructions for manual card processing...',
            ],
        ]);
    }

    /**
     * Encrypt card data using AES-256-CBC
     *
     * @return string|false
     */
    private function encryptCardData(array $data, string $key)
    {
        $cipher = 'aes-256-cbc';
        $ivLength = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivLength);

        $jsonData = json_encode($data);
        $hashedKey = hash('sha256', $key, true);

        $encrypted = openssl_encrypt($jsonData, $cipher, $hashedKey, OPENSSL_RAW_DATA, $iv);

        if ($encrypted === false) {
            return false;
        }

        // Prepend IV for decryption, encode as base64
        return base64_encode($iv . $encrypted);
    }

    /**
     * Decrypt card data using AES-256-CBC
     *
     * @return array|false
     */
    private function decryptCardData(string $encryptedData, string $key)
    {
        $cipher = 'aes-256-cbc';
        $ivLength = openssl_cipher_iv_length($cipher);

        $decoded = base64_decode($encryptedData, true);
        if ($decoded === false || strlen($decoded) < $ivLength) {
            return false;
        }

        $iv = substr($decoded, 0, $ivLength);
        $encrypted = substr($decoded, $ivLength);
        $hashedKey = hash('sha256', $key, true);

        $decrypted = openssl_decrypt($encrypted, $cipher, $hashedKey, OPENSSL_RAW_DATA, $iv);

        if ($decrypted === false) {
            return false;
        }

        $result = json_decode($decrypted, true);

        return is_array($result) ? $result : false;
    }

    /**
     * Check if a card is expired
     */
    private function isCardExpired(int $month, int $year): bool
    {
        // Normalize 2-digit year to 4-digit
        if ($year < 100) {
            $year += 2000;
        }

        $now = now();
        $currentYear = (int) $now->format('Y');
        $currentMonth = (int) $now->format('m');

        if ($year < $currentYear) {
            return true;
        }

        if ($year === $currentYear && $month < $currentMonth) {
            return true;
        }

        return false;
    }

    /**
     * Luhn algorithm check for card number validation
     */
    private function luhnCheck(string $cardNumber): bool
    {
        $cardNumber = preg_replace('/\D/', '', $cardNumber);

        if (strlen($cardNumber) < 13 || strlen($cardNumber) > 19) {
            return false;
        }

        $sum = 0;
        $length = strlen($cardNumber);
        $parity = $length % 2;

        for ($i = 0; $i < $length; $i++) {
            $digit = (int) $cardNumber[$i];

            if ($i % 2 === $parity) {
                $digit *= 2;
                if ($digit > 9) {
                    $digit -= 9;
                }
            }

            $sum += $digit;
        }

        return $sum % 10 === 0;
    }

    /**
     * Mask card number showing first 6 and last 4 digits
     */
    private function maskCardInfo(string $cardNumber): string
    {
        $cardNumber = preg_replace('/\D/', '', $cardNumber);

        if (strlen($cardNumber) < 10) {
            return str_repeat('*', strlen($cardNumber));
        }

        $first6 = substr($cardNumber, 0, 6);
        $last4 = substr($cardNumber, -4);
        $middleLength = strlen($cardNumber) - 10;

        return $first6 . str_repeat('*', $middleLength) . $last4;
    }
}
