<?php
/**
 * Process Payment for Binance Order
 * Uses Bridge Liquidation Address flow (same as manual process)
 */

// Set base directory for includes
// When called from order-monitor.php, __DIR__ is already the api directory
// So we need to go up one level to get to binance directory
$baseDir = dirname(__DIR__);

// Load required files with absolute paths
// Only load config if not already loaded (when called from order-monitor.php)
if (!class_exists('PDO') || !isset($pdo)) {
    require_once $baseDir . '/includes/config.php';
}

// Only require files if not already loaded
if (!class_exists('BinanceP2PAPI')) {
    require_once $baseDir . '/includes/binance-api.php';
}
if (!class_exists('BinanceP2PSettings')) {
    require_once $baseDir . '/includes/binance-settings.php';
}
if (!class_exists('BridgeAPI')) {
    require_once $baseDir . '/includes/bridge-api.php';
}
if (!class_exists('IBANValidator')) {
    require_once $baseDir . '/includes/iban-validator.php';
}
if (!function_exists('extractPaymentDetails')) {
    require_once $baseDir . '/includes/functions.php';
}
if (!class_exists('TransactionConfirmation')) {
    require_once $baseDir . '/includes/transaction-confirm.php';
}

/**
 * Process payment for a Binance order using liquidation address flow
 */
