<?php
/**
 * Binance WebSocket Chat Handler
 * Sends messages to buyers via Binance WebSocket
 * 
 * Run this as a background process or via cron
 */

// Enable error display for CLI debugging
if (php_sapi_name() === 'cli') {
    ini_set('display_errors', '1');
    ini_set('log_errors', '1');
    error_reporting(E_ALL);
    ini_set('output_buffering', '0');
    ini_set('session.auto_start', '0');
    
    // Clear any existing output buffers
    while (ob_get_level() > 0) {
        ob_end_clean();
    }
} else {
    // For web requests, suppress output
    ini_set('display_errors', '0');
}

// Prevent execution via web browser (CLI only)
// Allow execution if NOT clearly a web request (more permissive check)
$isWebRequest = (
    isset($_SERVER['HTTP_HOST']) ||
    isset($_SERVER['REQUEST_METHOD']) ||
    (isset($_SERVER['SERVER_SOFTWARE']) && php_sapi_name() !== 'cli')
);

if ($isWebRequest && php_sapi_name() !== 'cli') {
    // Only block if it's clearly a web request
    http_response_code(403);
    die('This script can only be run from command line.');
}

// Set base directory for includes
$baseDir = dirname(__DIR__);

// Temporarily disable session auto-start
$originalSessionAutoStart = ini_get('session.auto_start');
ini_set('session.auto_start', '0');

// Load required files with absolute paths
require_once $baseDir . '/includes/config.php';

// Clear any output buffer after loading config (CLI only)
if (php_sapi_name() === 'cli') {
    // Discard any output that was buffered (headers, etc.)
    while (ob_get_level() > 0) {
        ob_end_clean();
    }
    // Don't start a new buffer - we want immediate output
}
require_once $baseDir . '/includes/binance-api.php';
require_once $baseDir . '/includes/binance-settings.php';
require_once $baseDir . '/includes/functions.php';

// Keep error display enabled for CLI debugging
if (php_sapi_name() === 'cli') {
    ini_set('display_errors', '1');
    error_reporting(E_ALL);
}
ini_set('log_errors', '1');

set_time_limit(60); // Reduced from 300 to 60 seconds to prevent CPU overuse

// Add lock file to prevent multiple simultaneous executions
$lockFile = $baseDir . '/api/.websocket-chat.lock';
$cooldownFile = $baseDir . '/api/.websocket-chat-cooldown.lock';

// Check for ILLEGAL_PARAM cooldown - if we got ILLEGAL_PARAM recently, don't retry for 30 minutes
if (file_exists($cooldownFile)) {
    $cooldownTime = filemtime($cooldownFile);
    $cooldownAge = time() - $cooldownTime;
    
    // If cooldown is less than 30 minutes, exit immediately
    if ($cooldownAge < 1800) {
        $remainingMinutes = ceil((1800 - $cooldownAge) / 60);
        error_log("ILLEGAL_PARAM cooldown active (remaining: {$remainingMinutes} minutes). Exiting to prevent CPU overuse.");
        error_log("This means Binance rejected the connection. Check credentials before retrying.");
        exit(0);
    } else {
        // Cooldown expired, remove it
        @unlink($cooldownFile);
        error_log("ILLEGAL_PARAM cooldown expired. Will attempt connection.");
    }
}

// Check if another instance is already running
if (file_exists($lockFile)) {
    $lockTime = filemtime($lockFile);
    $lockAge = time() - $lockTime;
    
    // If lock file is older than 2 minutes, consider it stale and remove it
    if ($lockAge > 120) {
        @unlink($lockFile);
        error_log("Removed stale lock file (age: {$lockAge}s)");
    } else {
        error_log("Another WebSocket chat handler is already running (lock age: {$lockAge}s). Exiting to prevent CPU overuse.");
        exit(0);
    }
}

// Create lock file with timestamp
file_put_contents($lockFile, time());

// Register shutdown function to remove lock file
register_shutdown_function(function() use ($lockFile) {
    if (file_exists($lockFile)) {
        @unlink($lockFile);
    }
});

