<?php
/**
 * Process pending order after manual payment confirmation
 * - Deducts balance from store owner
 * - Sends order to external API (if enabled)
 * - Updates order status
 */

session_start();
require_once 'config.php';
require_once 'app/classes/StoreManager.php';
require_once 'app/classes/ExternalAPIService.php';
require_once 'app/classes/BalanceHistory.php';
require_once 'app/classes/ExternalAPIPricingManager.php';

use App\classes\ExternalAPIPricingManager;

header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => 'Method not allowed']);
    exit;
}

// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
    echo json_encode(['success' => false, 'message' => 'Authentication required']);
    exit;
}

$userId = $_SESSION['user_id'];
$orderId = intval($_POST['order_id'] ?? 0);

if ($orderId <= 0) {
    echo json_encode(['success' => false, 'message' => 'Invalid order ID']);
    exit;
}

// Verify user owns a store
$storeManager = new StoreManager($conn);
$store = $storeManager->getStoreByOwnerId($userId);

if (!$store) {
    echo json_encode(['success' => false, 'message' => 'Store not found']);
    exit;
}

// CRITICAL: Check if store is active - cannot process orders for inactive stores
if ($store['status'] !== 'active') {
    echo json_encode(['success' => false, 'message' => 'Store is currently inactive and not accepting orders']);
    exit;
}

