<?php
/**
 * API Endpoint: Submit Trading Order
 * Procesa y envía nuevas órdenes de trading
 */

session_start();

// Verificar autenticación del cliente
if (!isset($_SESSION['client_id']) || $_SESSION['client_status'] !== 'active') {
    http_response_code(401);
    header('Content-Type: application/json');
    echo json_encode(['success' => false, 'message' => 'No autorizado']);
    exit;
}

// Solo permitir POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Método no permitido']);
    exit;
}

// Configuración de la base de datos
require_once __DIR__ . '/../../../database/connection.php';
require_once __DIR__ . '/../finage_api.php';

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

try {
    $stage = 'init';
    // Obtener datos de la orden
    $input = json_decode(file_get_contents('php://input'), true);
    
    if (!$input) {
        throw new Exception('Datos de orden inválidos');
    }
    
    $client_id = $_SESSION['client_id'];
    $accountNumber = !empty($input['account_number']) ? trim($input['account_number']) : null;
    
    // Validar campos requeridos
    $requiredFields = ['orderSymbol', 'orderType', 'orderSide', 'orderVolume'];
    foreach ($requiredFields as $field) {
        if (empty($input[$field])) {
            throw new Exception("Campo requerido faltante: $field");
        }
    }
    
    $symbol = strtoupper(trim($input['orderSymbol']));
    $type = strtolower(trim($input['orderType']));
    $side = strtolower(trim($input['orderSide']));
    $volume = floatval($input['orderVolume']);
    $price = !empty($input['orderPrice']) ? floatval($input['orderPrice']) : null;
    $stopLoss = !empty($input['orderStopLoss']) ? floatval($input['orderStopLoss']) : null;
    $takeProfit = !empty($input['orderTakeProfit']) ? floatval($input['orderTakeProfit']) : null;
    $comment = !empty($input['orderComment']) ? trim($input['orderComment']) : '';
    
    // Validaciones básicas
    if ($volume <= 0 || $volume > 100) {
        throw new Exception('Volumen inválido. Debe estar entre 0.01 y 100 lotes');
    }
    
    if (!in_array($type, ['market', 'limit', 'stop'])) {
        throw new Exception('Tipo de orden inválido');
    }
    
    if (!in_array($side, ['buy', 'sell'])) {
        throw new Exception('Dirección de orden inválida');
    }
    
    if (in_array($type, ['limit', 'stop']) && (!$price || $price <= 0)) {
        throw new Exception('Precio requerido para órdenes limit y stop');
    }
    
    // Obtener cuenta de trading del cliente (filtrar por account_number si se proporcionó)
    $stage = 'load_account';
    if ($accountNumber) {
        $stmt = $pdo->prepare("SELECT * FROM trading_accounts WHERE client_id = ? AND account_number = ? AND status = 'active' LIMIT 1");
        $stmt->execute([$client_id, $accountNumber]);
    } else {
        $stmt = $pdo->prepare("SELECT * FROM trading_accounts WHERE client_id = ? AND status = 'active' ORDER BY created_at DESC LIMIT 1");
        $stmt->execute([$client_id]);
    }
    $account = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$account) {
        // Intentar crear/actualizar la cuenta de trading desde client_accounts
        $stage = 'upsert_account';
        try {
            if ($accountNumber) {
                $srcStmt = $pdo->prepare("SELECT * FROM client_accounts WHERE client_id = ? AND account_number = ? AND status = 'active' LIMIT 1");
                $srcStmt->execute([$client_id, $accountNumber]);
                $src = $srcStmt->fetch(PDO::FETCH_ASSOC);
            } else {
                $srcStmt = $pdo->prepare("SELECT * FROM client_accounts WHERE client_id = ? AND status = 'active' ORDER BY created_at DESC LIMIT 1");
                $srcStmt->execute([$client_id]);
                $src = $srcStmt->fetch(PDO::FETCH_ASSOC);
                $accountNumber = $src['account_number'] ?? $accountNumber;
            }
            if ($src && !empty($src['account_number'])) {
                $currency = $src['currency'] ?? 'USD';
                $leverage = isset($src['leverage']) ? (int)$src['leverage'] : 100;
                $balance = isset($src['balance']) ? (float)$src['balance'] : 0.0;
                $equity = isset($src['equity']) ? (float)$src['equity'] : $balance;
                $margin = isset($src['margin']) ? (float)$src['margin'] : 0.0;
                $free_margin = isset($src['free_margin']) ? (float)$src['free_margin'] : max(0.0, $equity - $margin);
                $status = $src['status'] ?? 'active';
                $ml = $margin > 0 ? (($equity / max($margin,1e-9)) * 100.0) : 0.0;
                // Upsert en trading_accounts
                $ex = $pdo->prepare("SELECT id FROM trading_accounts WHERE client_id = ? AND account_number = ? LIMIT 1");
                $ex->execute([$client_id, $src['account_number']]);
                $row = $ex->fetch(PDO::FETCH_ASSOC);
                if ($row) {
                    $u = $pdo->prepare("UPDATE trading_accounts SET currency=?, leverage=?, balance=?, equity=?, margin=?, free_margin=?, margin_level=?, status=?, updated_at=NOW() WHERE id = ?");
                    $u->execute([$currency, $leverage, $balance, $equity, $margin, $free_margin, $ml, $status, (int)$row['id']]);
                } else {
                    $i = $pdo->prepare("INSERT INTO trading_accounts (client_id, account_number, account_type, currency, leverage, balance, equity, margin, free_margin, margin_level, status, created_at) VALUES (?, ?, 'standard', ?, ?, ?, ?, ?, ?, ?, ?, NOW())");
                    $i->execute([$client_id, $src['account_number'], $currency, $leverage, $balance, $equity, $margin, $free_margin, $ml, $status]);
                }
                // Releer cuenta
                $r = $pdo->prepare("SELECT * FROM trading_accounts WHERE client_id = ? AND account_number = ? AND status = 'active' LIMIT 1");
                $r->execute([$client_id, $src['account_number']]);
                $account = $r->fetch(PDO::FETCH_ASSOC);
            }
        } catch (Throwable $__) { /* noop */ }
        if (!$account) {
            throw new Exception('No se encontró cuenta de trading activa');
        }
    }
    
    // Verificar que el instrumento existe
    $stage = 'load_instrument';
    $stmt = $pdo->prepare("
        SELECT * FROM trading_instruments 
        WHERE symbol = ? AND is_active = 1
    ");
    $stmt->execute([$symbol]);
    $instrument = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$instrument) {
        $defaultCategory = 'forex';
        $defaultContract = 100000;
        $defaultMinVol = 0.01;
        $defaultMaxVol = 50.0;
        $defaultStep = 0.01;
        $defaultPip = (strpos($symbol, 'JPY') !== false) ? 0.01 : 0.0001;
        $defaultMarginRate = 0.02;
        $base = substr($symbol, 0, 3);
        $quote = substr($symbol, 3, 3);
        $ins = $pdo->prepare("INSERT INTO trading_instruments (symbol, name, category, base_currency, quote_currency, pip_size, min_volume, max_volume, volume_step, contract_size, margin_rate, is_active, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW())");
        $ins->execute([$symbol, $symbol, $defaultCategory, $base, $quote, $defaultPip, $defaultMinVol, $defaultMaxVol, $defaultStep, $defaultContract, $defaultMarginRate]);
        $stmt = $pdo->prepare("\n        SELECT * FROM trading_instruments \n        WHERE symbol = ? AND is_active = 1\n    ");
        $stmt->execute([$symbol]);
        $instrument = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$instrument) {
            throw new Exception('Instrumento de trading no disponible');
        }
    }
    
    // Validar volumen mínimo y máximo
    $minVol = isset($instrument['min_volume']) ? floatval($instrument['min_volume']) : 0.01;
    $maxVol = isset($instrument['max_volume']) ? floatval($instrument['max_volume']) : 100.0;
    $stepVol = isset($instrument['volume_step']) ? floatval($instrument['volume_step']) : 0.01;
    if ($volume < $minVol) {
        throw new Exception("Volumen mínimo para $symbol es {$minVol} lotes");
    }
    
    if ($volume > $maxVol) {
        throw new Exception("Volumen máximo para $symbol es {$maxVol} lotes");
    }
    $relative = $volume - $minVol;
    $remainder = fmod($relative, $stepVol);
    if ($remainder > 1e-8 && ($stepVol - $remainder) > 1e-8) {
        throw new Exception("El volumen debe incrementarse en pasos de {$stepVol} lotes");
    }
    
    // Obtener precio actual para órdenes de mercado y validaciones
    $stage = 'get_price';
    $currentPrices = getCurrentPrices($symbol, $instrument['category'] ?? 'forex');
    $currentPrice = ($side === 'buy') ? $currentPrices['ask'] : $currentPrices['bid'];
    
    // Para órdenes de mercado, usar precio actual
    if ($type === 'market') {
        $price = $currentPrice;
    }
    
    // Validar precios de stop loss y take profit
    if ($stopLoss) {
        if ($side === 'buy' && $stopLoss >= $price) {
            throw new Exception('Stop Loss debe ser menor al precio de entrada para órdenes de compra');
        }
        if ($side === 'sell' && $stopLoss <= $price) {
            throw new Exception('Stop Loss debe ser mayor al precio de entrada para órdenes de venta');
        }
    }
    
    if ($takeProfit) {
        if ($side === 'buy' && $takeProfit <= $price) {
            throw new Exception('Take Profit debe ser mayor al precio de entrada para órdenes de compra');
        }
        if ($side === 'sell' && $takeProfit >= $price) {
            throw new Exception('Take Profit debe ser menor al precio de entrada para órdenes de venta');
        }
    }
    
    // Calcular margen requerido (unificado con UI): usa margin_rate si existe; si no, notional/leverage
    $leverage = floatval($account['leverage']);
    $contractSize = isset($instrument['contract_size']) ? floatval($instrument['contract_size']) : 100000;
    $quoteCurrency = $instrument['quote_currency'] ?? substr($symbol, -3);
    $accountCurrency = $account['currency'] ?? 'USD';
    $notionalQuote = $volume * $contractSize * $price; // valor en moneda cotizada
    $marginRate = isset($instrument['margin_rate']) ? floatval($instrument['margin_rate']) : null;
    $rawMarginQuote = ($marginRate && $marginRate > 0) ? ($notionalQuote * $marginRate) : ($notionalQuote / max($leverage, 1));

    // Convertir margen a moneda de cuenta si es distinto a quote
    $marginRequired = $rawMarginQuote;
    if (strtoupper($quoteCurrency) !== strtoupper($accountCurrency)) {
        $pair1 = strtoupper($accountCurrency . $quoteCurrency); // p.ej. USDCAD
        $pair2 = strtoupper($quoteCurrency . $accountCurrency); // p.ej. CADUSD
        $conv = null;
        try {
            $tick1 = getFinageAPI()->getRealTimePrice($pair1);
            if (is_array($tick1) && (isset($tick1['bid']) || isset($tick1['ask']) || isset($tick1['price']))) {
                $rate = isset($tick1['bid']) ? floatval($tick1['bid']) : (isset($tick1['ask']) ? floatval($tick1['ask']) : floatval($tick1['price']));
                if ($rate && $rate > 0) $conv = 1.0 / $rate; // amountQuote / (accountCurrency/quote)
            }
            if ($conv === null) {
                $tick2 = getFinageAPI()->getRealTimePrice($pair2);
                if (is_array($tick2) && (isset($tick2['bid']) || isset($tick2['ask']) || isset($tick2['price']))) {
                    $rate2 = isset($tick2['bid']) ? floatval($tick2['bid']) : (isset($tick2['ask']) ? floatval($tick2['ask']) : floatval($tick2['price']));
                    if ($rate2 && $rate2 > 0) $conv = $rate2; // amountQuote * (quote/accountCurrency)
                }
            }
        } catch (Throwable $e) { $conv = null; }
        if ($conv !== null) {
            $marginRequired = $rawMarginQuote * $conv;
        }
    }
    
    // Verificar margen disponible
    if ($marginRequired > $account['free_margin']) {
        throw new Exception('Margen insuficiente. Requerido: ' . number_format($marginRequired, 2) . 
                          ', Disponible: ' . number_format($account['free_margin'], 2));
    }
    
    // Determinar tipo de orden final
    $orderType = $type;
    if ($type !== 'market') {
        $orderType = $side . '_' . $type;
    }
    
    // Iniciar transacción
    $stage = 'begin_tx';
    $pdo->beginTransaction();
    
    try {
        if ($type === 'market') {
            // Ejecutar orden de mercado inmediatamente
            $stage = 'insert_position';
            $positionId = executeMarketOrder(
                $pdo, 
                $account['id'], 
                $symbol, 
                $side, 
                $volume, 
                $price, 
                $stopLoss, 
                $takeProfit, 
                $comment,
                $marginRequired
            );
            
            // Confirmar inserción de posición para evitar rollback por actualización de KPIs
            $stage = 'commit_position';
            $pdo->commit();
            
            // Actualizar KPIs de cuenta inmediatamente fuera de la transacción (margen/free_margin)
            $warnings = [];
            try { updateAccountBalanceSafe($pdo, $account['id'], $marginRequired); } catch (Throwable $tw) { $warnings[] = 'account_update_failed'; }
            
            echo json_encode([
                'success' => true,
                'message' => 'Orden de mercado ejecutada exitosamente',
                'order_id' => $positionId,
                'execution_price' => number_format($price, 5),
                'type' => 'position',
                'warnings' => $warnings
            ]);
            
        } else {
            // Crear orden pendiente
            $orderId = createPendingOrder(
                $pdo,
                $account['id'],
                $symbol,
                $orderType,
                $volume,
                $price,
                $stopLoss,
                $takeProfit,
                $comment
            );
            
            $pdo->commit();
            
            echo json_encode([
                'success' => true,
                'message' => 'Orden pendiente creada exitosamente',
                'order_id' => $orderId,
                'order_price' => number_format($price, 5),
                'type' => 'order'
            ]);
        }
        
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
    
} catch (Exception $e) {
    error_log("Error en submit-order.php: " . $e->getMessage());
    http_response_code(400);
    header('Content-Type: application/json');
    $diag = [];
    try {
        $dbName = $pdo->query('SELECT DATABASE()')->fetchColumn();
        $colsStmt = $pdo->prepare("SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = ? AND table_name = 'trading_accounts'");
        $colsStmt->execute([$dbName]);
        $existingCols = array_column($colsStmt->fetchAll(PDO::FETCH_ASSOC), 'COLUMN_NAME');
        // Triggers ligados a cuentas/posiciones
        $trgStmt = $pdo->prepare("SELECT TRIGGER_NAME, EVENT_MANIPULATION AS event, EVENT_OBJECT_TABLE AS tbl, ACTION_TIMING AS timing FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = ? AND EVENT_OBJECT_TABLE IN ('trading_positions','trading_accounts')");
        $trgStmt->execute([$dbName]);
        $triggers = $trgStmt->fetchAll(PDO::FETCH_ASSOC);
        $diag = [
            'database' => $dbName,
            'trading_accounts_columns' => $existingCols,
            'stage' => $stage,
            'triggers' => $triggers
        ];
    } catch (Throwable $t) {
        $diag = ['error' => 'diagnostics_failed', 'stage' => $stage];
    }
    echo json_encode([
        'success' => false,
        'message' => $e->getMessage(),
        'diagnostics' => $diag
    ]);
}