// Immediate output to verify script is running
echo "=== Binance WebSocket Chat Handler Started ===\n";
flush();
error_log("=== Binance WebSocket Chat Handler Started ===");

try {
    echo "Loading settings...\n";
    flush();
    $settings = new BinanceP2PSettings($pdo);
    echo "✓ Settings loaded\n";
    flush();
    
    // Get Binance API credentials
    echo "Getting API credentials...\n";
    flush();
    $apiKey = $settings->getBinanceApiKey();
    $secretKey = $settings->getBinanceSecretKey();
    
    if (empty($apiKey) || empty($secretKey)) {
        echo "ERROR: Binance API credentials not configured!\n";
        error_log("ERROR: Binance API credentials not configured!");
        exit(1);
    }
    
    echo "✓ API credentials loaded\n";
    flush();
    
    // IMPORTANT: Check for pending orders FIRST before getting credentials
    // This prevents unnecessary API calls and WebSocket connections
    echo "Checking for pending orders...\n";
    flush();
    
    $stmt = $pdo->prepare("
        SELECT order_no, fiat_amount, fiat_currency, order_status, proof_image_url, 
               chat_messages_sent, last_message_sent_at
        FROM binance_p2p_orders 
        WHERE order_status = 'BUYER_PAYED'
        AND chat_messages_sent = 0
        AND (last_message_sent_at IS NULL OR last_message_sent_at < DATE_SUB(NOW(), INTERVAL 1 MINUTE))
        ORDER BY updated_at DESC
        LIMIT 10
    ");
    $stmt->execute();
    $ordersToMessage = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    if (empty($ordersToMessage)) {
        echo "No pending messages to send - exiting immediately to prevent CPU overuse\n";
        error_log("No pending messages to send - exiting immediately");
        exit(0); // Exit BEFORE getting credentials to prevent unnecessary API calls
    }
    
    echo "Found " . count($ordersToMessage) . " orders to send messages for\n";
    flush();
    error_log("Found " . count($ordersToMessage) . " orders to send messages for");
    
    // Only get credentials if we have orders to process
    $binance = new BinanceP2PAPI($apiKey, $secretKey);
    
    // Get chat credentials
    echo "Getting chat credentials from Binance...\n";
    flush();
    $credentials = $binance->getChatCredentials();
    
    if (isset($credentials['error'])) {
        error_log("ERROR getting chat credentials: " . $credentials['error']);
        exit(1);
    }
    
    $wssUrl = $credentials['data']['chatWssUrl'] ?? null;
    $listenKey = $credentials['data']['listenKey'] ?? null;
    $listenToken = $credentials['data']['listenToken'] ?? null;
    
    if (empty($wssUrl) || empty($listenKey) || empty($listenToken)) {
        error_log("ERROR: Invalid chat credentials");
        exit(1);
    }
    
    error_log("Using token as-is from Binance API (length: " . strlen($listenToken) . ")");
    
    // Load WebSocket client
    require_once $baseDir . '/includes/websocket-client.php';
    
    // Connect to WebSocket - ONE ATTEMPT ONLY to prevent CPU overuse
    // If ILLEGAL_PARAM occurs, exit immediately - DO NOT RETRY
    try {
        // Pass token as-is (Binance docs show token should include TOKEN prefix in URL)
        $wsClient = new BinanceWebSocketClient($wssUrl, $listenKey, $listenToken);
        
        // Set a timeout for connection attempt
        set_time_limit(15);
        
        // Attempt connection - this will throw exception on ILLEGAL_PARAM
        try {
            $wsClient->connect();
        } catch (\Exception $e) {
            // If ILLEGAL_PARAM exception is thrown, exit immediately - NO RETRIES
            if (strpos($e->getMessage(), 'ILLEGAL_PARAM') !== false) {
                // Create cooldown file to prevent retries for 30 minutes
                file_put_contents($cooldownFile, time());
                error_log("CRITICAL ERROR: ILLEGAL_PARAM exception caught. Creating 30-minute cooldown.");
                error_log("This means Binance is rejecting the connection. Possible causes:");
                error_log("  1. Token/listenKey format is incorrect");
                error_log("  2. Credentials expired (get fresh credentials from Binance API)");
                error_log("  3. Multiple simultaneous connections not allowed");
                error_log("  4. Binance API may have changed requirements");
                error_log("ACTION REQUIRED: Check Binance API credentials and get fresh chat credentials.");
                error_log("Script will not retry for 30 minutes to prevent CPU overuse.");
                // Exit immediately - do not retry, do not continue
                exit(1);
            }
            // Re-throw other exceptions
            throw $e;
        }
        
        // Double-check for ILLEGAL_PARAM error after connection (shouldn't reach here if exception was thrown)
        $lastError = $wsClient->getLastError();
        if ($lastError && strpos($lastError, 'ILLEGAL_PARAM') !== false) {
            // Create cooldown file to prevent retries for 30 minutes
            file_put_contents($cooldownFile, time());
            error_log("CRITICAL ERROR: ILLEGAL_PARAM detected after connection. Creating 30-minute cooldown.");
            error_log("ACTION REQUIRED: Check Binance API credentials and get fresh chat credentials.");
            error_log("Script will not retry for 30 minutes to prevent CPU overuse.");
            exit(1);
        }
        
        $connected = $wsClient->isConnected();
        
        if (!$connected) {
            error_log("ERROR: WebSocket connection failed. Exiting to prevent CPU overuse.");
            exit(1);
        }
        
        // Check for immediate errors
        $immediateError = $wsClient->getLastError();
        if ($immediateError) {
            error_log("⚠️ WARNING: Error detected immediately after connection: $immediateError");
        }
        
        error_log("✓ WebSocket connected successfully");
        
        // Wait a moment for connection to fully stabilize
        error_log("Waiting 3 seconds for connection to stabilize...");
        sleep(3);
        
        // Double-check connection status
        if (!$wsClient->isConnected()) {
            error_log("ERROR: WebSocket connection lost after stabilization period");
            exit(1);
        }
        
        // Send messages for each order
        foreach ($ordersToMessage as $order) {
            $orderNo = $order['order_no'];
            $amount = $order['fiat_amount'];
            $currency = $order['fiat_currency'];
            $proofImageUrl = $order['proof_image_url'] ?? null;
            
            // Prepare messages (English and Spanish)
            $englishMessage = "✅ PAYMENT PROCESSED - ZATAM AUTOPAY BOT\n\n" .
                "Your payment has been successfully sent.\n\n" .
                "⏱ PROCESSING TIME:\n\n" .
                "• If your bank supports SEPA Instant: Up to 15 minutes\n" .
                "• Standard SEPA transfer: Up to 3 business days (typically 1 business day)\n\n" .
                "📋 NEXT STEPS:\n\n" .
                "1. Check your bank account for the funds\n" .
                "2. Verify the payment has been received\n" .
                "3. Release the USDT/USDC only after confirming funds in your account\n\n" .
                "⚠️ IMPORTANT REMINDER:\n\n" .
                "• I am an automated system\n" .
                "• I cannot read or respond to chat messages\n" .
                "• Please release only after you have confirmed the funds in your bank account\n" .
                "• Do NOT release based solely on this message\n\n" .
                "⭐ Thank you for your patience. Positive feedback is appreciated after completion!";
            
            $spanishMessage = "✅ PAGO PROCESADO - ZATAM AUTOPAY BOT\n\n" .
                "Su pago ha sido enviado exitosamente a través.\n\n" .
                "⏱ TIEMPO DE PROCESAMIENTO:\n\n" .
                "• Si su banco soporta SEPA Instant: Hasta 15 minutos\n" .
                "• Transferencia SEPA estándar: Hasta 3 días hábiles (normalmente 1 día hábil)\n\n" .
                "📋 PRÓXIMOS PASOS:\n\n" .
                "1. Verifique su cuenta bancaria por los fondos\n" .
                "2. Confirme que el pago ha sido recibido\n" .
                "3. Libere el USDT/USDC solo después de confirmar los fondos en su cuenta\n\n" .
                "⚠️ RECORDATORIO IMPORTANTE:\n\n" .
                "• Soy un sistema automatizado\n" .
                "• No puedo leer ni responder mensajes en el chat\n" .
                "• Por favor, libere solo después de confirmar los fondos en su cuenta bancaria\n" .
                "• NO libere basándose únicamente en este mensaje\n\n" .
                "⭐ Gracias por su paciencia. ¡Los comentarios positivos son apreciados después de completar!";
            
            // Send English message first
            error_log("Sending English message for order $orderNo");
            $sentEnglish = $wsClient->sendMessage($orderNo, $englishMessage);
            
            // Wait to check for errors (event loop processes responses)
            sleep(3);
            $englishError = $wsClient->getLastError();
            
            if ($sentEnglish && !$englishError) {
                error_log("✓ English message sent successfully for order $orderNo");
                
                // NOTE: Proof image sending is disabled - images appear as links, not images
                // Will be re-enabled once Binance image format is fixed
                // if (!empty($proofImageUrl)) {
                //     sleep(2);
                //     $sentImage = $wsClient->sendImage($orderNo, $proofImageUrl);
                //     if ($sentImage) {
                //         error_log("Proof of payment image sent successfully for order $orderNo");
                //     } else {
                //         error_log("Failed to send proof image for order $orderNo");
                //     }
                // }
                
                // Wait 3 seconds before sending Spanish message
                sleep(3);
                error_log("Sending Spanish message for order $orderNo");
                $sentSpanish = $wsClient->sendMessage($orderNo, $spanishMessage);
                
                // Wait to check for errors
                sleep(3);
                $spanishError = $wsClient->getLastError();
                
                if ($sentSpanish && !$spanishError) {
                    error_log("✓ Spanish message sent successfully for order $orderNo");
                    
                    // Update last message sent time and mark as sent
                    $stmt = $pdo->prepare("
                        UPDATE binance_p2p_orders 
                        SET last_message_sent_at = NOW(),
                            chat_messages_sent = 1,
                            updated_at = NOW()
                        WHERE order_no = ?
                    ");
                    $stmt->execute([$orderNo]);
                    error_log("✓ Order $orderNo marked as messages sent in database");
                } else {
                    $errorMsg = $spanishError ?: 'Unknown error';
                    error_log("✗ Failed to send Spanish message for order $orderNo: $errorMsg (will retry next run)");
                    // Don't mark as sent if Spanish message failed
                }
            } else {
                $errorMsg = $englishError ?: 'Unknown error';
                error_log("✗ Failed to send English message for order $orderNo: $errorMsg (will retry next run)");
            }
        }
        
        // Close connection
        $wsClient->close();
        
    } catch (Exception $e) {
        error_log("FATAL ERROR in WebSocket chat: " . $e->getMessage());
        error_log("Stack trace: " . $e->getTraceAsString());
        
        // Mark messages for retry
        foreach ($ordersToMessage as $order) {
            $stmt = $pdo->prepare("
                UPDATE binance_p2p_orders 
                SET last_message_sent_at = NULL,
                    updated_at = NOW()
                WHERE order_no = ?
            ");
            $stmt->execute([$order['order_no']]);
        }
    }
    
    echo "=== Binance WebSocket Chat Handler Completed ===\n";
    error_log("=== Binance WebSocket Chat Handler Completed ===");
    
} catch (Exception $e) {
    echo "FATAL ERROR: " . $e->getMessage() . "\n";
    echo "Stack trace: " . $e->getTraceAsString() . "\n";
    error_log("FATAL ERROR in WebSocket chat handler: " . $e->getMessage());
    error_log("Stack trace: " . $e->getTraceAsString());
    exit(1);
}

/**
 * Note: Full WebSocket implementation requires:
 * 1. WebSocket client library (e.g., Ratchet, ReactPHP WebSocket)
 * 2. Persistent connection handling
 * 3. Message queuing system
 * 4. Reconnection logic
 * 
 * For production, consider:
 * - Using a message queue (Redis, RabbitMQ)
 * - Running WebSocket client as a daemon/service
 * - Implementing proper error handling and reconnection
 */