function processBinanceOrderPayment($orderId, $orderNo, $orderData, $pdo, $settings) {
    error_log("Processing payment for order: $orderNo");
    
    try {
        // If payment details are missing, try to get them from buyer's payment method
        $paymentDetails = extractPaymentDetails($orderData);
        
        if (empty($paymentDetails['iban'] ?? $paymentDetails['IBAN'] ?? '')) {
            error_log("Payment details missing in process-payment.php, attempting to get from buyer userId...");
            
            $buyerUserId = $orderData['buyerUserId'] ?? $orderData['buyer_user_id'] ?? null;
            if (!empty($buyerUserId)) {
                // Get Binance API credentials for payment method lookup
                $apiKey = $settings->getBinanceApiKey();
                $secretKey = $settings->getBinanceSecretKey();
                
                if (!empty($apiKey) && !empty($secretKey)) {
                    $binance = new BinanceP2PAPI($apiKey, $secretKey);
                    $paymentMethodResponse = $binance->getPaymentMethodByUserId($buyerUserId);
                
                if (!isset($paymentMethodResponse['error']) && isset($paymentMethodResponse['data'])) {
                    $paymentMethodData = $paymentMethodResponse['data'];
                    if (isset($paymentMethodData['paymentMethodFields']) || isset($paymentMethodData['fields'])) {
                        $fields = $paymentMethodData['paymentMethodFields'] ?? $paymentMethodData['fields'] ?? [];
                        foreach ($fields as $field) {
                            $fieldName = strtolower($field['fieldName'] ?? '');
                            $fieldValue = $field['fieldValue'] ?? '';
                            if (stripos($fieldName, 'iban') !== false && !empty($fieldValue)) {
                                $paymentDetails['iban'] = $fieldValue;
                                error_log("Found IBAN from payment method in process-payment: " . substr($fieldValue, 0, 4) . "****");
                                break;
                            }
                        }
                    }
                }
                } else {
                    error_log("Binance API credentials not available for payment method lookup");
                }
            }
            
            // If IBAN still not found, try to extract from chat messages
            if (empty($paymentDetails['iban'] ?? $paymentDetails['IBAN'] ?? '')) {
                error_log("IBAN still missing after checking payment method. Attempting to extract from chat messages...");
                $ibanFromChat = extractIBANFromChat($orderNo, $pdo, $settings);
                
                if (!empty($ibanFromChat)) {
                    $paymentDetails['iban'] = $ibanFromChat;
                    $paymentDetails['iban_source'] = 'chat'; // Track that IBAN came from chat
                    error_log("✓ Found IBAN from chat messages: " . substr($ibanFromChat, 0, 4) . "****");
                } else {
                    error_log("✗ IBAN not found in chat messages either");
                }
            }
        }
        
        // Update order status to processing
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET payment_status = 'processing', updated_at = NOW() 
            WHERE id = ?
        ");
        $stmt->execute([$orderId]);
        
        // Get order details
        // Binance API returns: totalPrice (fiat amount), fiatUnit (currency), or fiat/fiatCurrency
        $fiatCurrency = strtoupper($orderData['fiatUnit'] ?? $orderData['fiatCurrency'] ?? $orderData['fiat_currency'] ?? $orderData['fiat'] ?? 'EUR');
        $fiatAmount = floatval($orderData['totalPrice'] ?? $orderData['fiatAmount'] ?? $orderData['fiat_amount'] ?? 0);
        
        // Log for debugging
        if ($fiatAmount == 0) {
            error_log("WARNING: Fiat amount is 0. Order data keys: " . implode(', ', array_keys($orderData)));
            error_log("Order data sample: " . json_encode(array_intersect_key($orderData, array_flip(['totalPrice', 'fiatAmount', 'fiat_amount', 'fiatUnit', 'fiatCurrency', 'fiat_currency', 'fiat', 'price', 'amount']))));
        }
        
        // Only process EUR/SEPA for now (as per your manual process)
        if ($fiatCurrency !== 'EUR') {
            $error = "Only EUR/SEPA orders are supported currently";
            updateOrderError($pdo, $orderId, $error);
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Extract payment details from order (if not already extracted above)
        if (empty($paymentDetails['iban'] ?? $paymentDetails['IBAN'] ?? '')) {
            $paymentDetails = extractPaymentDetails($orderData);
            // Mark IBAN source as payment_page if found in order data
            if (!empty($paymentDetails['iban'] ?? $paymentDetails['IBAN'] ?? '')) {
                $paymentDetails['iban_source'] = 'payment_page';
            }
        }
        
        // Validate IBAN and extract details
        $iban = strtoupper(str_replace(' ', '', $paymentDetails['iban'] ?? $paymentDetails['IBAN'] ?? ''));
        $receiverName = trim($paymentDetails['name'] ?? $paymentDetails['account_holder_name'] ?? $paymentDetails['receiver_name'] ?? '');
        $bankName = trim($paymentDetails['bank_name'] ?? '');
        
        // Check if any required field is missing
        $missingFields = [];
        if (empty($iban)) {
            $missingFields[] = 'IBAN';
        }
        if (empty($receiverName)) {
            $missingFields[] = 'Name';
        }
        // Bank name is now extracted from payType/tradeMethodName or defaults to "revolut"
        // So we don't need to mark it as missing - it will always have a value
        // Only check if it's truly empty (shouldn't happen after extraction improvements)
        if (empty($bankName)) {
            // This shouldn't happen anymore, but log it if it does
            error_log("WARNING: Bank name is still empty after extraction, using default 'revolut'");
            $bankName = 'revolut';
        }
        
        if (!empty($missingFields)) {
            $error = "Missing required payment details: " . implode(', ', $missingFields);
            error_log("Order $orderNo: $error - marking as paid automatically for manual processing");
            error_log("process-payment - Full order data structure (first 3000 chars): " . substr(json_encode($orderData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), 0, 3000));
            
            // Mark order as paid on Binance automatically (user will handle manually)
            error_log("Order $orderNo: Calling markOrderAsPaidOnBinance() because required fields are missing");
            $markPaidResult = markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            
            if ($markPaidResult['success']) {
                error_log("Order $orderNo: Successfully marked as paid in Binance");
            } else {
                error_log("Order $orderNo: FAILED to mark as paid in Binance - " . ($markPaidResult['error'] ?? 'Unknown error'));
            }
            
            updateOrderError($pdo, $orderId, $error);
            return ['success' => false, 'error' => $error, 'marked_paid' => $markPaidResult['success']];
        }
        
        // Validate IBAN before sending to Bridge
        $ibanValidation = IBANValidator::validateIBAN($iban);
        
        if (!$ibanValidation['valid']) {
            // If IBAN validation fails, mark as paid automatically (user will handle manually)
            $error = "IBAN validation failed: " . ($ibanValidation['error'] ?? 'Invalid IBAN format');
            error_log("Order $orderNo: $error - marking as paid automatically for manual processing");
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            updateOrderError($pdo, $orderId, $error);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // IBAN is valid - continue processing
        $countryCode3 = $ibanValidation['country_code_3'];
        $iban = $ibanValidation['iban']; // Validated and cleaned IBAN
        
        // Name and bank name already extracted above (before validation check)
        // Use default bank name if still empty (shouldn't happen due to check above, but just in case)
        if (empty($bankName)) {
            $bankName = 'Revolut';
        }
        
        // Store original name before translation
        $originalName = $receiverName;
        
        // Translate name to English if needed
        $receiverName = IBANValidator::translateToEnglish($receiverName);
        
        // Check if name was actually translated
        $nameWasTranslated = (strtolower(trim($receiverName)) !== strtolower(trim($originalName)));
        
        // Update payment_details with translated name and metadata
        // Store the translated name in the 'name' field (this is what will be displayed)
        $paymentDetails['name'] = $receiverName; // Store translated name
        $paymentDetails['account_holder_name'] = $receiverName; // Also update account_holder_name if it exists
        $paymentDetails['receiver_name'] = $receiverName; // Also update receiver_name if it exists
        
        if ($nameWasTranslated) {
            $paymentDetails['name_translated'] = 'yes';
            $paymentDetails['original_name'] = $originalName; // Store original for reference
        } else {
            $paymentDetails['name_translated'] = 'no';
        }
        
        // Update payment_details in database with translated name and metadata
        try {
            $updateStmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET payment_details = ? 
                WHERE id = ?
            ");
            $updateStmt->execute([json_encode($paymentDetails), $orderId]);
            error_log("Updated payment_details with translated name: '$receiverName' (original: '$originalName', IBAN source: " . ($paymentDetails['iban_source'] ?? 'payment_page') . ", Name translated: " . ($nameWasTranslated ? 'yes' : 'no') . ")");
        } catch (Exception $e) {
            error_log("Warning: Could not update payment_details metadata: " . $e->getMessage());
        }
        
        // Get Bridge customer ID (your account)
        $bridgeCustomerId = $settings->getBridgeCustomerId();
        
        if (empty($bridgeCustomerId)) {
            $error = "Bridge customer ID not configured";
            updateOrderError($pdo, $orderId, $error);
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Initialize Bridge API
        $bridge = new BridgeAPI(BRIDGE_API_KEY);
        
        // Step 1: Create or get external account for buyer
        // Note: createOrGetExternalAccount will translate the name internally and return the translated name
        $accountResult = createOrGetExternalAccount(
            $bridge, 
            $bridgeCustomerId, 
            $iban, 
            $receiverName, 
            $bankName, 
            $countryCode3,
            $pdo,
            $orderId
        );
        
        // Handle both old format (string) and new format (array)
        if (is_array($accountResult)) {
            $externalAccountId = $accountResult['account_id'];
            $finalTranslatedName = $accountResult['translated_name'] ?? $receiverName;
        } else {
            // Old format - just account ID
            $externalAccountId = $accountResult;
            $finalTranslatedName = $receiverName; // Use the translated name we have
        }
        
        // Update payment_details with the final translated name that was sent to Bridge
        if ($nameWasTranslated && !empty($finalTranslatedName)) {
            $paymentDetails['name'] = $finalTranslatedName;
            $paymentDetails['translated_name'] = $finalTranslatedName; // Store separately for clarity
            
            try {
                $updateStmt = $pdo->prepare("
                    UPDATE binance_p2p_orders 
                    SET payment_details = ? 
                    WHERE id = ?
                ");
                $updateStmt->execute([json_encode($paymentDetails), $orderId]);
                error_log("Updated payment_details with final translated name: '$finalTranslatedName'");
            } catch (Exception $e) {
                error_log("Warning: Could not update payment_details with final translated name: " . $e->getMessage());
            }
        }
        
        if (!$externalAccountId) {
            $error = "Failed to create/get external account";
            updateOrderError($pdo, $orderId, $error);
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Step 2: Calculate USDC amount using Bridge exchange rate (BEFORE sending)
        // This matches your manual process:
        // - User places order: needs 13.54 EUR
        // - You check Bridge exchange rate (USDC -> EUR) in sell.php
        // - Calculate: USDC needed = 13.54 / exchange_rate
        // - Add small buffer (0.01-0.03) for rate fluctuations
        // - Send that calculated USDC amount
        
        error_log("Getting Bridge exchange rate for: USDC -> $fiatCurrency");
        
        // Get Bridge exchange rate (USDC to Fiat) - this is the rate BEFORE sending
        // Same rate you see in your manual process on sell.php
        $rateData = $bridge->getExchangeRate('usd', strtolower($fiatCurrency));
        
        if (isset($rateData['error'])) {
            error_log("Error getting exchange rate: " . $rateData['error']);
            // Use fallback rate
            $exchangeRate = 1.0;
        } else {
            // Bridge returns sell_rate for USDC -> Fiat conversion
            // This is the rate you see in your manual process
            $exchangeRate = isset($rateData['sell_rate']) ? floatval($rateData['sell_rate']) : 1.0;
            
            // Also log buy_rate if available for reference
            if (isset($rateData['buy_rate'])) {
                error_log("Bridge buy_rate (Fiat -> USDC): " . $rateData['buy_rate']);
            }
        }
        
        error_log("Bridge exchange rate (USDC -> $fiatCurrency): $exchangeRate");
        error_log("Order fiat amount: $fiatAmount $fiatCurrency");
        
        // IMPORTANT: Bridge charges 0.60% fee
        // User orders 13.54 EUR - they should RECEIVE 13.54 EUR
        // But Bridge charges 0.60% fee, so we need to send MORE USDC to cover the fee
        // Calculation: amount_before_fee = amount_after_fee / (1 - fee_rate)
        // Example: 13.54 EUR / (1 - 0.006) = 13.54 / 0.994 = 13.6217 EUR
        
        $bridgeFeeRate = 0.006; // 0.60% = 0.006
        $fiatAmountBeforeFee = $fiatAmount / (1 - $bridgeFeeRate);
        
        error_log("Fiat amount before Bridge fee (0.60%): $fiatAmountBeforeFee $fiatCurrency");
        error_log("Fiat amount user will receive: $fiatAmount $fiatCurrency");
        error_log("Bridge fee amount: " . ($fiatAmountBeforeFee - $fiatAmount) . " $fiatCurrency");
        
        // Calculate USDC needed: fiat_amount_before_fee / exchange_rate
        // Example: 13.6217 EUR / 0.92 (exchange rate) = 14.81 USDC
        $usdcAmount = $fiatAmountBeforeFee / $exchangeRate;
        
        // Add small buffer (0.01-0.03) to account for rate fluctuations
        // This matches your manual process of adding 0.01-0.03 more
        $usdcAmount = $usdcAmount + 0.02;
        
        // Round to 6 decimal places (USDC precision)
        $usdcAmount = round($usdcAmount, 6);
        
        error_log("Calculated USDC amount: $usdcAmount USDC (for $fiatAmount $fiatCurrency at rate $exchangeRate, including 0.60% Bridge fee)");
        
        // Step 3: Create liquidation address
        $liquidationAddress = createLiquidationAddress(
            $bridge,
            $bridgeCustomerId,
            $externalAccountId,
            $fiatCurrency,
            $pdo,
            $orderId
        );
        
        if (!$liquidationAddress || !isset($liquidationAddress['address'])) {
            $error = "Failed to create liquidation address";
            updateOrderError($pdo, $orderId, $error);
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Step 4: Send USDC from wallet to liquidation address
        $walletAddress = $settings->get('wallet_address'); // Your wallet address
        $walletPrivateKey = $settings->get('wallet_private_key'); // Encrypted private key
        
        if (empty($walletAddress) || empty($walletPrivateKey)) {
            $error = "Wallet address or private key not configured";
            updateOrderError($pdo, $orderId, $error);
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Send USDC to liquidation address
        error_log("Sending $usdcAmount USDC from $walletAddress to liquidation address: " . $liquidationAddress['address']);
        
        $txHash = sendUSDCToLiquidationAddress(
            $walletAddress,
            $walletPrivateKey,
            $liquidationAddress['address'],
            $usdcAmount,
            $pdo,
            $orderId
        );
        
        if (!$txHash) {
            $error = "Failed to send USDC to liquidation address";
            updateOrderError($pdo, $orderId, $error);
            // Still mark as paid (as per requirement)
            markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
            return ['success' => false, 'error' => $error, 'marked_paid' => true];
        }
        
        // Step 5: Wait for transaction confirmation
        if (!class_exists('TransactionConfirmation')) {
            require_once $baseDir . '/includes/transaction-confirm.php';
        }
        
        $txConfirmed = false;
        $txConfirmations = 0;
        
        if ($txHash) {
            $txConfirm = new TransactionConfirmation();
            // Wait up to 60 seconds for 1 confirmation
            $txConfirmed = $txConfirm->waitForConfirmation($txHash, 60, 1);
            
            if ($txConfirmed) {
                $txStatus = $txConfirm->getTransactionStatus($txHash);
                $txConfirmations = $txStatus['confirmations'] ?? 0;
                error_log("Transaction confirmed with $txConfirmations confirmations");
            } else {
                error_log("Transaction not confirmed yet, but proceeding with payment marking");
            }
        }
        
        // Success - update order with all details
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET bridge_customer_id = ?,
                bridge_external_account_id = ?,
                bridge_transfer_id = ?,
                bridge_liquidation_address = ?,
                tx_hash = ?,
                usdc_amount_sent = ?,
                exchange_rate_used = ?,
                tx_confirmed = ?,
                tx_confirmations = ?,
                payment_status = 'completed',
                payment_processed_at = NOW(),
                updated_at = NOW()
            WHERE id = ?
        ");
        $stmt->execute([
            $bridgeCustomerId,
            $externalAccountId,
            $liquidationAddress['id'] ?? $txHash,
            $liquidationAddress['address'] ?? null,
            $txHash,
            $usdcAmount,
            $exchangeRate,
            $txConfirmed ? 1 : 0,
            $txConfirmations,
            $orderId
        ]);
        
        error_log("Payment processed successfully. Liquidation Address: " . $liquidationAddress['address']);
        error_log("Transaction Hash: $txHash");
        error_log("USDC Amount Sent: $usdcAmount");
        
        // IMPORTANT: Mark order as paid on Binance AFTER USDC is sent
        error_log("USDC sent successfully. Now marking order as paid on Binance...");
        $markPaidResult = markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
        
        if ($markPaidResult['success']) {
            error_log("Order $orderNo marked as paid on Binance successfully");
        } else {
            error_log("WARNING: Failed to mark order $orderNo as paid on Binance: " . ($markPaidResult['error'] ?? 'Unknown error'));
        }
        
        // IMPORTANT: Send custom message to buyer AFTER order is marked as paid
        error_log("Preparing to send custom message to buyer for order $orderNo...");
        sendPaymentConfirmationMessage($orderNo, $fiatAmount, $fiatCurrency, $pdo, $settings);
        error_log("Custom message queued for order $orderNo (will be sent via WebSocket cron)");
        
        return [
            'success' => true,
            'liquidation_address' => $liquidationAddress['address'],
            'tx_hash' => $txHash,
            'marked_paid' => $markPaidResult['success'],
            'message_sent' => true
        ];
        
    } catch (Exception $e) {
        $error = "Payment processing error: " . $e->getMessage();
        error_log("ERROR: $error");
        error_log("Stack trace: " . $e->getTraceAsString());
        
        // Mark order as paid anyway (as per requirement)
        markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings);
        updateOrderError($pdo, $orderId, $error);
        
        return [
            'success' => false,
            'error' => $error,
            'marked_paid' => true
        ];
    }
}

/**
 * Extract payment details from order data
 * Binance API may have payment info in various nested structures
 */
function extractPaymentDetails($orderData) {
    $details = [];
    
    // Log all keys for debugging
    error_log("Extracting payment details. Order data keys: " . implode(', ', array_keys($orderData)));
    
    // Deep search for payment-related data in the entire structure
    // First, let's recursively search for any field containing "iban", "account", "bank", etc.
    $allValues = [];
    $recursiveSearch = function($data, $path = '') use (&$recursiveSearch, &$allValues) {
        if (is_array($data)) {
            foreach ($data as $key => $value) {
                $currentPath = $path ? "$path.$key" : $key;
                if (is_array($value)) {
                    $recursiveSearch($value, $currentPath);
                } else {
                    $keyLower = strtolower($key);
                    $valueStr = (string)$value;
                    // Check if key or value contains payment-related terms
                    if (stripos($keyLower, 'iban') !== false || 
                        stripos($keyLower, 'account') !== false ||
                        stripos($keyLower, 'bank') !== false ||
                        stripos($keyLower, 'field') !== false ||
                        stripos($keyLower, 'payaccount') !== false ||
                        (strlen($valueStr) > 10 && preg_match('/^[A-Z]{2}\d{2}/', $valueStr))) { // IBAN pattern
                        $allValues[$currentPath] = $valueStr;
                    }
                }
            }
        }
    };
    $recursiveSearch($orderData);
    
    if (!empty($allValues)) {
        error_log("Found potential payment fields: " . json_encode($allValues));
        foreach ($allValues as $path => $value) {
            $pathLower = strtolower($path);
            if (stripos($pathLower, 'iban') !== false || 
                stripos($pathLower, 'payaccount') !== false ||
                (stripos($pathLower, 'account') !== false && strlen($value) > 10)) {
                if (!isset($details['iban']) && preg_match('/^[A-Z]{2}\d{2}/', strtoupper($value))) {
                    // Remove ALL whitespace characters including non-breaking spaces
                    $iban = strtoupper(preg_replace('/\s+/', '', $value));
                    $iban = str_replace(["\xC2\xA0", "\u{00A0}"], '', $iban);
                    if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $iban)) {
                        $details['iban'] = $iban;
                        error_log("Found IBAN at path: $path = " . substr($details['iban'], 0, 4) . "****");
                    }
                }
            }
        }
    }
    
    // Check for payMethods array structure (this is what Binance actually returns)
    // Structure: payMethods[0].fields[] where each field has fieldName and fieldValue
    if (isset($orderData['payMethods']) && is_array($orderData['payMethods'])) {
        error_log("Found payMethods array with " . count($orderData['payMethods']) . " payment methods");
        
        foreach ($orderData['payMethods'] as $payMethod) {
            if (!is_array($payMethod) || !isset($payMethod['fields']) || !is_array($payMethod['fields'])) {
                continue;
            }
            
            error_log("Processing payment method: " . ($payMethod['identifier'] ?? $payMethod['tradeMethodName'] ?? 'unknown'));
            
            foreach ($payMethod['fields'] as $field) {
                if (!is_array($field)) continue;
                
                $fieldName = strtolower($field['fieldName'] ?? '');
                $fieldValue = trim($field['fieldValue'] ?? '');
                
                if (empty($fieldValue)) continue;
                
                error_log("Processing field: fieldName='$fieldName', fieldValue='" . substr($fieldValue, 0, 10) . "...'");
                
                // Extract IBAN - handle multiple field name variations
                // Priority: Check fieldContentType first, then fieldName
                
                // Check fieldContentType for "pay_account" which contains IBAN/account number
                if (isset($field['fieldContentType']) && strtolower($field['fieldContentType']) === 'pay_account' && !isset($details['iban'])) {
                    // Remove ALL whitespace characters including non-breaking spaces
                    $accountValue = strtoupper(preg_replace('/\s+/', '', $fieldValue));
                    $accountValue = str_replace(["\xC2\xA0", "\u{00A0}"], '', $accountValue);
                    if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $accountValue)) {
                        $details['iban'] = $accountValue;
                        error_log("Found IBAN via fieldContentType=pay_account: " . substr($accountValue, 0, 4) . "****");
                    }
                }
                
                // Check fieldName for IBAN-related fields
                // "IBAN Number", "Bank Card/Account Number", "IBAN"
                if (!isset($details['iban']) && 
                    (stripos($fieldName, 'iban') !== false || 
                     stripos($fieldName, 'bank card') !== false ||
                     stripos($fieldName, 'account number') !== false)) {
                    // Clean and validate IBAN
                    // Remove ALL whitespace characters including non-breaking spaces (\u00a0, \xC2\xA0)
                    $iban = strtoupper(preg_replace('/\s+/', '', $fieldValue));
                    // Also explicitly remove non-breaking space characters
                    $iban = str_replace(["\xC2\xA0", "\u{00A0}"], '', $iban);
                    // Check if it matches IBAN pattern (2 letters + 2 digits + alphanumeric)
                    if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $iban)) {
                        $details['iban'] = $iban;
                        error_log("Found IBAN in payMethods: $fieldName = " . substr($iban, 0, 4) . "****");
                    } else {
                        // Log the cleaned IBAN for debugging
                        error_log("Field '$fieldName' contains value that doesn't match IBAN pattern. Original: '" . substr($fieldValue, 0, 20) . "', Cleaned: '$iban'");
                    }
                }
                
                // Extract bank name - handle "Bank Name" and "Bank name" (case variations)
                // Also check fieldContentType for "bank"
                if (isset($field['fieldContentType']) && strtolower($field['fieldContentType']) === 'bank' && !isset($details['bank_name'])) {
                    $details['bank_name'] = $fieldValue;
                    error_log("Found bank name via fieldContentType=bank: $fieldValue");
                } elseif (stripos($fieldName, 'bank name') !== false ||
                          (stripos($fieldName, 'bank') !== false && 
                           stripos($fieldName, 'account') === false && 
                           stripos($fieldName, 'opening') === false &&
                           stripos($fieldName, 'branch') === false &&
                           stripos($fieldName, 'card') === false)) {
                    if (!isset($details['bank_name'])) {
                        $details['bank_name'] = $fieldValue;
                        error_log("Found bank name: $fieldName = $fieldValue");
                    }
                }
                
                // Extract account holder name - "Real name", "Full Name", "Name"
                // Priority: Check fieldContentType for "payee" first
                if (isset($field['fieldContentType']) && strtolower($field['fieldContentType']) === 'payee' && !isset($details['name'])) {
                    $details['name'] = $fieldValue;
                    error_log("Found account holder name via fieldContentType=payee: $fieldValue");
                } elseif (!isset($details['name']) && 
                         (stripos($fieldName, 'real name') !== false ||
                          stripos($fieldName, 'full name') !== false ||
                          ($fieldName === 'name' || stripos($fieldName, 'name') !== false && 
                           stripos($fieldName, 'bank') === false && 
                           stripos($fieldName, 'account') === false &&
                           stripos($fieldName, 'iban') === false &&
                           stripos($fieldName, 'opening') === false &&
                           stripos($fieldName, 'branch') === false))) {
                    $details['name'] = $fieldValue;
                    error_log("Found account holder name: $fieldName = $fieldValue");
                }
            }
        }
    }
    
    // According to SAPI v7.4 documentation, payment details are stored in payment method fields
    // Structure: paymentMethodFields[] array with fieldName and fieldValue
    // Example: {fieldName: "IBAN", fieldValue: "DE89370400440532013000"}
    
    // Check for paymentMethodFields array (this is the correct structure per SAPI docs)
    if (isset($orderData['paymentMethodFields']) && is_array($orderData['paymentMethodFields'])) {
        error_log("Found paymentMethodFields array with " . count($orderData['paymentMethodFields']) . " fields");
        
        foreach ($orderData['paymentMethodFields'] as $field) {
            if (!is_array($field)) continue;
            
            $fieldName = strtolower($field['fieldName'] ?? $field['field_title'] ?? $field['fieldTitle'] ?? '');
            $fieldValue = $field['fieldValue'] ?? $field['field_value'] ?? $field['fieldValue'] ?? '';
            
            if (empty($fieldValue)) continue;
            
            error_log("Processing field: fieldName='$fieldName', fieldValue='" . substr($fieldValue, 0, 10) . "...'");
            
            // Check for IBAN (case-insensitive matching)
            if (stripos($fieldName, 'iban') !== false || 
                stripos($fieldName, 'account') !== false ||
                stripos($fieldName, 'bank account') !== false ||
                stripos($fieldName, 'account number') !== false) {
                $details['iban'] = $fieldValue;
                error_log("Found IBAN in paymentMethodFields: $fieldName = " . substr($fieldValue, 0, 4) . "****");
            }
            
            // Check for bank name
            if (stripos($fieldName, 'bank') !== false && stripos($fieldName, 'account') === false) {
                $details['bank_name'] = $fieldValue;
                error_log("Found bank name: $fieldName = $fieldValue");
            }
            
            // Check for account holder name
            if (stripos($fieldName, 'name') !== false || 
                stripos($fieldName, 'holder') !== false ||
                stripos($fieldName, 'receiver') !== false ||
                stripos($fieldName, 'account holder') !== false) {
                $details['name'] = $fieldValue;
                error_log("Found name: $fieldName = $fieldValue");
            }
        }
    }
    
    // Also check buyerPaymentInfo (legacy structure)
    if (isset($orderData['buyerPaymentInfo']) && is_array($orderData['buyerPaymentInfo'])) {
        error_log("Found buyerPaymentInfo with keys: " . implode(', ', array_keys($orderData['buyerPaymentInfo'])));
        
        // If it's an array of fields (like paymentMethodFields)
        if (isset($orderData['buyerPaymentInfo'][0]) && is_array($orderData['buyerPaymentInfo'][0])) {
            foreach ($orderData['buyerPaymentInfo'] as $field) {
                $fieldName = strtolower($field['fieldName'] ?? $field['field_title'] ?? '');
                $fieldValue = $field['fieldValue'] ?? $field['field_value'] ?? '';
                
                if (stripos($fieldName, 'iban') !== false || stripos($fieldName, 'account') !== false) {
                    $details['iban'] = $fieldValue;
                }
                if (stripos($fieldName, 'bank') !== false && !isset($details['bank_name'])) {
                    $details['bank_name'] = $fieldValue;
                }
                if (stripos($fieldName, 'name') !== false && !isset($details['name'])) {
                    $details['name'] = $fieldValue;
                }
            }
        } else {
            // Direct key-value structure
            $details = array_merge($details, $orderData['buyerPaymentInfo']);
        }
    }
    
    // Check other possible locations
    if (isset($orderData['paymentDetails']) && is_array($orderData['paymentDetails'])) {
        $details = array_merge($details, $orderData['paymentDetails']);
        error_log("Found paymentDetails with keys: " . implode(', ', array_keys($orderData['paymentDetails'])));
    }
    
    if (isset($orderData['paymentInfo']) && is_array($orderData['paymentInfo'])) {
        $details = array_merge($details, $orderData['paymentInfo']);
        error_log("Found paymentInfo with keys: " . implode(', ', array_keys($orderData['paymentInfo'])));
    }
    
    // Check for nested structures
    if (isset($orderData['buyer']) && is_array($orderData['buyer'])) {
        if (isset($orderData['buyer']['paymentInfo'])) {
            $details = array_merge($details, $orderData['buyer']['paymentInfo']);
        }
        if (isset($orderData['buyer']['paymentDetails'])) {
            $details = array_merge($details, $orderData['buyer']['paymentDetails']);
        }
        if (isset($orderData['buyer']['paymentMethodFields'])) {
            foreach ($orderData['buyer']['paymentMethodFields'] as $field) {
                $fieldName = strtolower($field['fieldName'] ?? '');
                $fieldValue = $field['fieldValue'] ?? '';
                if (stripos($fieldName, 'iban') !== false) {
                    $details['iban'] = $fieldValue;
                }
            }
        }
    }
    
    // Check for payAccount field (per SAPI v7.4 documentation)
    // payAccount might contain the payment account details (IBAN, account number, etc.)
    if (isset($orderData['payAccount']) && !empty($orderData['payAccount'])) {
        $payAccount = $orderData['payAccount'];
        
        // If payAccount is a string, it might be the IBAN directly
        if (is_string($payAccount)) {
            if (preg_match('/^[A-Z]{2}\d{2}/', strtoupper($payAccount))) {
                // It's an IBAN
                $details['iban'] = strtoupper(str_replace(' ', '', $payAccount));
                error_log("Found IBAN in payAccount field: " . substr($details['iban'], 0, 4) . "****");
            } else {
                // Might be account number or other payment info
                $details['account'] = $payAccount;
                error_log("Found account in payAccount field: " . substr($payAccount, 0, 10) . "...");
            }
        } 
        // If payAccount is an array, it might contain structured payment data
        elseif (is_array($payAccount)) {
            error_log("Found payAccount as array with keys: " . implode(', ', array_keys($payAccount)));
            
            // Check for IBAN in payAccount array
            foreach (['iban', 'IBAN', 'account', 'accountNumber', 'account_number'] as $key) {
                if (isset($payAccount[$key]) && !empty($payAccount[$key])) {
                    $value = $payAccount[$key];
                    if (preg_match('/^[A-Z]{2}\d{2}/', strtoupper($value))) {
                        // Remove ALL whitespace characters including non-breaking spaces
                        $iban = strtoupper(preg_replace('/\s+/', '', $value));
                        $iban = str_replace(["\xC2\xA0", "\u{00A0}"], '', $iban);
                        if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $iban)) {
                            $details['iban'] = $iban;
                            error_log("Found IBAN in payAccount[$key]: " . substr($details['iban'], 0, 4) . "****");
                            break;
                        }
                    }
                }
            }
            
            // Merge other payAccount fields
            $details = array_merge($details, $payAccount);
        }
    }
    
    // Also check payAccount in nested structures
    if (isset($orderData['buyer']) && is_array($orderData['buyer']) && isset($orderData['buyer']['payAccount'])) {
        $payAccount = $orderData['buyer']['payAccount'];
        if (is_string($payAccount) && preg_match('/^[A-Z]{2}\d{2}/', strtoupper($payAccount))) {
            // Remove ALL whitespace characters including non-breaking spaces
            $iban = strtoupper(preg_replace('/\s+/', '', $payAccount));
            $iban = str_replace(["\xC2\xA0", "\u{00A0}"], '', $iban);
            if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $iban)) {
                $details['iban'] = $iban;
                error_log("Found IBAN in buyer.payAccount: " . substr($details['iban'], 0, 4) . "****");
            }
        } elseif (is_array($payAccount)) {
            $details = array_merge($details, $payAccount);
        }
    }
    
    // Fallback: Check for direct fields (case-insensitive)
    $ibanFields = ['iban', 'IBAN', 'accountNumber', 'account_number', 'bankAccount', 'bank_account', 'payAccount', 'pay_account'];
    foreach ($ibanFields as $field) {
        if (isset($orderData[$field]) && !empty($orderData[$field]) && !isset($details['iban'])) {
            $value = $orderData[$field];
            // Only use if it looks like an IBAN
            if (preg_match('/^[A-Z]{2}\d{2}/', strtoupper($value))) {
                // Remove ALL whitespace characters including non-breaking spaces
                $iban = strtoupper(preg_replace('/\s+/', '', $value));
                $iban = str_replace(["\xC2\xA0", "\u{00A0}"], '', $iban);
                if (preg_match('/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/', $iban)) {
                    $details['iban'] = $iban;
                    error_log("Found IBAN in direct field: $field");
                    break;
                }
            }
        }
    }
    
    // Fallback: Bank name from payType or tradeMethodName
    if (!isset($details['bank_name'])) {
        // First try direct fields
        $bankFields = ['bankName', 'bank_name', 'bank', 'bankNameText', 'bankNameTextValue'];
        foreach ($bankFields as $field) {
            if (isset($orderData[$field]) && !empty($orderData[$field])) {
                $details['bank_name'] = $orderData[$field];
                break;
            }
        }
        
        // If still not found, try extracting from payType or tradeMethodName
        if (!isset($details['bank_name'])) {
            // Check payType field (e.g., "BBVABank" -> "BBVA")
            if (isset($orderData['payType']) && !empty($orderData['payType'])) {
                $payType = $orderData['payType'];
                // Remove common suffixes like "Bank", "bank", etc.
                $bankName = preg_replace('/\s*(bank|Bank|BANK)\s*$/i', '', $payType);
                if (!empty($bankName)) {
                    $details['bank_name'] = trim($bankName);
                    error_log("Extracted bank name from payType: " . $details['bank_name']);
                }
            }
            
            // Check tradeMethodName in payMethods array
            if (!isset($details['bank_name']) && isset($orderData['payMethods']) && is_array($orderData['payMethods'])) {
                foreach ($orderData['payMethods'] as $payMethod) {
                    if (isset($payMethod['tradeMethodName']) && !empty($payMethod['tradeMethodName'])) {
                        $tradeMethodName = $payMethod['tradeMethodName'];
                        // Remove common suffixes
                        $bankName = preg_replace('/\s*(bank|Bank|BANK|transfer|Transfer)\s*$/i', '', $tradeMethodName);
                        if (!empty($bankName)) {
                            $details['bank_name'] = trim($bankName);
                            error_log("Extracted bank name from tradeMethodName: " . $details['bank_name']);
                            break;
                        }
                    }
                    // Also check identifier field
                    if (!isset($details['bank_name']) && isset($payMethod['identifier']) && !empty($payMethod['identifier'])) {
                        $identifier = $payMethod['identifier'];
                        // Remove common suffixes
                        $bankName = preg_replace('/\s*(bank|Bank|BANK|transfer|Transfer)\s*$/i', '', $identifier);
                        if (!empty($bankName)) {
                            $details['bank_name'] = trim($bankName);
                            error_log("Extracted bank name from identifier: " . $details['bank_name']);
                            break;
                        }
                    }
                }
            }
        }
        
        // Final fallback: Use default "revolut" if still not found
        if (!isset($details['bank_name']) || empty($details['bank_name'])) {
            $details['bank_name'] = 'revolut';
            error_log("Bank name not found in order data, using default: revolut");
        }
    }
    
    // Fallback: Account holder name
    if (!isset($details['name'])) {
        $nameFields = ['accountHolderName', 'account_holder_name', 'name', 'receiverName', 'receiver_name', 'holderName'];
        foreach ($nameFields as $field) {
            if (isset($orderData[$field]) && !empty($orderData[$field])) {
                $details['name'] = $orderData[$field];
                break;
            }
        }
    }
    
    // Log extracted details (without sensitive data)
    if (!empty($details)) {
        $logDetails = $details;
        if (isset($logDetails['iban'])) {
            $logDetails['iban'] = substr($logDetails['iban'], 0, 4) . '****';
        }
        error_log("Extracted payment details: " . json_encode($logDetails));
    } else {
        error_log("WARNING: No payment details extracted. Full order structure: " . json_encode($orderData, JSON_PRETTY_PRINT));
    }
    
    return $details;
}

