<?php
require_once __DIR__ . '/bootstrap.php';

$auth   = requireAuth();
$userId = $auth['user_id'];
$db     = Database::getInstance();

// ── Realistic simulated price engine ─────────────────────────
// Multi-layered sine waves + pseudo-random walk per minute.
// Prices drift realistically without needing external APIs.
function getSimPrice(string $symbol): float {
    static $bases = [
        'BTCUSDT'   => 67500.0,
        'ETHUSDT'   => 3520.0,
        'BNBUSDT'   => 580.0,
        'SOLUSDT'   => 172.0,
        'XRPUSDT'   => 0.58,
        'ADAUSDT'   => 0.46,
        'DOGEUSDT'  => 0.162,
        'MATICUSDT' => 0.88,
    ];
    $base = $bases[$symbol] ?? 100.0;
    $t    = time();
    $seed = crc32($symbol);

    // Multiple frequency oscillations
    $wave  = sin($t / 900  + $seed * 0.001) * 0.012   // 15-min cycle ±1.2%
           + sin($t / 300  + $seed * 0.003) * 0.006   // 5-min  cycle ±0.6%
           + sin($t / 60   + $seed * 0.007) * 0.003   // 1-min  cycle ±0.3%
           + sin($t / 15   + $seed * 0.013) * 0.0015; // 15-sec cycle ±0.15%

    // Pseudo-random walk: changes per 30s bucket
    $bucket = floor($t / 30);
    $rand1  = (crc32($symbol . $bucket)     % 10000) / 10000.0; // 0..1
    $rand2  = (crc32($symbol . $bucket . 'b') % 10000) / 10000.0;
    $walk   = ($rand1 - 0.5) * 0.008 + ($rand2 - 0.5) * 0.004; // ±0.4-0.6%

    // Trend: slowly drifts over hours
    $trend  = sin($t / 14400 + $seed * 0.0001) * 0.015; // 4-hr cycle ±1.5%

    $factor = 1 + $wave + $walk + $trend;
    return round($base * max(0.5, min(2.0, $factor)), $base >= 100 ? 2 : 6);
}

function getAllPrices(): array {
    $symbols = ['BTCUSDT','ETHUSDT','BNBUSDT','SOLUSDT','XRPUSDT','ADAUSDT','DOGEUSDT','MATICUSDT'];
    $prices  = [];
    foreach ($symbols as $s) $prices[$s] = getSimPrice($s);
    return $prices;
}

// ── Tick & auto-close open trades ────────────────────────────
// Called on every request that touches trades.
// Also runs for ALL users (not just requesting user) so trades
// close even when users are offline — acts like a background worker.
function tickAllTrades(object $db, int $forUserId): void {
    // Tick open trades for this user always
    // Also opportunistically tick other users' trades (<=20) to simulate background processing
    $opens = $db->fetchAll(
        "SELECT * FROM trades WHERE status='open' AND user_id=? ORDER BY opened_at DESC",
        [$forUserId]
    );
    // Background: grab up to 15 other users' open trades too
    $bgTrades = $db->fetchAll(
        "SELECT * FROM trades WHERE status='open' AND user_id!=? ORDER BY RAND() LIMIT 15",
        [$forUserId]
    );
    processTradeBatch(array_merge($opens, $bgTrades), $db);
}

