<?php
/**
 * Trading Engine Class
 * Handles all trading operations
 */

class TradingEngine {
    private static $instance = null;
    
    private function __construct() {}
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Place a new trade
     */
    public function placeTrade($userId, $data) {
        $required = ['asset_id', 'direction', 'amount', 'duration'];
        foreach ($required as $field) {
            if (empty($data[$field])) {
                return ['success' => false, 'error' => "Missing required field: {$field}"];
            }
        }
        
        if (!in_array($data['direction'], ['up', 'down'])) {
            return ['success' => false, 'error' => 'Invalid trade direction'];
        }
        
        $user = dbFetch(
            "SELECT u.*, w.balance, w.demo_balance 
             FROM users u 
             LEFT JOIN user_wallets w ON u.id = w.user_id 
             WHERE u.id = :id AND u.status = 'active'",
            ['id' => $userId]
        );
        
        if (!$user) {
            return ['success' => false, 'error' => 'User not found'];
        }
        
        $asset = dbFetch(
            "SELECT * FROM assets WHERE id = :id AND status = 'active'",
            ['id' => $data['asset_id']]
        );
        
        if (!$asset) {
            return ['success' => false, 'error' => 'Asset not available'];
        }
        
        if (!$asset['is_24h']) {
            $currentTime = date('H:i:s');
            if ($currentTime < $asset['trading_hours_start'] || $currentTime > $asset['trading_hours_end']) {
                return ['success' => false, 'error' => 'Trading is closed for this asset'];
            }
        }
        
        $amount = (float)$data['amount'];
        $isDemo = !empty($data['is_demo']);
        
        if ($amount < $asset['min_trade']) {
            return ['success' => false, 'error' => 'Minimum trade amount is ' . formatCurrency($asset['min_trade'])];
        }
        
        if ($amount > $asset['max_trade']) {
            return ['success' => false, 'error' => 'Maximum trade amount is ' . formatCurrency($asset['max_trade'])];
        }
        
        $balanceField = $isDemo ? 'demo_balance' : 'balance';
        if ($user[$balanceField] < $amount) {
            return ['success' => false, 'error' => 'Insufficient balance'];
        }
        
        $duration = dbFetch(
            "SELECT * FROM trade_durations WHERE duration_seconds = :duration AND status = 'active'",
            ['duration' => $data['duration']]
        );
        
        if (!$duration) {
            return ['success' => false, 'error' => 'Invalid trade duration'];
        }
        
        $payoutPercent = $asset['payout_percent'] + $duration['payout_modifier'];
        $potentialProfit = calculatePayout($amount, $payoutPercent);
        
        $currentPrice = $this->getCurrentPrice($asset['symbol']);
        if (!$currentPrice) {
            return ['success' => false, 'error' => 'Unable to get current price. Please try again.'];
        }
        
        $startTime = date('Y-m-d H:i:s');
        $endTime = date('Y-m-d H:i:s', time() + $duration['duration_seconds']);
        
        try {
            dbBeginTransaction();
            
            $sql = "UPDATE user_wallets SET {$balanceField} = {$balanceField} - :amount WHERE user_id = :user_id";
            dbQuery($sql, ['amount' => $amount, 'user_id' => $userId]);
            
            $tradeId = dbInsert('trades', [
                'trade_id' => generateTradeId(),
                'user_id' => $userId,
                'asset_id' => $asset['id'],
                'symbol' => $asset['symbol'],
                'trade_type' => $data['trade_type'] ?? 'binary',
                'direction' => $data['direction'],
                'amount' => $amount,
                'duration_seconds' => $duration['duration_seconds'],
                'entry_price' => $currentPrice,
                'payout_percent' => $payoutPercent,
                'potential_profit' => $potentialProfit,
                'result' => 'pending',
                'is_demo' => $isDemo ? 1 : 0,
                'start_time' => $startTime,
                'end_time' => $endTime,
                'ip_address' => getClientIp()
            ]);
            
            if (!$tradeId) {
                throw new Exception('Failed to create trade');
            }
            
            dbQuery(
                "UPDATE user_wallets SET total_trades = total_trades + 1 WHERE user_id = :user_id",
                ['user_id' => $userId]
            );
            
            if (!$isDemo) {
                $wallet = dbFetch("SELECT balance FROM user_wallets WHERE user_id = :id", ['id' => $userId]);
                dbInsert('transactions', [
                    'transaction_id' => generateTransactionId('TRD'),
                    'user_id' => $userId,
                    'type' => 'trade_loss',
                    'amount' => -$amount,
                    'balance_before' => $user['balance'],
                    'balance_after' => $wallet['balance'],
                    'reference_type' => 'trade',
                    'reference_id' => $tradeId,
                    'description' => "Trade placed on {$asset['symbol']} ({$data['direction']})"
                ]);
            }
            
            dbCommit();
            
            $trade = dbFetch("SELECT * FROM trades WHERE id = :id", ['id' => $tradeId]);
            
            return [
                'success' => true,
                'trade' => $trade,
                'message' => 'Trade placed successfully'
            ];
            
        } catch (Exception $e) {
            dbRollback();
            error_log("Trade Error: " . $e->getMessage());
            return ['success' => false, 'error' => 'Failed to place trade. Please try again.'];
        }
    }
    