/**
 * Extract IBAN from chat messages as fallback
 * 
 * @param string $orderNo Binance order number
 * @param PDO $pdo Database connection
 * @param BinanceP2PSettings $settings Settings instance
 * @return string|null IBAN if found, null otherwise
 */
function extractIBANFromChat($orderNo, $pdo, $settings) {
    try {
        error_log("Attempting to extract IBAN from chat messages for order: $orderNo");
        
        // Get Binance API credentials
        $apiKey = $settings->getBinanceApiKey();
        $secretKey = $settings->getBinanceSecretKey();
        
        if (empty($apiKey) || empty($secretKey)) {
            error_log("Binance API credentials not available for chat message extraction");
            return null;
        }
        
        $binance = new BinanceP2PAPI($apiKey, $secretKey);
        
        // Fetch chat messages (try multiple pages to get all messages)
        $maxPages = 1; // Check up to 1 pages (100 messages)
        $allMessages = [];
        
        for ($page = 1; $page <= $maxPages; $page++) {
            $chatResponse = $binance->getChatMessages($orderNo, $page, 10);
            
            if (isset($chatResponse['error'])) {
                error_log("Error fetching chat messages (page $page): " . $chatResponse['error']);
                break; // Stop if error occurs
            }
            
            $messages = $chatResponse['data']['messages'] ?? $chatResponse['messages'] ?? $chatResponse['data'] ?? [];
            
            if (empty($messages) || !is_array($messages)) {
                error_log("No messages found on page $page");
                break; // No more messages
            }
            
            $allMessages = array_merge($allMessages, $messages);
            error_log("Fetched " . count($messages) . " messages from page $page (total so far: " . count($allMessages) . ")");
            
            // If we got fewer than requested, we've reached the end
            if (count($messages) < 10) {
                break;
            }
        }
        
        if (empty($allMessages)) {
            error_log("No chat messages found for order $orderNo");
            return null;
        }
        
        error_log("Total messages to search: " . count($allMessages));
        
        // Search through all messages for IBAN patterns
        $ibanPatterns = [
            // Standard IBAN pattern: 2 letters + 2 digits + up to 30 alphanumeric
            '/\b([A-Z]{2}[0-9]{2}[A-Z0-9]{4,30})\b/i',
            // IBAN with spaces: 2 letters + 2 digits + groups of 4 characters
            '/\b([A-Z]{2}[0-9]{2}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4,12})\b/i',
            // More flexible: starts with 2 letters, then 2 digits, then alphanumeric
            '/\b([A-Z]{2}[0-9]{2}[A-Z0-9\s]{10,34})\b/i',
        ];
        
        $foundIBANs = [];
        
        foreach ($allMessages as $message) {
            // Get message content
            $content = $message['content'] ?? $message['message'] ?? $message['text'] ?? '';
            $sender = $message['self'] ?? false;
            
            // Only check messages from buyer (not from us)
            if ($sender === true) {
                continue; // Skip our own messages
            }
            
            if (empty($content)) {
                continue;
            }
            
            error_log("Checking message from buyer: " . substr($content, 0, 10));
            
            // Try each IBAN pattern
            foreach ($ibanPatterns as $pattern) {
                if (preg_match_all($pattern, $content, $matches)) {
                    foreach ($matches[1] as $potentialIBAN) {
                        // Clean the IBAN (remove spaces)
                        $cleanedIBAN = strtoupper(preg_replace('/\s+/', '', trim($potentialIBAN)));
                        
                        // Basic validation: should be 15-34 characters
                        if (strlen($cleanedIBAN) >= 15 && strlen($cleanedIBAN) <= 34) {
                            // Validate using IBANValidator
                            $validation = IBANValidator::validateIBAN($cleanedIBAN);
                            
                            if ($validation['valid']) {
                                error_log("✓ Found valid IBAN in chat: " . substr($cleanedIBAN, 0, 4) . "**** (from message: " . substr($content, 0, 50) . "...)");
                                $foundIBANs[] = [
                                    'iban' => $cleanedIBAN,
                                    'message' => substr($content, 0, 10),
                                    'validated' => true
                                ];
                            } else {
                                error_log("Found potential IBAN but validation failed: " . substr($cleanedIBAN, 0, 4) . "**** - " . ($validation['error'] ?? 'Unknown error'));
                            }
                        }
                    }
                }
            }
        }
        
        if (!empty($foundIBANs)) {
            // Return the first valid IBAN found
            $firstIBAN = $foundIBANs[0]['iban'];
            error_log("Returning IBAN extracted from chat: " . substr($firstIBAN, 0, 4) . "****");
            return $firstIBAN;
        }
        
        error_log("No valid IBAN found in chat messages");
        return null;
        
    } catch (Exception $e) {
        error_log("Exception extracting IBAN from chat: " . $e->getMessage());
        error_log("Stack trace: " . $e->getTraceAsString());
        return null;
    }
}

