<?php

declare(strict_types=1);

namespace Origin\Payment;

use Origin\Engine\EngineClient;
use RuntimeException;
use InvalidArgumentException;
use JsonException;
use DateTimeImmutable;

class PaymentBridge
{
    private EngineClient $engine;
    private array $config;

    public function __construct(EngineClient $engine, array $config)
    {
        $this->engine = $engine;
        $this->config = $config;
    }

    public function createStripeCheckout(array $order): array
    {
        $required = ['amount', 'currency', 'success_url', 'cancel_url', 'items'];
        foreach ($required as $field) {
            if (empty($order[$field])) {
                throw new InvalidArgumentException("Missing required field: {$field}");
            }
        }

        $payload = [
            'gateway' => 'stripe',
            'amount' => (int)round($order['amount'] * 100),
            'currency' => strtolower($order['currency']),
            'success_url' => $order['success_url'],
            'cancel_url' => $order['cancel_url'],
            'line_items' => array_map(
                fn(array $item): array => [
                    'price_data' => [
                        'currency' => strtolower($order['currency']),
                        'product_data' => ['name' => $item['name'] ?? 'Product'],
                        'unit_amount' => (int)round(($item['price'] ?? 0) * 100),
                    ],
                    'quantity' => $item['quantity'] ?? 1,
                ],
                $order['items']
            ),
            'mode' => $order['mode'] ?? 'payment',
            'metadata' => array_merge(
                $order['metadata'] ?? [],
                ['origin_node' => '669427', 'created_at' => date('c')]
            ),
        ];

        return $this->engine->initPayment([
            'gateway' => 'stripe',
            'action' => 'create_checkout',
            'payload' => $payload,
        ]);
    }

    public function validateStripeWebhook(string $payload, string $sigHeader): array
    {
        $secret = $this->config['stripe']['webhook_secret'] ?? '';
        if (empty($secret)) {
            throw new RuntimeException('Stripe webhook secret not configured');
        }

        $timestamp = null;
        $signatures = [];
        $elements = explode(',', $sigHeader);

        foreach ($elements as $element) {
            $element = trim($element);
            if (str_starts_with($element, 't=')) {
                $timestamp = substr($element, 2);
            } elseif (str_starts_with($element, 'v1=')) {
                $signatures[] = substr($element, 3);
            }
        }

        if ($timestamp === null || empty($signatures)) {
            throw new RuntimeException('Invalid Stripe signature format');
        }

        if (abs(time() - (int)$timestamp) > 300) {
            throw new RuntimeException('Stripe webhook timestamp too old');
        }

        $signedPayload = $timestamp . '.' . $payload;
        $expectedSig = hash_hmac('sha256', $signedPayload, $secret);

        $valid = false;
        foreach ($signatures as $signature) {
            if (hash_equals($expectedSig, $signature)) {
                $valid = true;
                break;
            }
        }

        if (!$valid) {
            throw new RuntimeException('Stripe webhook signature invalid');
        }

        try {
            $event = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            throw new RuntimeException('Invalid Stripe payload JSON: ' . $e->getMessage());
        }

        return [
            'valid' => true,
            'type' => $event['type'] ?? 'unknown',
            'event' => $event,
        ];
    }