function processTradeBatch(array $trades, object $db): void {
    foreach ($trades as $t) {
        $cur   = getSimPrice($t['symbol']);
        $entry = (float)$t['entry_price'];
        $lev   = max(1, (int)$t['leverage']);
        $amt   = (float)$t['amount'];

        // P&L
        $pct = $t['direction'] === 'buy'
            ? ($cur - $entry) / $entry
            : ($entry - $cur) / $entry;
        $pnl    = round($amt * $pct * $lev, 2);
        $pnlPct = round($pct * $lev * 100, 4);

        // Determine if trade should close
        $closeReason = null;

        // Liquidation: lose ≥95% of stake
        if ($pnl <= -($amt * 0.95)) {
            $closeReason = 'liquidated';
        }
        // Stop loss hit
        elseif (!empty($t['stop_loss'])) {
            $sl = (float)$t['stop_loss'];
            if (($t['direction']==='buy'  && $cur <= $sl) ||
                ($t['direction']==='sell' && $cur >= $sl)) {
                $closeReason = 'stop_loss';
            }
        }
        // Take profit hit
        if (!$closeReason && !empty($t['take_profit'])) {
            $tp = (float)$t['take_profit'];
            if (($t['direction']==='buy'  && $cur >= $tp) ||
                ($t['direction']==='sell' && $cur <= $tp)) {
                $closeReason = 'take_profit';
            }
        }

        if ($closeReason) {
            $finalPnl = $closeReason === 'liquidated' ? -(float)$t['amount'] : $pnl;
            $refund   = (float)$t['amount'] + $finalPnl;
            $col      = $t['wallet'] === 'interest' ? 'interest_balance' : 'deposit_balance';

            if ($refund > 0) {
                $db->query("UPDATE users SET $col = $col + ? WHERE id=?",
                    [round($refund, 2), $t['user_id']]);
            }
            $db->query(
                "UPDATE trades SET current_price=?,pnl=?,pnl_pct=?,status='closed',close_price=?,close_reason=?,closed_at=NOW() WHERE id=?",
                [$cur, $finalPnl, round($finalPnl/(float)$t['amount']*100,4), $cur, $closeReason, $t['id']]
            );
            // Transaction log
            $txType = $finalPnl >= 0 ? 'roi' : 'trade_loss';
            $txDesc = ($finalPnl >= 0 ? 'Trade profit' : 'Trade loss')
                    . " #{$t['id']} — {$t['symbol_label']} " . strtoupper($t['direction'])
                    . " ×{$t['leverage']} (auto: {$closeReason})";
            $db->insert(
                "INSERT INTO transactions (user_id,type,amount,description,reference_id,wallet,status) VALUES (?,?,?,?,?,?,'completed')",
                [$t['user_id'], $txType, abs($finalPnl), $txDesc, $t['id'], $t['wallet']]
            );
        } else {
            // Update price and PnL only
            $db->query("UPDATE trades SET current_price=?,pnl=?,pnl_pct=? WHERE id=?",
                [$cur, $pnl, $pnlPct, $t['id']]);
        }
    }
}

// ── GET /trade/positions ──────────────────────────────────────
if (methodIs('GET') && strpos($uri, '/trade/positions') !== false) {
    tickAllTrades($db, $userId);

    $open   = $db->fetchAll(
        "SELECT * FROM trades WHERE user_id=? AND status='open' ORDER BY opened_at DESC",
        [$userId]
    );
    $closed = $db->fetchAll(
        "SELECT * FROM trades WHERE user_id=? AND status='closed' ORDER BY closed_at DESC LIMIT 30",
        [$userId]
    );
    $user = $db->fetch("SELECT deposit_balance,interest_balance FROM users WHERE id=?", [$userId]);

    jsonSuccess([
        'open'     => $open,
        'closed'   => $closed,
        'balances' => [
            'deposit'  => (float)$user['deposit_balance'],
            'interest' => (float)$user['interest_balance'],
        ],
        'prices'   => getAllPrices(),
    ]);
}