/**
 * Create or get external account for buyer
 */
function createOrGetExternalAccount($bridge, $customerId, $iban, $name, $bankName, $countryCode3, $pdo, $orderId) {
    try {
        // Ensure IBAN is a string
        if (!is_string($iban)) {
            $iban = (string)$iban;
        }
        
        // Check if external account already exists for this IBAN
        $existingAccounts = $bridge->getExternalAccounts($customerId);
        
        if (isset($existingAccounts['data']) && is_array($existingAccounts['data'])) {
            foreach ($existingAccounts['data'] as $account) {
                if (isset($account['iban']) && is_string($account['iban']) && strtoupper(str_replace(' ', '', $account['iban'])) === $iban) {
                    error_log("Reusing existing external account: " . $account['id']);
                    // Return translated name from existing account if available
                    $existingTranslatedName = $account['account_owner_name'] ?? $name;
                    return ['account_id' => $account['id'], 'translated_name' => $existingTranslatedName];
                }
            }
        }
        
        // Create new external account
        // Bridge API expects specific structure for SEPA accounts
        // Translate full name to English first (if needed)
        $translatedName = IBANValidator::translateToEnglish($name);
        
        // Split name into first_name and last_name
        $nameParts = explode(' ', trim($translatedName), 2);
        $firstName = $nameParts[0] ?? '';
        $lastName = isset($nameParts[1]) ? $nameParts[1] : '';
        
        // Bridge API requires first_name to be non-empty
        // If only one name part, use it as first_name and also as last_name
        if (empty($firstName) && !empty($lastName)) {
            $firstName = $lastName;
        } elseif (empty($lastName) && !empty($firstName)) {
            // Single word name - use it for both first_name and last_name
            $lastName = $firstName;
        } elseif (empty($firstName) && empty($lastName)) {
            // Fallback: use full name for both if somehow both are empty
            $firstName = $translatedName;
            $lastName = $translatedName;
        }
        
        // Translate first_name and last_name separately to ensure they're in English
        $translatedFirstName = IBANValidator::translateToEnglish($firstName);
        $translatedLastName = IBANValidator::translateToEnglish($lastName);
        
        error_log("Name translation: Original='$name' -> Translated='$translatedName'");
        error_log("  First name: '$firstName' -> '$translatedFirstName'");
        error_log("  Last name: '$lastName' -> '$translatedLastName'");
        
        // Check if name was actually translated (different from original)
        $nameWasTranslated = (strtolower(trim($translatedName)) !== strtolower(trim($name))) || 
                            (strtolower(trim($translatedFirstName)) !== strtolower(trim($firstName))) ||
                            (strtolower(trim($translatedLastName)) !== strtolower(trim($lastName)));
        
        $accountData = [
            'currency' => 'eur', // Required by Bridge API
            'account_owner_type' => 'individual', // Required
            'first_name' => $translatedFirstName, // Use translated first name
            'last_name' => $translatedLastName, // Use translated last name
            'account_owner_name' => $translatedName, // Full translated name
            'bank_name' => $bankName,
            'account_type' => 'iban', // Required for SEPA accounts
            'iban' => [
                'account_number' => $iban, // IBAN as account_number
                'country' => $countryCode3 // 3-letter country code (e.g., ESP, DEU, FRA)
            ]
        ];
        
        error_log("Creating external account: " . json_encode($accountData));
        
        $result = $bridge->createExternalAccount($customerId, $accountData);
        
        if (isset($result['error'])) {
            error_log("Error creating external account: " . $result['error']);
            if (isset($result['errors'])) {
                error_log("Bridge API Error Details: " . $result['errors']);
            }
            if (isset($result['full_response'])) {
                error_log("Bridge API Full Error Response: " . json_encode($result['full_response']));
            }
            return ['account_id' => null, 'translated_name' => $translatedName];
        }
        
        $externalAccountId = $result['id'] ?? null;
        
        if ($externalAccountId) {
            error_log("Created external account: $externalAccountId");
        }
        
        // Return both account ID and final translated name that was sent to Bridge
        return ['account_id' => $externalAccountId, 'translated_name' => $translatedName];
        
    } catch (Exception $e) {
        error_log("Exception creating external account: " . $e->getMessage());
        return null;
    }
}