    /**
     * Get current price from Binance
     */
    public function getCurrentPrice($symbol) {
        $url = BINANCE_API_URL . "/ticker/price?symbol=" . $symbol;
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => true
        ]);
        
        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            error_log("Binance API Error: " . $error);
            return null;
        }
        
        $data = json_decode($response, true);
        return isset($data['price']) ? (float)$data['price'] : null;
    }
    
    /**
     * Get historical klines (candlestick data)
     */
    public function getKlines($symbol, $interval = '1m', $limit = 100) {
        $url = BINANCE_API_URL . "/klines?symbol={$symbol}&interval={$interval}&limit={$limit}";
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => true
        ]);
        
        $response = curl_exec($ch);
        curl_close($ch);
        
        $data = json_decode($response, true);
        
        if (!is_array($data)) {
            return [];
        }
        
        $klines = [];
        foreach ($data as $kline) {
            $klines[] = [
                'time' => (int)($kline[0] / 1000),
                'open' => (float)$kline[1],
                'high' => (float)$kline[2],
                'low' => (float)$kline[3],
                'close' => (float)$kline[4],
                'volume' => (float)$kline[5]
            ];
        }
        
        return $klines;
    }
    
    /**
     * Close expired trades
     */
    public function closeExpiredTrades() {
        $trades = dbFetchAll(
            "SELECT t.*, u.win_rate_override 
             FROM trades t 
             LEFT JOIN users u ON t.user_id = u.id 
             WHERE t.result = 'pending' AND t.end_time <= NOW()"
        );
        
        $results = ['processed' => 0, 'won' => 0, 'lost' => 0, 'tie' => 0];
        
        foreach ($trades as $trade) {
            $result = $this->closeTrade($trade);
            if ($result['success']) {
                $results['processed']++;
                $results[$result['result']]++;
            }
        }
        
        return $results;
    }
    
    /**
     * Close a single trade
     */
    public function closeTrade($trade) {
        if ($trade['result'] !== 'pending') {
            return ['success' => false, 'error' => 'Trade already closed'];
        }
        
        if ($trade['admin_override'] && $trade['override_result']) {
            $result = $trade['override_result'];
            $exitPrice = $trade['entry_price'];
        } else {
            $exitPrice = $this->getCurrentPrice($trade['symbol']);
            if (!$exitPrice) {
                return ['success' => false, 'error' => 'Unable to get exit price'];
            }
            
            $result = $this->determineTradeResult($trade, $exitPrice);
        }
        
        try {
            dbBeginTransaction();
            
            $actualProfit = 0;
            $balanceField = $trade['is_demo'] ? 'demo_balance' : 'balance';
            
            if ($result === 'won') {
                $actualProfit = $trade['potential_profit'];
                $totalReturn = $trade['amount'] + $actualProfit;
                
                dbQuery(
                    "UPDATE user_wallets SET 
                        {$balanceField} = {$balanceField} + :amount,
                        total_won = total_won + 1,
                        total_profit = total_profit + :profit
                     WHERE user_id = :user_id",
                    [
                        'amount' => $totalReturn,
                        'profit' => $actualProfit,
                        'user_id' => $trade['user_id']
                    ]
                );
                
                if (!$trade['is_demo']) {
                    $wallet = dbFetch("SELECT balance FROM user_wallets WHERE user_id = :id", ['id' => $trade['user_id']]);
                    dbInsert('transactions', [
                        'transaction_id' => generateTransactionId('WIN'),
                        'user_id' => $trade['user_id'],
                        'type' => 'trade_win',
                        'amount' => $totalReturn,
                        'balance_before' => $wallet['balance'] - $totalReturn,
                        'balance_after' => $wallet['balance'],
                        'reference_type' => 'trade',
                        'reference_id' => $trade['id'],
                        'description' => "Won trade on {$trade['symbol']}"
                    ]);
                }
                
            } elseif ($result === 'lost') {
                $actualProfit = -$trade['amount'];
                
                dbQuery(
                    "UPDATE user_wallets SET 
                        total_lost = total_lost + 1,
                        total_loss = total_loss + :loss
                     WHERE user_id = :user_id",
                    ['loss' => $trade['amount'], 'user_id' => $trade['user_id']]
                );
                
            } else {
                dbQuery(
                    "UPDATE user_wallets SET {$balanceField} = {$balanceField} + :amount WHERE user_id = :user_id",
                    ['amount' => $trade['amount'], 'user_id' => $trade['user_id']]
                );
            }
            
            dbUpdate('trades', [
                'exit_price' => $exitPrice,
                'actual_profit' => $actualProfit,
                'result' => $result,
                'closed_at' => date('Y-m-d H:i:s')
            ], 'id = :id', ['id' => $trade['id']]);
            
            $this->sendTradeNotification($trade['user_id'], $trade, $result, $actualProfit);
            
            if (!$trade['is_demo'] && $result === 'lost') {
                $this->processReferralCommission($trade['user_id'], $trade['amount']);
            }
            
            dbCommit();
            
            return ['success' => true, 'result' => $result, 'profit' => $actualProfit];
            
        } catch (Exception $e) {
            dbRollback();
            error_log("Close Trade Error: " . $e->getMessage());
            return ['success' => false, 'error' => 'Failed to close trade'];
        }
    }
    
    /**
     * Determine trade result based on price movement
     * IMPORTANT: 
     * - DEMO accounts: ALWAYS use natural market results (risk-free)
     * - LIVE accounts: Apply Global Win Rate (0-100%)
     */
    private function determineTradeResult($trade, $exitPrice) {
        $entryPrice = (float)$trade['entry_price'];
        $direction = $trade['direction'];
        $isDemo = (bool)$trade['is_demo'];
        
        // First, calculate NATURAL market result
        if ($exitPrice > $entryPrice) {
            $naturalResult = $direction === 'up' ? 'won' : 'lost';
        } elseif ($exitPrice < $entryPrice) {
            $naturalResult = $direction === 'down' ? 'won' : 'lost';
        } else {
            return 'tie';
        }
        
        // DEMO ACCOUNT: Always return natural market result (RISK-FREE)
        if ($isDemo) {
            return $naturalResult;
        }
        
        // LIVE ACCOUNT: Apply Global Win Rate manipulation (0-100%)
        $globalWinRate = (int)getSetting('global_win_rate', 45);
        
        // Ensure win rate is within 0-100 bounds
        if ($globalWinRate < 0) $globalWinRate = 0;
        if ($globalWinRate > 100) $globalWinRate = 100;
        
        // Check for user-specific win rate override
        $userWinRate = isset($trade['win_rate_override']) && $trade['win_rate_override'] !== null 
            ? (int)$trade['win_rate_override'] 
            : $globalWinRate;
        
        // Ensure user win rate is also within bounds
        if ($userWinRate < 0) $userWinRate = 0;
        if ($userWinRate > 100) $userWinRate = 100;
        
        // Special cases for 0% and 100%
        if ($userWinRate === 0) {
            return 'lost'; // 0% = All trades lose
        }
        if ($userWinRate === 100) {
            return 'won'; // 100% = All trades win
        }
        
        // Random determination based on win rate
        $random = mt_rand(1, 100);
        $shouldWin = $random <= $userWinRate;
        
        // Apply manipulation for LIVE trades only
        if ($shouldWin && $naturalResult === 'lost') {
            return 'won'; // Override to win
        } elseif (!$shouldWin && $naturalResult === 'won') {
            return 'lost'; // Override to lose
        }
        
        return $naturalResult;
    }
    
    /**
     * Send trade notification
     */
    private function sendTradeNotification($userId, $trade, $result, $profit) {
        $title = $result === 'won' ? 'Trade Won!' : ($result === 'lost' ? 'Trade Lost' : 'Trade Tie');
        $message = "Your {$trade['direction']} trade on {$trade['symbol']} has ended. ";
        
        if ($result === 'won') {
            $message .= "You won " . formatCurrency($profit) . "!";
        } elseif ($result === 'lost') {
            $message .= "You lost " . formatCurrency(abs($profit)) . ".";
        } else {
            $message .= "It was a tie, your amount has been refunded.";
        }
        
        dbInsert('notifications', [
            'user_id' => $userId,
            'type' => 'trade',
            'title' => $title,
            'message' => $message,
            'link' => '/user/trades.php'
        ]);
    }
    
    /**
     * Process referral commission
     */
    private function processReferralCommission($userId, $tradeAmount) {
        $user = dbFetch("SELECT referred_by FROM users WHERE id = :id", ['id' => $userId]);
        
        if (!$user || !$user['referred_by']) {
            return;
        }
        
        $commissionPercent = getSetting('referral_commission', REFERRAL_COMMISSION_PERCENT);
        $commission = ($tradeAmount * $commissionPercent) / 100;
        
        if ($commission <= 0) {
            return;
        }
        
        dbQuery(
            "UPDATE user_wallets SET 
                balance = balance + :commission,
                referral_earnings = referral_earnings + :commission
             WHERE user_id = :user_id",
            ['commission' => $commission, 'user_id' => $user['referred_by']]
        );
        
        dbQuery(
            "UPDATE referrals SET 
                commission_earned = commission_earned + :commission,
                total_trades = total_trades + 1
             WHERE referrer_id = :referrer_id AND referred_id = :referred_id",
            [
                'commission' => $commission,
                'referrer_id' => $user['referred_by'],
                'referred_id' => $userId
            ]
        );
        
        $wallet = dbFetch("SELECT balance FROM user_wallets WHERE user_id = :id", ['id' => $user['referred_by']]);
        dbInsert('transactions', [
            'transaction_id' => generateTransactionId('REF'),
            'user_id' => $user['referred_by'],
            'type' => 'referral',
            'amount' => $commission,
            'balance_before' => $wallet['balance'] - $commission,
            'balance_after' => $wallet['balance'],
            'reference_type' => 'referral',
            'reference_id' => $userId,
            'description' => "Referral commission from user trade"
        ]);
    }
    
    /**
     * Get user's active trades
     */
    public function getActiveTrades($userId, $isDemo = false) {
        return dbFetchAll(
            "SELECT t.*, a.name as asset_name 
             FROM trades t 
             LEFT JOIN assets a ON t.asset_id = a.id 
             WHERE t.user_id = :user_id AND t.result = 'pending' AND t.is_demo = :is_demo
             ORDER BY t.end_time ASC",
            ['user_id' => $userId, 'is_demo' => $isDemo ? 1 : 0]
        );
    }
    
    /**
     * Get user's trade history
     */
    public function getTradeHistory($userId, $isDemo = false, $limit = 50, $offset = 0) {
        return dbFetchAll(
            "SELECT t.*, a.name as asset_name 
             FROM trades t 
             LEFT JOIN assets a ON t.asset_id = a.id 
             WHERE t.user_id = :user_id AND t.result != 'pending' AND t.is_demo = :is_demo
             ORDER BY t.closed_at DESC
             LIMIT :limit OFFSET :offset",
            ['user_id' => $userId, 'is_demo' => $isDemo ? 1 : 0, 'limit' => $limit, 'offset' => $offset]
        );
    }
    
    /**
     * Get available assets
     */
    public function getAssets() {
        return dbFetchAll(
            "SELECT * FROM assets WHERE status = 'active' ORDER BY sort_order ASC"
        );
    }
    
    /**
     * Get trade durations
     */
    public function getDurations() {
        return dbFetchAll(
            "SELECT * FROM trade_durations WHERE status = 'active' ORDER BY sort_order ASC"
        );
    }
    
    /**
     * Admin: Override trade result
     */
    public function overrideTrade($tradeId, $result, $adminId) {
        if (!in_array($result, ['won', 'lost'])) {
            return ['success' => false, 'error' => 'Invalid result'];
        }
        
        $trade = dbFetch("SELECT * FROM trades WHERE id = :id", ['id' => $tradeId]);
        
        if (!$trade) {
            return ['success' => false, 'error' => 'Trade not found'];
        }
        
        if ($trade['result'] !== 'pending') {
            return ['success' => false, 'error' => 'Trade already closed'];
        }
        
        dbUpdate('trades', [
            'admin_override' => 1,
            'override_result' => $result,
            'override_by' => $adminId
        ], 'id = :id', ['id' => $tradeId]);
        
        logActivity('admin', $adminId, 'trade_override', "Override trade #{$tradeId} to {$result}");
        
        return ['success' => true, 'message' => 'Trade override set successfully'];
    }
    
    /**
     * Admin: Set user win rate
     */
    public function setUserWinRate($userId, $winRate, $adminId) {
        if ($winRate < 0 || $winRate > 100) {
            return ['success' => false, 'error' => 'Win rate must be between 0 and 100'];
        }
        
        dbUpdate('users', [
            'win_rate_override' => $winRate
        ], 'id = :id', ['id' => $userId]);
        
        logActivity('admin', $adminId, 'win_rate_change', "Set user #{$userId} win rate to {$winRate}%");
        
        return ['success' => true, 'message' => 'User win rate updated'];
    }
}

// Helper function
function trading() {
    return TradingEngine::getInstance();
}