// ── POST /trade/tick (called by frontend every 5s) ────────────
if (methodIs('POST') && strpos($uri, '/trade/tick') !== false) {
    tickAllTrades($db, $userId);

    $open = $db->fetchAll(
        "SELECT id,symbol,symbol_label,direction,amount,leverage,entry_price,current_price,pnl,pnl_pct,take_profit,stop_loss,wallet,opened_at FROM trades WHERE user_id=? AND status='open' ORDER BY opened_at DESC",
        [$userId]
    );
    $user = $db->fetch("SELECT deposit_balance,interest_balance FROM users WHERE id=?", [$userId]);

    // Check if any trades just closed since last tick — return them
    $justClosed = $db->fetchAll(
        "SELECT * FROM trades WHERE user_id=? AND status='closed' AND closed_at >= DATE_SUB(NOW(), INTERVAL 6 SECOND) ORDER BY closed_at DESC",
        [$userId]
    );

    jsonSuccess([
        'open'        => $open,
        'just_closed' => $justClosed,
        'prices'      => getAllPrices(),
        'balances'    => [
            'deposit'  => (float)$user['deposit_balance'],
            'interest' => (float)$user['interest_balance'],
        ],
    ]);
}

// ── POST /trade/open ──────────────────────────────────────────
if (methodIs('POST') && strpos($uri, '/trade/open') !== false) {
    $symbol   = strtoupper(sanitize($input['symbol']    ?? 'BTCUSDT'));
    $label    = sanitize($input['label']                ?? 'BTC/USDT');
    $dir      = sanitize($input['direction']            ?? 'buy');
    $amount   = round((float)($input['amount']         ?? 0), 2);
    $leverage = min(100, max(1, (int)($input['leverage'] ?? 1)));
    $wallet   = in_array($input['wallet'] ?? '', ['deposit','interest']) ? $input['wallet'] : 'deposit';
    // TP/SL are USD profit/loss amounts, not prices.
    // e.g. take_profit=150 means "close when profit reaches $150"
    //      stop_loss=50    means "close when loss reaches $50"
    $tpAmount = !empty($input['take_profit']) && is_numeric($input['take_profit'])
                ? abs((float)$input['take_profit']) : null;
    $slAmount = !empty($input['stop_loss'])   && is_numeric($input['stop_loss'])
                ? abs((float)$input['stop_loss'])   : null;

    if (!in_array($dir, ['buy','sell']))   jsonError('Direction must be buy or sell.');
    if ($amount < 1)                       jsonError('Minimum trade amount is $1.');

    $valid = ['BTCUSDT','ETHUSDT','BNBUSDT','SOLUSDT','XRPUSDT','ADAUSDT','DOGEUSDT','MATICUSDT'];
    if (!in_array($symbol, $valid))        jsonError('Invalid trading symbol.');

    if ($tpAmount !== null && $tpAmount <= 0) jsonError('Take profit must be greater than $0.');
    if ($slAmount !== null && $slAmount <= 0) jsonError('Stop loss must be greater than $0.');
    if ($slAmount !== null && $slAmount >= $amount) jsonError('Stop loss cannot exceed your stake amount ($' . number_format($amount, 2) . ').');

    $col     = $wallet === 'interest' ? 'interest_balance' : 'deposit_balance';
    $user    = $db->fetch("SELECT $col AS bal FROM users WHERE id=?", [$userId]);
    $balance = (float)($user['bal'] ?? 0);

    if ($balance < $amount)
        jsonError("Insufficient balance. Available: $" . number_format($balance, 2));

    $price = getSimPrice($symbol);
    $lev   = min(100, max(1, (int)($input['leverage'] ?? 1))); // re-read for price calc

    // Convert TP/SL amounts → price targets for storage
    // P&L = amount * leverage * (price_move / entry_price)
    // So: target_price = entry + (pnl_target / (amount * leverage)) * entry
    $tp = null;
    $sl = null;
    if ($tpAmount !== null) {
        $priceMoveRatio = $tpAmount / ($amount * $lev);
        $tp = $dir === 'buy'
            ? $price * (1 + $priceMoveRatio)
            : $price * (1 - $priceMoveRatio);
        $tp = round($tp, 6);
    }
    if ($slAmount !== null) {
        $priceMoveRatio = $slAmount / ($amount * $lev);
        $sl = $dir === 'buy'
            ? $price * (1 - $priceMoveRatio)
            : $price * (1 + $priceMoveRatio);
        $sl = round($sl, 6);
    }

    // Deduct stake
    $db->query("UPDATE users SET $col = $col - ? WHERE id=?", [$amount, $userId]);

    $tradeId = $db->insert(
        "INSERT INTO trades (user_id,symbol,symbol_label,direction,amount,leverage,entry_price,current_price,take_profit,stop_loss,wallet) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
        [$userId, $symbol, $label, $dir, $amount, $leverage, $price, $price, $tp, $sl, $wallet]
    );

    $db->insert(
        "INSERT INTO transactions (user_id,type,amount,description,reference_id,wallet,status) VALUES (?,?,?,?,?,?,'completed')",
        [$userId, 'investment', $amount,
         "Trade #{$tradeId} opened — {$label} " . strtoupper($dir) . " ×{$leverage} @ $" . number_format($price, 2),
         $tradeId, $wallet]
    );

    jsonSuccess([
        'trade_id'    => $tradeId,
        'entry_price' => $price,
        'symbol'      => $symbol,
        'label'       => $label,
        'leverage'    => $leverage,
        'tp_amount'   => $tpAmount,
        'sl_amount'   => $slAmount,
    ], "Trade opened: {$label} " . strtoupper($dir) . " \${$amount} ×{$leverage} @ $" . number_format($price, 2));
}