/**
 * Obtener precios actuales (simulados)
 */
function getCurrentPrices($symbol, $category) {
    $finage = getFinageAPI();
    $tick = $finage->getRealTimePrice($symbol);
    if (!is_array($tick) || (!isset($tick['bid']) && !isset($tick['ask']))) {
        throw new Exception('No se pudieron obtener precios del proveedor');
    }
    return [
        'bid' => floatval($tick['bid'] ?? $tick['price'] ?? 0),
        'ask' => floatval($tick['ask'] ?? ($tick['price'] ?? 0)),
    ];
}

function mapAssetType($category) {
    $category = strtolower((string)$category);
    if (in_array($category, ['forex', 'fx'])) return 'forex';
    if (in_array($category, ['crypto'])) return 'crypto';
    if (in_array($category, ['stocks', 'equities'])) return 'stocks';
    if (in_array($category, ['indices', 'index'])) return 'indices';
    if (in_array($category, ['commodities', 'commodity'])) return 'commodities';
    return 'forex';
}

/**
 * Ejecutar orden de mercado
 */
function executeMarketOrder($pdo, $accountId, $symbol, $side, $volume, $price, $stopLoss, $takeProfit, $comment, $marginRequired) {
    $stmt = $pdo->prepare("INSERT INTO trading_positions (
            account_id, symbol, type, volume, open_price,
            stop_loss, take_profit, margin, profit, comment, status, opened_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0.00, ?, 'open', NOW())
    ");
    $stmt->execute([
        $accountId, $symbol, $side, $volume, $price,
        $stopLoss, $takeProfit, $marginRequired, $comment
    ]);
    
    return $pdo->lastInsertId();
}