    public function createXMRPayment(float $amount, string $orderRef): array
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException('Amount must be positive');
        }

        $xmrConfig = $this->config['xmr'];
        $url = rtrim($xmrConfig['wallet_url'], '/') . '/json_rpc';

        $rpcPayload = json_encode([
            'jsonrpc' => '2.0',
            'id' => bin2hex(random_bytes(8)),
            'method' => 'create_address',
            'params' => [
                'account_index' => 0,
                'label' => 'order:' . $orderRef,
            ],
        ], JSON_THROW_ON_ERROR);

        $response = $this->xmrRpcCall($url, $rpcPayload, $xmrConfig);

        $subaddress = $response['result']['address'] ?? null;
        if ($subaddress === null) {
            throw new RuntimeException('Failed to generate XMR subaddress');
        }

        $paymentRecord = [
            'gateway' => 'xmr',
            'action' => 'create_payment',
            'payload' => [
                'order_ref' => $orderRef,
                'amount_xmr' => $amount,
                'subaddress' => $subaddress,
                'address_index' => $response['result']['address_index'] ?? null,
                'created_at' => date('c'),
                'expires_at' => (new DateTimeImmutable('+24 hours'))->format('c'),
            ],
        ];

        $engineResponse = $this->engine->initPayment($paymentRecord);

        return [
            'payment_id' => $engineResponse['payment_id'] ?? null,
            'subaddress' => $subaddress,
            'amount_xmr' => $amount,
            'order_ref' => $orderRef,
            'expires_at' => $paymentRecord['payload']['expires_at'],
        ];
    }

    public function checkXMRPayment(string $subaddress, int $requiredConfirmations = 10): array
    {
        $xmrConfig = $this->config['xmr'];
        $url = rtrim($xmrConfig['wallet_url'], '/') . '/json_rpc';

        $rpcPayload = json_encode([
            'jsonrpc' => '2.0',
            'id' => bin2hex(random_bytes(8)),
            'method' => 'get_transfers',
            'params' => [
                'in' => true,
                'pending' => true,
                'pool' => true,
                'subaddr_indices' => [],
            ],
        ], JSON_THROW_ON_ERROR);

        $response = $this->xmrRpcCall($url, $rpcPayload, $xmrConfig);

        $transfers = array_merge(
            $response['result']['in'] ?? [],
            $response['result']['pending'] ?? [],
            $response['result']['pool'] ?? []
        );

        foreach ($transfers as $transfer) {
            if (($transfer['address'] ?? '') === $subaddress && ($transfer['amount'] ?? 0) > 0) {
                $confirmations = $transfer['confirmations'] ?? 0;

                return [
                    'found' => true,
                    'subaddress' => $subaddress,
                    'amount_atomic' => $transfer['amount'],
                    'amount_xmr' => $transfer['amount'] / 1e12,
                    'confirmations' => $confirmations,
                    'confirmed' => $confirmations >= $requiredConfirmations,
                    'tx_hash' => $transfer['txid'] ?? null,
                    'height' => $transfer['height'] ?? null,
                    'timestamp' => $transfer['timestamp'] ?? null,
                ];
            }
        }

        return ['found' => false, 'subaddress' => $subaddress];
    }

    public function createUSDTPayment(float $amount, string $orderRef): array
    {
        return $this->createTronPayment('usdt_trc20', $amount, $orderRef);
    }

    public function createUSDCPayment(float $amount, string $orderRef): array
    {
        return $this->createTronPayment('usdc_polygon', $amount, $orderRef);
    }

    private function createTronPayment(string $token, float $amount, string $orderRef): array
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException('Amount must be positive');
        }

        $paymentRecord = [
            'gateway' => $token,
            'action' => 'create_payment',
            'payload' => [
                'order_ref' => $orderRef,
                'amount' => $amount,
                'token' => $token,
                'created_at' => date('c'),
                'expires_at' => (new DateTimeImmutable('+24 hours'))->format('c'),
            ],
        ];

        $response = $this->engine->initPayment($paymentRecord);

        return [
            'payment_id' => $response['payment_id'] ?? null,
            'deposit_address' => $response['deposit_address'] ?? null,
            'amount' => $amount,
            'token' => $token,
            'order_ref' => $orderRef,
            'expires_at' => $paymentRecord['payload']['expires_at'],
        ];
    }

    public function checkTronPayment(string $paymentId): array
    {
        return $this->engine->checkPayment($paymentId);
    }

    public function createETransferPayment(float $amount, string $orderRef, string $customerEmail): array
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException('Amount must be positive');
        }

        if (!filter_var($customerEmail, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid customer email');
        }

        $secretQuestion = bin2hex(random_bytes(4));
        $secretAnswer = bin2hex(random_bytes(8));

        $paymentRecord = [
            'gateway' => 'etransfer',
            'action' => 'create_payment',
            'payload' => [
                'order_ref' => $orderRef,
                'amount' => $amount,
                'customer_email' => $customerEmail,
                'secret_question' => $secretQuestion,
                'secret_answer_hash' => hash('sha256', $secretAnswer),
                'status' => 'awaiting_deposit',
                'created_at' => date('c'),
                'expires_at' => (new DateTimeImmutable('+72 hours'))->format('c'),
            ],
        ];

        $response = $this->engine->initPayment($paymentRecord);

        return [
            'payment_id' => $response['payment_id'] ?? null,
            'amount' => $amount,
            'order_ref' => $orderRef,
            'deposit_email' => $this->config['etransfer']['imap_user'] ?? 'payments@origin.local',
            'secret_question' => $secretQuestion,
            'secret_answer' => $secretAnswer,
            'expires_at' => $paymentRecord['payload']['expires_at'],
        ];
    }

    public function verifyETransferDeposit(string $paymentId, array $emailData): bool
    {
        $patterns = $this->config['etransfer']['auto_deposit_patterns'] ?? [];
        $subject = $emailData['subject'] ?? '';
        $body = $emailData['body'] ?? '';
        $combined = $subject . ' ' . $body;

        $matched = false;
        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $combined)) {
                $matched = true;
                break;
            }
        }

        if (!$matched) {
            return false;
        }

        $this->engine->initPayment([
            'gateway' => 'etransfer',
            'action' => 'confirm_deposit',
            'payload' => [
                'payment_id' => $paymentId,
                'email_subject' => $subject,
                'email_from' => $emailData['from'] ?? '',
                'verified_at' => date('c'),
            ],
        ]);

        return true;
    }

    private function xmrRpcCall(string $url, string $payload, array $xmrConfig): array
    {
        $headers = ['Content-Type: application/json'];
        if (!empty($xmrConfig['rpc_user'])) {
            $credentials = base64_encode($xmrConfig['rpc_user'] . ':' . ($xmrConfig['rpc_password'] ?? ''));
            $headers[] = 'Authorization: Basic ' . $credentials;
        }

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_TIMEOUT => $xmrConfig['timeout'] ?? 30,
        ]);

        $response = curl_exec($ch);
        $err = curl_error($ch);
        curl_close($ch);

        if ($response === false) {
            throw new RuntimeException('XMR wallet RPC error: ' . $err);
        }

        try {
            $decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            throw new RuntimeException('Invalid XMR RPC response: ' . $e->getMessage());
        }

        if (isset($decoded['error'])) {
            throw new RuntimeException(
                'XMR RPC error: ' . ($decoded['error']['message'] ?? json_encode($decoded['error']))
            );
        }

        return $decoded;
    }
}