// ── POST /trade/close (manual close) ─────────────────────────
if (methodIs('POST') && strpos($uri, '/trade/close') !== false) {
    $tradeId = (int)($input['trade_id'] ?? 0);
    if (!$tradeId) jsonError('Trade ID required.');

    $trade = $db->fetch("SELECT * FROM trades WHERE id=? AND user_id=? AND status='open'",
        [$tradeId, $userId]);
    if (!$trade) jsonError('Trade not found or already closed.');

    $cur   = getSimPrice($trade['symbol']);
    $entry = (float)$trade['entry_price'];
    $lev   = max(1, (int)$trade['leverage']);
    $amt   = (float)$trade['amount'];

    $pct    = $trade['direction'] === 'buy'
        ? ($cur - $entry) / $entry
        : ($entry - $cur) / $entry;
    $pnl    = round($amt * $pct * $lev, 2);
    $pnlPct = round($pct * $lev * 100, 4);

    $refund = round($amt + $pnl, 2);
    $col    = $trade['wallet'] === 'interest' ? 'interest_balance' : 'deposit_balance';
    if ($refund > 0) {
        $db->query("UPDATE users SET $col = $col + ? WHERE id=?", [$refund, $userId]);
    }

    $db->query(
        "UPDATE trades SET current_price=?,pnl=?,pnl_pct=?,status='closed',close_price=?,close_reason='manual',closed_at=NOW() WHERE id=?",
        [$cur, $pnl, $pnlPct, $cur, $tradeId]
    );

    $txType = $pnl >= 0 ? 'roi' : 'trade_loss';
    $db->insert(
        "INSERT INTO transactions (user_id,type,amount,description,reference_id,wallet,status) VALUES (?,?,?,?,?,?,'completed')",
        [$userId, $txType, abs($pnl),
         ($pnl >= 0 ? 'Trade profit' : 'Trade loss') . " #{$tradeId} — {$trade['symbol_label']} " . strtoupper($trade['direction']) . " (manual)",
         $tradeId, $trade['wallet']]
    );

    $msg = $pnl >= 0
        ? "Trade closed with profit: +" . number_format($pnl, 2)
        : "Trade closed at loss: " . number_format($pnl, 2);

    jsonSuccess([
        'pnl'         => $pnl,
        'pnl_pct'     => $pnlPct,
        'close_price' => $cur,
        'refund'      => max(0, $refund),
    ], $msg);
}

// ── GET /trade/prices ─────────────────────────────────────────
if (methodIs('GET') && strpos($uri, '/trade/prices') !== false) {
    jsonSuccess(['prices' => getAllPrices()]);
}