/**
 * Create liquidation address
 */
function createLiquidationAddress($bridge, $customerId, $externalAccountId, $currency, $pdo, $orderId) {
    try {
        // Check for existing active liquidation address
        $existingAddresses = $bridge->getCustomerLiquidationAddresses($customerId);
        
        if (isset($existingAddresses['data']) && is_array($existingAddresses['data'])) {
            foreach ($existingAddresses['data'] as $address) {
            // Check if liquidation address matches: USDC on Polygon
            if (isset($address['external_account_id']) && 
                $address['external_account_id'] === (string)$externalAccountId &&
                isset($address['chain']) && strtolower($address['chain']) === 'polygon' &&
                isset($address['currency']) && strtolower($address['currency']) === 'usdc' &&
                isset($address['destination_payment_rail']) && $address['destination_payment_rail'] === 'sepa' &&
                isset($address['destination_currency']) && strtolower($address['destination_currency']) === strtolower($currency) &&
                isset($address['state']) && $address['state'] === 'active') {
                    error_log("Reusing existing liquidation address: " . $address['id']);
                    return $address;
                }
            }
        }
        
        // Create new liquidation address
        // Bridge API requirements for SEPA liquidation address:
        // - chain: "polygon" (USDC on Polygon network)
        // - currency: "usdc" 
        // - external_account_id: ID from external account creation
        // - destination_payment_rail: "sepa"
        // - destination_currency: "eur"
        $liquidationData = [
            'chain' => 'polygon',  // Polygon network (USDC on Polygon)
            'currency' => 'usdc',   // USDC currency
            'external_account_id' => (string)$externalAccountId, // External account ID created first
            'destination_payment_rail' => 'sepa', // SEPA payment rail
            'destination_currency' => strtolower($currency), // EUR (must match external account currency)
            'custom_developer_fee_percent' => '0.6' // 0.6% Bridge fee
        ];
        
        error_log("Creating liquidation address with Bridge API requirements: " . json_encode($liquidationData));
        
        $result = $bridge->createLiquidationAddress($customerId, $liquidationData);
        
        if (isset($result['error'])) {
            error_log("Error creating liquidation address: " . $result['error']);
            return null;
        }
        
        if (isset($result['address'])) {
            error_log("Created liquidation address: " . $result['address']);
        }
        
        return $result;
        
    } catch (Exception $e) {
        error_log("Exception creating liquidation address: " . $e->getMessage());
        return null;
    }
}


