<?php
require_once 'Database.php';
require_once 'Security.php';
require_once 'Wallet.php';
require_once 'Escrow.php';

class Trading {
    private $db;
    private $wallet;
    private $escrow;
    
    public function __construct() {
        $this->db = new Database();
        $this->wallet = new Wallet();
        $this->escrow = new Escrow();
    }
    
    // Create sell order
    public function createSellOrder($sellerId, $cryptoId, $amount, $pricePerUnit, $paymentMethod, $paymentDetails) {
        try {
            $this->db->beginTransaction();
            
            // Validate minimum amount
            $crypto = $this->getCryptocurrency($cryptoId);
            if (!$crypto || $amount < $crypto['min_trade_amount']) {
                throw new Exception('Amount below minimum trade amount');
            }
            
            // Lock funds in seller's wallet
            $lockResult = $this->wallet->lockFunds($sellerId, $cryptoId, $amount);
            if (!$lockResult['success']) {
                throw new Exception($lockResult['message']);
            }
            
            // Calculate total and fees
            $totalAmount = $amount * $pricePerUnit;
            $tradingFee = $this->calculateTradingFee($cryptoId, $totalAmount);
            
            // Set expiration time
            $expiresAt = date('Y-m-d H:i:s', time() + (TRADE_TIMEOUT * 60));
            
            // Create trade record
            $this->db->query("INSERT INTO trades (seller_id, crypto_id, amount, price_per_unit, total_amount, trading_fee, payment_method, payment_details, expires_at) 
                             VALUES (:seller_id, :crypto_id, :amount, :price, :total, :fee, :payment_method, :payment_details, :expires_at)");
            
            $this->db->bind(':seller_id', $sellerId);
            $this->db->bind(':crypto_id', $cryptoId);
            $this->db->bind(':amount', $amount);
            $this->db->bind(':price', $pricePerUnit);
            $this->db->bind(':total', $totalAmount);
            $this->db->bind(':fee', $tradingFee);
            $this->db->bind(':payment_method', $paymentMethod);
            $this->db->bind(':payment_details', $paymentDetails);
            $this->db->bind(':expires_at', $expiresAt);
            
            if (!$this->db->execute()) {
                throw new Exception('Failed to create sell order');
            }
            
            $tradeId = $this->db->lastInsertId();
            
            $this->db->endTransaction();
            
            Security::logSecurityEvent('SELL_ORDER_CREATED', "Created sell order for $amount {$crypto['symbol']}", $sellerId);
            
            return [
                'success' => true, 
                'message' => 'Sell order created successfully',
                'trade_id' => $tradeId
            ];
            
        } catch (Exception $e) {
            $this->db->cancelTransaction();
            error_log('Create sell order error: ' . $e->getMessage());
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }
    
    // Create buy order (match with sell order)
    public function createBuyOrder($buyerId, $tradeId) {
        try {
            $this->db->beginTransaction();
            
            // Get trade details
            $trade = $this->getTradeById($tradeId);
            if (!$trade || $trade['status'] !== 'open') {
                throw new Exception('Trade not available');
            }
            
            // Check if trade has expired
            if (strtotime($trade['expires_at']) < time()) {
                $this->cancelExpiredTrade($tradeId);
                throw new Exception('Trade has expired');
            }
            
            // Prevent self-trading
            if ($trade['seller_id'] == $buyerId) {
                throw new Exception('Cannot buy from yourself');
            }
            
            // Update trade with buyer
            $this->db->query("UPDATE trades SET buyer_id = :buyer_id, status = 'matched' WHERE id = :trade_id AND status = 'open'");
            $this->db->bind(':buyer_id', $buyerId);
            $this->db->bind(':trade_id', $tradeId);
            
            if (!$this->db->execute() || $this->db->rowCount() === 0) {
                throw new Exception('Failed to match trade');
            }
            
            // Create escrow entry
            $escrowResult = $this->escrow->createEscrow($tradeId, $trade['amount'], $trade['crypto_id']);
            if (!$escrowResult['success']) {
                throw new Exception($escrowResult['message']);
            }
            
            // Update trade status to in_escrow
            $this->db->query("UPDATE trades SET status = 'in_escrow', escrow_address = :escrow_address WHERE id = :trade_id");
            $this->db->bind(':escrow_address', $escrowResult['escrow_address']);
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
            
            $this->db->endTransaction();
            
            Security::logSecurityEvent('BUY_ORDER_CREATED', "Matched with sell order #$tradeId", $buyerId);
            
            return [
                'success' => true, 
                'message' => 'Buy order matched successfully. Please complete payment.',
                'trade_id' => $tradeId,
                'payment_details' => $trade['payment_details']
            ];
            
        } catch (Exception $e) {
            $this->db->cancelTransaction();
            error_log('Create buy order error: ' . $e->getMessage());
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }
    
    // Mark payment as sent (buyer)
    public function markPaymentSent($tradeId, $buyerId) {
        try {
            $trade = $this->getTradeById($tradeId);
            
            if (!$trade || $trade['buyer_id'] != $buyerId || $trade['status'] !== 'in_escrow') {
                return ['success' => false, 'message' => 'Invalid trade'];
            }
            
            $this->db->query("UPDATE trades SET status = 'payment_pending' WHERE id = :trade_id");
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
            
            Security::logSecurityEvent('PAYMENT_SENT', "Payment marked as sent for trade #$tradeId", $buyerId);
            
            return ['success' => true, 'message' => 'Payment marked as sent. Waiting for seller confirmation.'];
            
        } catch (Exception $e) {
            error_log('Mark payment sent error: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Failed to update payment status'];
        }
    }
    
    // Confirm payment received (seller)
    public function confirmPaymentReceived($tradeId, $sellerId) {
        try {
            $this->db->beginTransaction();
            
            $trade = $this->getTradeById($tradeId);
            
            if (!$trade || $trade['seller_id'] != $sellerId || $trade['status'] !== 'payment_pending') {
                throw new Exception('Invalid trade');
            }
            
            // Release escrow funds to buyer
            $escrowResult = $this->escrow->releaseEscrow($tradeId, $sellerId, 'Payment confirmed by seller');
            
            if (!$escrowResult['success']) {
                throw new Exception($escrowResult['message']);
            }
            
            // Update trade status
            $this->db->query("UPDATE trades SET status = 'completed', completed_at = NOW() WHERE id = :trade_id");
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
            
            // Deduct trading fee from seller
            if ($trade['trading_fee'] > 0) {
                $this->deductTradingFee($sellerId, $trade['crypto_id'], $trade['trading_fee'], $tradeId);
            }
            
            $this->db->endTransaction();
            
            Security::logSecurityEvent('PAYMENT_CONFIRMED', "Payment confirmed for trade #$tradeId", $sellerId);
            
            return ['success' => true, 'message' => 'Trade completed successfully!'];
            
        } catch (Exception $e) {
            $this->db->cancelTransaction();
            error_log('Confirm payment error: ' . $e->getMessage());
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }
    
    // Cancel trade
    public function cancelTrade($tradeId, $userId, $reason = 'User cancelled') {
        try {
            $this->db->beginTransaction();
            
            $trade = $this->getTradeById($tradeId);
            
            if (!$trade) {
                throw new Exception('Trade not found');
            }
            
            // Check if user can cancel this trade
            if ($trade['seller_id'] != $userId && $trade['buyer_id'] != $userId) {
                throw new Exception('Unauthorized');
            }
            
            // Can only cancel if not completed
            if ($trade['status'] === 'completed') {
                throw new Exception('Cannot cancel completed trade');
            }
            
            // Handle escrow refund if trade is in escrow
            if (in_array($trade['status'], ['in_escrow', 'payment_pending'])) {
                $escrowResult = $this->escrow->refundEscrow($tradeId, $userId, $reason);
                if (!$escrowResult['success']) {
                    throw new Exception($escrowResult['message']);
                }
            } else {
                // Unlock seller's funds for open trades
                $unlockResult = $this->wallet->unlockFunds($trade['seller_id'], $trade['crypto_id'], $trade['amount']);
                if (!$unlockResult['success']) {
                    throw new Exception($unlockResult['message']);
                }
            }
            
            // Update trade status
            $this->db->query("UPDATE trades SET status = 'cancelled' WHERE id = :trade_id");
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
            
            $this->db->endTransaction();
            
            Security::logSecurityEvent('TRADE_CANCELLED', "Trade #$tradeId cancelled: $reason", $userId);
            
            return ['success' => true, 'message' => 'Trade cancelled successfully'];
            
        } catch (Exception $e) {
            $this->db->cancelTransaction();
            error_log('Cancel trade error: ' . $e->getMessage());
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }
    
    // Get available trades (buy orders)
    public function getAvailableTrades($cryptoId = null, $limit = 50) {
        $query = "SELECT t.*, u.username as seller_username, c.symbol, c.name 
                  FROM trades t 
                  JOIN users u ON t.seller_id = u.id 
                  JOIN cryptocurrencies c ON t.crypto_id = c.id 
                  WHERE t.status = 'open' AND t.expires_at > NOW()";
        
        if ($cryptoId) {
            $query .= " AND t.crypto_id = :crypto_id";
        }
        
        $query .= " ORDER BY t.price_per_unit ASC LIMIT :limit";
        
        $this->db->query($query);
        
        if ($cryptoId) {
            $this->db->bind(':crypto_id', $cryptoId);
        }
        
        $this->db->bind(':limit', $limit);
        return $this->db->resultSet();
    }
    
    // Get user's trades
    public function getUserTrades($userId, $status = null) {
        $query = "SELECT t.*, 
                         seller.username as seller_username,
                         buyer.username as buyer_username,
                         c.symbol, c.name 
                  FROM trades t 
                  LEFT JOIN users seller ON t.seller_id = seller.id 
                  LEFT JOIN users buyer ON t.buyer_id = buyer.id 
                  JOIN cryptocurrencies c ON t.crypto_id = c.id 
                  WHERE (t.seller_id = :user_id OR t.buyer_id = :user_id)";
        
        if ($status) {
            $query .= " AND t.status = :status";
        }
        
        $query .= " ORDER BY t.created_at DESC";
        
        $this->db->query($query);
        $this->db->bind(':user_id', $userId);
        
        if ($status) {
            $this->db->bind(':status', $status);
        }
        
        return $this->db->resultSet();
    }
    
    // Get trade by ID
    public function getTradeById($tradeId) {
        $this->db->query("SELECT t.*, 
                         seller.username as seller_username, seller.email as seller_email,
                         buyer.username as buyer_username, buyer.email as buyer_email,
                         c.symbol, c.name 
                         FROM trades t 
                         LEFT JOIN users seller ON t.seller_id = seller.id 
                         LEFT JOIN users buyer ON t.buyer_id = buyer.id 
                         JOIN cryptocurrencies c ON t.crypto_id = c.id 
                         WHERE t.id = :trade_id");
        $this->db->bind(':trade_id', $tradeId);
        return $this->db->single();
    }
    
    // Calculate trading fee
    private function calculateTradingFee($cryptoId, $totalAmount) {
        $this->db->query("SELECT * FROM trading_fees WHERE crypto_id = :crypto_id AND is_active = 1");
        $this->db->bind(':crypto_id', $cryptoId);
        $feeStructure = $this->db->single();
        
        if (!$feeStructure) {
            return 0;
        }
        
        $fee = $totalAmount * ($feeStructure['fee_percentage'] / 100);
        $fee = max($fee, $feeStructure['minimum_fee']);
        $fee = min($fee, $feeStructure['maximum_fee']);
        
        return $fee;
    }
    
    // Deduct trading fee
    private function deductTradingFee($userId, $cryptoId, $feeAmount, $tradeId) {
        // This would typically be deducted in the native currency or a fee token
        // For simplicity, we'll record it as a transaction
        $this->db->query("INSERT INTO transactions (user_id, crypto_id, transaction_type, amount, reference_type, reference_id, notes, status) 
                         VALUES (:user_id, :crypto_id, 'fee', :amount, 'trade', :trade_id, 'Trading fee', 'confirmed')");
        
        $this->db->bind(':user_id', $userId);
        $this->db->bind(':crypto_id', $cryptoId);
        $this->db->bind(':amount', -$feeAmount);
        $this->db->bind(':trade_id', $tradeId);
        $this->db->execute();
    }
    
    // Generate escrow address
    // Moved to Escrow class
    
    // Get cryptocurrency info
    private function getCryptocurrency($cryptoId) {
        $this->db->query("SELECT * FROM cryptocurrencies WHERE id = :id");
        $this->db->bind(':id', $cryptoId);
        return $this->db->single();
    }
    
    // Cancel expired trades (should be run as a cron job)
    public function cancelExpiredTrades() {
        try {
            $this->db->beginTransaction();
            
            // Get expired trades
            $this->db->query("SELECT * FROM trades WHERE status = 'open' AND expires_at < NOW()");
            $expiredTrades = $this->db->resultSet();
            
            foreach ($expiredTrades as $trade) {
                $this->cancelExpiredTrade($trade['id']);
            }
            
            $this->db->endTransaction();
            
            return count($expiredTrades);
            
        } catch (Exception $e) {
            $this->db->cancelTransaction();
            error_log('Cancel expired trades error: ' . $e->getMessage());
            return 0;
        }
    }
    
    // Cancel single expired trade
    private function cancelExpiredTrade($tradeId) {
        $trade = $this->getTradeById($tradeId);
        
        if ($trade && $trade['status'] === 'open') {
            // Unlock seller's funds
            $this->wallet->unlockFunds($trade['seller_id'], $trade['crypto_id'], $trade['amount']);
            
            // Update trade status
            $this->db->query("UPDATE trades SET status = 'cancelled' WHERE id = :trade_id");
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
        } elseif ($trade && in_array($trade['status'], ['in_escrow', 'payment_pending'])) {
            // Use escrow system for trades in escrow
            $this->escrow->refundEscrow($tradeId, null, 'Trade expired automatically');
            
            // Update trade status
            $this->db->query("UPDATE trades SET status = 'cancelled' WHERE id = :trade_id");
            $this->db->bind(':trade_id', $tradeId);
            $this->db->execute();
        }
    }
    
    // Get trading statistics
    public function getTradingStats($userId = null) {
        $stats = [];
        
        $whereClause = $userId ? "WHERE seller_id = :user_id OR buyer_id = :user_id" : "";
        
        // Total trades
        $this->db->query("SELECT COUNT(*) as total_trades FROM trades $whereClause");
        if ($userId) $this->db->bind(':user_id', $userId);
        $totalTrades = $this->db->single();
        $stats['total_trades'] = $totalTrades['total_trades'];
        
        // Trades by status
        $this->db->query("SELECT status, COUNT(*) as count FROM trades $whereClause GROUP BY status");
        if ($userId) $this->db->bind(':user_id', $userId);
        $statusStats = $this->db->resultSet();
        
        foreach ($statusStats as $stat) {
            $stats['by_status'][$stat['status']] = $stat['count'];
        }
        
        // Total volume
        $this->db->query("SELECT SUM(total_amount) as total_volume FROM trades WHERE status = 'completed' $whereClause");
        if ($userId) $this->db->bind(':user_id', $userId);
        $volume = $this->db->single();
        $stats['total_volume'] = $volume['total_volume'] ?? 0;
        
        return $stats;
    }
}
?>