// Helper function to get admin price if ExternalAPIPricingManager doesn't have it
function getAdminPriceForPackage($conn, $packageSize, $networkType = 'mtn') {
    // Parse package size to GB
    $packageSizeGb = 0;
    if (preg_match('/(\d+(?:\.\d+)?)\s*GB/i', $packageSize, $matches)) {
        $packageSizeGb = (float)$matches[1];
    } elseif (preg_match('/(\d+(?:\.\d+)?)\s*MB/i', $packageSize, $matches)) {
        $packageSizeGb = round(((float)$matches[1]) / 1024, 4);
    } else {
        $num = floatval(preg_replace('/[^0-9.]/', '', $packageSize));
        $packageSizeGb = $num > 0 ? $num : 0;
    }
    
    $stmt = $conn->prepare("
        SELECT price_per_gb FROM agent_pricing_rules 
        WHERE network_type = ? AND role = 'agent' AND is_active = 1 
        AND min_size_gb <= ? AND max_size_gb >= ?
        ORDER BY min_size_gb DESC
        LIMIT 1
    ");
    $stmt->bind_param("sdd", $networkType, $packageSizeGb, $packageSizeGb);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    $stmt->close();
    
    if ($row) {
        return round($row['price_per_gb'] * $packageSizeGb, 2);
    }
    return null;
}

// Check if API should be used for this network
function shouldUseExternalAPI($networkType) {
    global $conn;
    $stmt = $conn->prepare("SELECT config_value FROM payment_config WHERE config_key = 'api_enabled'");
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    $stmt->close();
    
    $apiEnabled = $row && (bool)$row['config_value'];
    
    // Only use external API for MTN currently
    return $apiEnabled && $networkType === 'mtn';
}

// Start transaction FIRST to prevent race conditions
$conn->begin_transaction();

try {
    // CRITICAL: Atomically update status to 'processing' to prevent duplicate processing
    // This UPDATE will only succeed if status is still 'pending', ensuring only one request can process it
    $lockOrderStmt = $conn->prepare("
        UPDATE orders 
        SET status = 'processing', 
            status_updated_at = NOW()
        WHERE id = ? AND store_id = ? AND status = 'pending'
    ");
    $lockOrderStmt->bind_param("ii", $orderId, $store['id']);
    $lockOrderStmt->execute();
    $affectedRows = $lockOrderStmt->affected_rows;
    $lockOrderStmt->close();
    
    // If no rows were affected, the order was already processed or doesn't exist
    if ($affectedRows === 0) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => 'Order not found or already being processed']);
        exit;
    }
    
    // Now safely get order details (status is already 'processing', so no race condition)
    $orderStmt = $conn->prepare("
        SELECT 
            o.id,
            o.order_reference,
            o.agent_id,
            o.store_id,
            o.network_type,
            o.package_size,
            o.beneficiary_number,
            o.amount,
            o.status
        FROM orders o
        WHERE o.id = ? AND o.store_id = ?
    ");
    $orderStmt->bind_param("ii", $orderId, $store['id']);
    $orderStmt->execute();
    $orderResult = $orderStmt->get_result();
    $order = $orderResult->fetch_assoc();
    $orderStmt->close();
    
    if (!$order) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => 'Order not found']);
        exit;
    }
    
    // Customer paid store owner's price (stored in order amount)
    $customerPaidAmount = floatval($order['amount']); // Store owner's price (what customer paid)
    
    // Check if order is from a sub-agent (not the store owner)
    $orderAgentId = $order['agent_id'];
    $storeOwnerId = $store['owner_id'];
    $isSubAgent = ($orderAgentId != $storeOwnerId);
    
    // Calculate pricing for both sub-agent (if applicable) and store owner
    $subAgentAmount = 0;
    $storeOwnerAmount = 0;
    
    // Always calculate admin base pricing for store owner (what store owner pays)
    $externalPricingManager = new ExternalAPIPricingManager($conn);
    $adminPrice = $externalPricingManager->getAdminPrice($order['package_size'], $order['network_type']);
    
    // If admin price not found, try helper function
    if ($adminPrice === null) {
        $adminPrice = getAdminPriceForPackage($conn, $order['package_size'], $order['network_type']);
    }
    
    // Store owner always pays admin base pricing
    $storeOwnerAmount = ($adminPrice !== null && $adminPrice > 0) ? $adminPrice : $customerPaidAmount;
    
    if ($isSubAgent) {
        // Sub-agent order: Use store owner's sub-agent pricing from store_pricing table
        require_once 'app/classes/StorePricingManager.php';
        $storePricingManager = new StorePricingManager($conn);
        $priceInfo = $storePricingManager->calculatePrice($order['store_id'], $order['package_size'], $order['network_type']);
        
        if ($priceInfo['price'] !== null && $priceInfo['price'] > 0) {
            $subAgentAmount = $priceInfo['price'];
        } else {
            // Fallback: if store pricing not found, use admin price
            $subAgentAmount = ($adminPrice !== null && $adminPrice > 0) ? $adminPrice : $customerPaidAmount;
        }
    }
    
    // Lock and check balances
    if ($isSubAgent) {
        // Lock and check sub-agent's balance
        $subAgentBalanceStmt = $conn->prepare("SELECT balance FROM agents WHERE id = ? FOR UPDATE");
        $subAgentBalanceStmt->bind_param("i", $orderAgentId);
        $subAgentBalanceStmt->execute();
        $subAgentBalanceResult = $subAgentBalanceStmt->get_result();
        $subAgentData = $subAgentBalanceResult->fetch_assoc();
        $subAgentBalanceStmt->close();
        
        $subAgentCurrentBalance = floatval($subAgentData['balance'] ?? 0);
        
        if ($subAgentCurrentBalance < $subAgentAmount) {
            $conn->rollback();
            echo json_encode([
                'success' => false,
                'message' => 'Insufficient balance (Sub-agent). Required: GH₵ ' . number_format($subAgentAmount, 2) . ', Available: GH₵ ' . number_format($subAgentCurrentBalance, 2)
            ]);
            exit;
        }
    }
    
    // Lock and check store owner's balance (always needed)
    $ownerBalanceStmt = $conn->prepare("SELECT balance FROM agents WHERE id = ? FOR UPDATE");
    $ownerBalanceStmt->bind_param("i", $storeOwnerId);
    $ownerBalanceStmt->execute();
    $ownerBalanceResult = $ownerBalanceStmt->get_result();
    $ownerData = $ownerBalanceResult->fetch_assoc();
    $ownerBalanceStmt->close();
    
    $ownerCurrentBalance = floatval($ownerData['balance'] ?? 0);
    
    if ($ownerCurrentBalance < $storeOwnerAmount) {
        $conn->rollback();
        echo json_encode([
            'success' => false,
            'message' => 'Insufficient balance (Store owner). Required: GH₵ ' . number_format($storeOwnerAmount, 2) . ', Available: GH₵ ' . number_format($ownerCurrentBalance, 2)
        ]);
        exit;
    }
    
    // Deduct sub-agent's balance (if sub-agent)
    if ($isSubAgent) {
        $subAgentNewBalance = round($subAgentCurrentBalance - $subAgentAmount, 2);
        $updateSubAgentStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
        $updateSubAgentStmt->bind_param("di", $subAgentNewBalance, $orderAgentId);
        if (!$updateSubAgentStmt->execute()) {
            $updateSubAgentStmt->close();
            throw new Exception("Failed to update sub-agent balance");
        }
        $updateSubAgentStmt->close();
        
        // Log sub-agent balance history
        try {
            \App\classes\BalanceHistory::log($conn, $orderAgentId, -1 * $subAgentAmount, 'order', $orderId, $order['beneficiary_number'], $subAgentCurrentBalance, $subAgentNewBalance);
        } catch (Exception $e) {
            error_log("CRITICAL: Failed to log sub-agent balance history for order $orderId: " . $e->getMessage());
        }
    }
    
    // Deduct store owner's balance (always at admin base pricing)
    $ownerNewBalance = round($ownerCurrentBalance - $storeOwnerAmount, 2);
    $updateOwnerStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
    $updateOwnerStmt->bind_param("di", $ownerNewBalance, $storeOwnerId);
    if (!$updateOwnerStmt->execute()) {
        $updateOwnerStmt->close();
        throw new Exception("Failed to update store owner balance");
    }
    $updateOwnerStmt->close();
    
    // Store newBalance for potential refunds (use store owner's balance for refunds)
    $balanceAfterDebit = $ownerNewBalance;
    
    // Log store owner balance history
    try {
        \App\classes\BalanceHistory::log($conn, $storeOwnerId, -1 * $storeOwnerAmount, 'order', $orderId, $order['beneficiary_number'], $ownerCurrentBalance, $ownerNewBalance);
    } catch (Exception $e) {
        error_log("CRITICAL: Failed to log store owner balance history for order $orderId: " . $e->getMessage());
    }
    
    // Process with external API if needed
    $apiResponse = null;
    $apiSuccess = false;
    
    if (shouldUseExternalAPI($order['network_type'])) {
        $apiService = new ExternalAPIService($order['network_type']);
        $capacity = $apiService->convertPackageSize($order['package_size']);
        $cleanPhone = $apiService->validatePhoneNumber($order['beneficiary_number']);
        
        if ($cleanPhone) {
            // Call external API
            $apiResponse = $apiService->purchaseData($cleanPhone, $order['network_type'], $capacity, 'wallet', $orderId);
            $apiSuccess = $apiResponse['success'] ?? false;
            
            if ($apiSuccess) {
                // Get the external API's transaction reference
                $externalApiReference = $apiResponse['data']['data']['transactionReference'] ?? null;
                $purchaseId = $apiResponse['data']['data']['purchaseId'] ?? null;
                $dataWebsiteTransactionId = $purchaseId ? (string)$purchaseId : null;
                
                // Update order with API response data
                $api_response_data = json_encode($apiResponse['data']);
                
                // Check if external_transaction_id column exists
                $checkColStmt = $conn->query("SHOW COLUMNS FROM orders LIKE 'external_transaction_id'");
                $hasExternalId = $checkColStmt && $checkColStmt->num_rows > 0;
                if ($checkColStmt) $checkColStmt->close();
                
                if ($hasExternalId) {
                    $updateOrderStmt = $conn->prepare("
                        UPDATE orders 
                        SET external_transaction_id = ?, 
                            external_reference = ?, 
                            api_response_data = ?,
                            status_updated_at = NOW() 
                        WHERE id = ?
                    ");
                    $updateOrderStmt->bind_param("sssi", $dataWebsiteTransactionId, $externalApiReference, $api_response_data, $orderId);
                } else {
                    // Status already set to 'processing' at start, just update timestamp if needed
                    $updateOrderStmt = $conn->prepare("
                        UPDATE orders 
                        SET status_updated_at = NOW() 
                        WHERE id = ?
                    ");
                    $updateOrderStmt->bind_param("i", $orderId);
                }
                
                $updateOrderStmt->execute();
                $updateOrderStmt->close();
            } else {
                // API call failed - update order status to failed
                $api_response_data = json_encode(['error' => $apiResponse['error'] ?? 'API call failed']);
                
                $checkColStmt = $conn->query("SHOW COLUMNS FROM orders LIKE 'api_response_data'");
                $hasApiResponse = $checkColStmt && $checkColStmt->num_rows > 0;
                if ($checkColStmt) $checkColStmt->close();
                
                if ($hasApiResponse) {
                    $updateOrderStmt = $conn->prepare("
                        UPDATE orders 
                        SET status = 'failed', 
                            api_response_data = ?, 
                            status_updated_at = NOW() 
                        WHERE id = ?
                    ");
                    $updateOrderStmt->bind_param("si", $api_response_data, $orderId);
                } else {
                    $updateOrderStmt = $conn->prepare("
                        UPDATE orders 
                        SET status = 'failed',
                            status_updated_at = NOW() 
                        WHERE id = ?
                    ");
                    $updateOrderStmt->bind_param("i", $orderId);
                }
                
                $updateOrderStmt->execute();
                $updateOrderStmt->close();
                
                // Refund balances since API failed (refund both sub-agent and store owner if applicable)
                if ($isSubAgent) {
                    // Refund sub-agent
                    $subAgentRefundBalance = round($subAgentNewBalance + $subAgentAmount, 2);
                    $refundSubAgentStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
                    $refundSubAgentStmt->bind_param("di", $subAgentRefundBalance, $orderAgentId);
                    $refundSubAgentStmt->execute();
                    $refundSubAgentStmt->close();
                    
                    // Log sub-agent refund
                    if (class_exists('BalanceHistory')) {
                        \App\classes\BalanceHistory::log($conn, $orderAgentId, $subAgentAmount, 'refund', $orderId, 'API call failed', $subAgentNewBalance, $subAgentRefundBalance);
                    }
                }
                
                // Refund store owner
                $ownerRefundBalance = round($ownerNewBalance + $storeOwnerAmount, 2);
                $refundOwnerStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
                $refundOwnerStmt->bind_param("di", $ownerRefundBalance, $storeOwnerId);
                $refundOwnerStmt->execute();
                $refundOwnerStmt->close();
                
                // Log store owner refund
                if (class_exists('BalanceHistory')) {
                    \App\classes\BalanceHistory::log($conn, $storeOwnerId, $storeOwnerAmount, 'refund', $orderId, 'API call failed', $ownerNewBalance, $ownerRefundBalance);
                }
                
                $conn->commit();
                echo json_encode([
                    'success' => false,
                    'message' => 'Order processed but API call failed: ' . ($apiResponse['error'] ?? 'Unknown error') . '. Balance has been refunded.'
                ]);
                exit;
            }
        } else {
            // Invalid phone number
            $updateOrderStmt = $conn->prepare("
                UPDATE orders 
                SET status = 'failed',
                    status_updated_at = NOW() 
                WHERE id = ?
            ");
            $updateOrderStmt->bind_param("i", $orderId);
            $updateOrderStmt->execute();
            $updateOrderStmt->close();
            
            // Refund balances (refund both sub-agent and store owner if applicable)
            if ($isSubAgent) {
                // Refund sub-agent
                $subAgentRefundBalance = round($subAgentNewBalance + $subAgentAmount, 2);
                $refundSubAgentStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
                $refundSubAgentStmt->bind_param("di", $subAgentRefundBalance, $orderAgentId);
                $refundSubAgentStmt->execute();
                $refundSubAgentStmt->close();
                
                // Log sub-agent refund
                if (class_exists('BalanceHistory')) {
                    \App\classes\BalanceHistory::log($conn, $orderAgentId, $subAgentAmount, 'refund', $orderId, 'Invalid phone number', $subAgentNewBalance, $subAgentRefundBalance);
                }
            }
            
            // Refund store owner
            $ownerRefundBalance = round($ownerNewBalance + $storeOwnerAmount, 2);
            $refundOwnerStmt = $conn->prepare("UPDATE agents SET balance = ? WHERE id = ?");
            $refundOwnerStmt->bind_param("di", $ownerRefundBalance, $storeOwnerId);
            $refundOwnerStmt->execute();
            $refundOwnerStmt->close();
            
            // Log store owner refund
            if (class_exists('BalanceHistory')) {
                \App\classes\BalanceHistory::log($conn, $storeOwnerId, $storeOwnerAmount, 'refund', $orderId, 'Invalid phone number', $ownerNewBalance, $ownerRefundBalance);
            }
            
            $conn->commit();
            echo json_encode([
                'success' => false,
                'message' => 'Invalid phone number format. Balance has been refunded.'
            ]);
            exit;
        }
    } else {
        // No external API - status already set to 'processing' at start
        // Just update timestamp if needed (optional, can be removed if not needed)
        // No update needed since status was already set
    }
    
    // Commit transaction
    $conn->commit();
    
    $message = 'Order processed successfully';
    if ($apiSuccess) {
        $message .= '. Order sent to external API and is being processed.';
    } else if (!shouldUseExternalAPI($order['network_type'])) {
        $message .= '. Order is being processed manually.';
    }
    
    echo json_encode([
        'success' => true,
        'message' => $message,
        'order_id' => $orderId,
        'order_reference' => $order['order_reference'] ?? null,
        'api_sent' => $apiSuccess
    ]);
    
} catch (Exception $e) {
    $conn->rollback();
    echo json_encode([
        'success' => false,
        'message' => 'Failed to process order: ' . $e->getMessage()
    ]);
}
?>