/**
 * Send USDC to liquidation address
 */
function sendUSDCToLiquidationAddress($fromAddress, $encryptedPrivateKey, $toAddress, $amount, $pdo, $orderId) {
    try {
        // Get base directory (same as parent function)
        $baseDir = dirname(__DIR__);
        
        // Only require if not already loaded
        if (!class_exists('USDCSender')) {
            require_once $baseDir . '/includes/usdc-sender.php';
        }
        
        $sender = new USDCSender();
        $result = $sender->sendUSDC($fromAddress, $encryptedPrivateKey, $toAddress, $amount);
        
        if (!$result['success']) {
            error_log("Failed to send USDC: " . ($result['error'] ?? 'Unknown error'));
            return null;
        }
        
        $txHash = $result['txHash'];
        error_log("USDC sent successfully. TX Hash: $txHash");
        
        // Update order with transaction hash
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET error_message = CONCAT(COALESCE(error_message, ''), ' | TX: ', ?),
                updated_at = NOW()
            WHERE id = ?
        ");
        $stmt->execute([$txHash, $orderId]);
        
        return $txHash;
        
    } catch (Exception $e) {
        error_log("Error sending USDC: " . $e->getMessage());
        return null;
    }
}

/**
 * Mark order for manual review
 */
function markForManualReview($pdo, $orderId, $orderNo, $reason) {
    $stmt = $pdo->prepare("
        UPDATE binance_p2p_orders 
        SET requires_manual_review = 1, 
            review_reason = ?,
            payment_status = 'pending',
            error_message = ?,
            updated_at = NOW()
        WHERE id = ?
    ");
    $stmt->execute([$reason, $reason, $orderId]);
    
    createNotification($pdo, 'payment_failed', 
        "Order Requires Manual Review", 
        "Order $orderNo requires manual review: " . $reason, 
        $orderNo);
    
    error_log("Order $orderNo marked for manual review: " . $reason);
}

/**
 * Mark order as paid on Binance
 */
function markOrderAsPaidOnBinance($orderNo, $pdo, $orderId, $settings) {
    try {
        error_log("markOrderAsPaidOnBinance called for order: $orderNo (ID: $orderId)");
        
        $apiKey = $settings->getBinanceApiKey();
        $secretKey = $settings->getBinanceSecretKey();
        
        if (empty($apiKey) || empty($secretKey)) {
            $error = "Binance API credentials not configured";
            error_log("ERROR: $error");
            throw new Exception($error);
        }
        
        error_log("Calling Binance API to mark order $orderNo as paid...");
        $binance = new BinanceP2PAPI($apiKey, $secretKey);
        $result = $binance->markOrderAsPaid($orderNo);
        
        error_log("Binance API response for markOrderAsPaid: " . json_encode($result));
        
        // Check for errors - handle null, empty, or error responses
        // Note: For merchantOrder endpoint, empty response with HTTP 200 is treated as success by signedRequest
        if ($result === null || (empty($result) && !isset($result['success']))) {
            $error = "Binance API returned null or empty response";
            error_log("ERROR: $error");
            
            $stmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET error_message = CONCAT(COALESCE(error_message, ''), ' | Binance API Error: ', ?),
                    updated_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([$error, $orderId]);
            
            return ['success' => false, 'error' => $error];
        }
        
        if (isset($result['error'])) {
            $error = $result['error'];
            error_log("ERROR: Binance API returned error: $error");
            
            $stmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET error_message = CONCAT(COALESCE(error_message, ''), ' | Binance API Error: ', ?),
                    updated_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([$error, $orderId]);
            
            return ['success' => false, 'error' => $error];
        }
        
        // Check for Binance API error code in response (even if HTTP 200)
        if (isset($result['code']) && $result['code'] !== '000000' && $result['code'] !== 0) {
            $error = isset($result['msg']) ? $result['msg'] : (isset($result['message']) ? $result['message'] : 'Binance API error code: ' . $result['code']);
            error_log("ERROR: Binance API returned error code: " . $result['code'] . " - $error");
            
            $stmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET error_message = CONCAT(COALESCE(error_message, ''), ' | Binance API Error: ', ?),
                    updated_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([$error, $orderId]);
            
            return ['success' => false, 'error' => $error];
        }
        
        // Verify success - Binance API should return code "000000" or success indicator
        $isSuccess = false;
        if (isset($result['code']) && ($result['code'] === '000000' || $result['code'] === 0)) {
            $isSuccess = true;
        } elseif (isset($result['success']) && $result['success'] === true) {
            $isSuccess = true;
        } elseif (isset($result['data']) || (is_array($result) && !isset($result['error']) && !isset($result['code']))) {
            // If response has data or is an array without error/code, assume success
            $isSuccess = true;
        }
        
        if (!$isSuccess) {
            $error = "Binance API response does not indicate success: " . json_encode($result);
            error_log("ERROR: $error");
            
            $stmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET error_message = CONCAT(COALESCE(error_message, ''), ' | Binance API Error: ', ?),
                    updated_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([$error, $orderId]);
            
            return ['success' => false, 'error' => $error];
        }
        
        // Success - update database
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET marked_paid_at = NOW(),
                order_status = 'BUYER_PAYED',
                updated_at = NOW()
            WHERE id = ?
        ");
        $stmt->execute([$orderId]);
        
        error_log("SUCCESS: Order $orderNo marked as paid on Binance (response: " . json_encode($result) . ")");
        
        return ['success' => true];
        
    } catch (Exception $e) {
        $error = "Error marking order as paid: " . $e->getMessage();
        error_log("EXCEPTION: $error");
        error_log("Stack trace: " . $e->getTraceAsString());
        
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET error_message = CONCAT(COALESCE(error_message, ''), ' | Mark Paid Error: ', ?),
                updated_at = NOW()
            WHERE id = ?
        ");
        $stmt->execute([$e->getMessage(), $orderId]);
        
        return ['success' => false, 'error' => $e->getMessage()];
    }
}

/**
 * Send payment confirmation message
 */
function sendPaymentConfirmationMessage($orderNo, $amount, $currency, $pdo, $settings) {
    try {
        $message = "Payment of " . number_format($amount, 2) . " " . strtoupper($currency) . " has been sent. Please wait 15 minutes to receive money if your bank supports SEPA Instant.";
        
        $stmt = $pdo->prepare("
            UPDATE binance_p2p_orders 
            SET chat_messages_sent = 1,
                last_message_sent_at = NOW(),
                updated_at = NOW()
            WHERE order_no = ?
        ");
        $stmt->execute([$orderNo]);
        
        error_log("Payment confirmation message prepared for order $orderNo");
        
    } catch (Exception $e) {
        error_log("Error preparing chat message: " . $e->getMessage());
    }
}

/**
 * Update order with error
 */
function updateOrderError($pdo, $orderId, $error) {
    $stmt = $pdo->prepare("
        UPDATE binance_p2p_orders 
        SET error_message = ?,
            payment_status = 'failed',
            updated_at = NOW()
        WHERE id = ?
    ");
    $stmt->execute([$error, $orderId]);
}

/**
 * Create notification
 */
function createNotification($pdo, $type, $title, $message, $orderNo = null) {
    try {
        $stmt = $pdo->prepare("
            INSERT INTO binance_p2p_notifications 
            (notification_type, title, message, order_no, is_read, created_at)
            VALUES (?, ?, ?, ?, 0, NOW())
        ");
        $stmt->execute([$type, $title, $message, $orderNo]);
    } catch (Exception $e) {
        error_log("Error creating notification: " . $e->getMessage());
    }
}