/**
 * Crear orden pendiente
 */
function createPendingOrder($pdo, $accountId, $symbol, $type, $volume, $price, $stopLoss, $takeProfit, $comment) {
    $stmt = $pdo->prepare("
        INSERT INTO trading_orders (
            account_id, symbol, type, volume, price,
            stop_loss, take_profit, comment, status, created_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', NOW())
    ");
    
    $stmt->execute([
        $accountId, $symbol, $type, $volume, $price,
        $stopLoss, $takeProfit, $comment
    ]);
    
    return $pdo->lastInsertId();
}

/**
 * Actualizar balance de la cuenta
 */
function updateAccountBalance($pdo, $accountId, $marginUsed) {
    try {
        $stmt = $pdo->prepare("UPDATE trading_accounts SET margin = margin + ?, free_margin = free_margin - ?, updated_at = NOW() WHERE id = ?");
        $stmt->execute([$marginUsed, $marginUsed, $accountId]);
    } catch (PDOException $e) {
        $msg = $e->getMessage();
        if (strpos($msg, 'Unknown column') !== false && strpos($msg, 'margin') !== false) {
            try { $pdo->exec("ALTER TABLE trading_accounts ADD COLUMN margin DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE trading_accounts ADD COLUMN free_margin DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE trading_accounts ADD COLUMN equity DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE trading_accounts ADD COLUMN margin_level DECIMAL(9,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            $stmt = $pdo->prepare("UPDATE trading_accounts SET margin = margin + ?, free_margin = free_margin - ?, updated_at = NOW() WHERE id = ?");
            $stmt->execute([$marginUsed, $marginUsed, $accountId]);
        } else {
            throw $e;
        }
    }
}
function ensureTradingAccountColumns($pdo) {
    try {
        $dbName = $pdo->query('SELECT DATABASE()')->fetchColumn();
        $colsStmt = $pdo->prepare("SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = ? AND table_name = 'trading_accounts'");
        $colsStmt->execute([$dbName]);
        $existing = array_map('strtolower', array_column($colsStmt->fetchAll(PDO::FETCH_ASSOC), 'COLUMN_NAME'));
        $required = [
            'margin' => "ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `margin` DECIMAL(18,2) NOT NULL DEFAULT 0.00",
            'free_margin' => "ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `free_margin` DECIMAL(18,2) NOT NULL DEFAULT 0.00",
            'equity' => "ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `equity` DECIMAL(18,2) NOT NULL DEFAULT 0.00",
            'margin_level' => "ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `margin_level` DECIMAL(9,2) NOT NULL DEFAULT 0.00"
        ];
        foreach ($required as $col => $ddl) {
            if (!in_array($col, $existing, true)) { try { $pdo->exec($ddl); } catch (Throwable $e) {} }
        }
    } catch (Throwable $e) {}
}
?>
<?php
function updateAccountBalanceSafe($pdo, $accountId, $marginUsed) {
    try {
        $dbName = $pdo->query('SELECT DATABASE()')->fetchColumn();
        $dbName = $dbName ?: 'simple_crm';
        $sql = "UPDATE `".$dbName."`.`trading_accounts` SET `margin` = COALESCE(`margin`,0) + ?, `free_margin` = COALESCE(`free_margin`,0) - ?, `updated_at` = NOW() WHERE `id` = ?";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([$marginUsed, $marginUsed, $accountId]);
    } catch (PDOException $e) {
        $msg = $e->getMessage();
        if (strpos($msg, 'Unknown column') !== false && strpos($msg, 'margin') !== false) {
            try { $pdo->exec("ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `margin` DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `free_margin` DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `equity` DECIMAL(18,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            try { $pdo->exec("ALTER TABLE `".$dbName."`.`trading_accounts` ADD COLUMN `margin_level` DECIMAL(9,2) NOT NULL DEFAULT 0.00"); } catch (Throwable $t) {}
            $sql = "UPDATE `".$dbName."`.`trading_accounts` SET `margin` = COALESCE(`margin`,0) + ?, `free_margin` = COALESCE(`free_margin`,0) - ?, `updated_at` = NOW() WHERE `id` = ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$marginUsed, $marginUsed, $accountId]);
        } else {
            throw $e;
        }
    }
}
