import { config } from '../config/index.js';
import { IExchangeConnector } from '../connectors/types.js';
import { RiskEngine, RiskProfile } from './riskEngine.js';
import { MarketContext, RuleResult, SignalDirection } from '../strategies/rules/types.js';
import logger from '../utils/logger.js';
import { supabaseService } from './SupabaseService.js';
import { Notifier } from './notifier.js';
import { ApiServer } from './apiServer.js';
import { SymbolMapper } from '../utils/symbolMapper.js';

let deprecationWarned = false;

/**
 * @deprecated Legacy execution manager retained only for backward compatibility.
 * Use TradeExecutor + AutoTrader for all new execution flows.
 */
export class ExecutionManager {
    private riskEngine: RiskEngine;
    private notifier: Notifier;
    private activeTraders: Map<string, any> = new Map(); // Symbol -> Position Details
    private cooldowns: Map<string, number> = new Map(); // Symbol -> Timestamp of last exit

    constructor(
        private exchange: IExchangeConnector,
        private apiServer?: ApiServer,
        private userId: string = 'global' // Pass 'global' or user UUID
    ) {
        const allowLegacy = String(process.env.ALLOW_LEGACY_EXECUTION_MANAGER || '').trim().toLowerCase() === 'true';
        if (!allowLegacy) {
            throw new Error(
                '[ExecutionManager] CRITICAL: This class is DEPRECATED and disabled by default. ' +
                'Set ALLOW_LEGACY_EXECUTION_MANAGER=true only for controlled test tooling. ' +
                'Use TradeExecutor and AutoTrader for runtime flows.'
            );
        }

        this.riskEngine = new RiskEngine();
        this.notifier = new Notifier();
        if (!deprecationWarned) {
            logger.warn('[ExecutionManager] Legacy execution manager enabled for compatibility/testing only.');
            deprecationWarned = true;
        }
    }

    /**
     * Attempts to execute a trade based on a strategy result.
     */
    /**
     * Attempts to execute a trade based on a strategy result.
     */
    public async handleSignal(symbol: string, result: RuleResult, context: MarketContext) {
        if (!config.ENABLE_TRADING) {
            logger.info(`[Execution] 💡 Trading is DISABLED. Alert only for ${symbol}.`);
            return;
        }

        const activePos = this.activeTraders.get(symbol);

        // --- EXIT LOGIC (If in trade) ---
        if (activePos) {
            // 1. Exit on Signal Flip (Trend Reversal)
            const isOpposite = (activePos.side === SignalDirection.BUY && result.signal === SignalDirection.SELL) ||
                (activePos.side === SignalDirection.SELL && result.signal === SignalDirection.BUY);

            if (isOpposite) {
                await this.executeExit(symbol, context.currentPrice, 'Strategy Signal Flip');
                return;
            }

            // 2. Exit on Trend Dissipation (Neutral after Profit Target)
            const profitPercent = ((context.currentPrice - activePos.entryPrice) / activePos.entryPrice) * 100 * (activePos.side === SignalDirection.BUY ? 1 : -1);
            if (profitPercent >= 1.0 && result.signal === SignalDirection.NONE) {
                await this.executeExit(symbol, context.currentPrice, 'Trend Neutralized (Profit Target Met)');
                return;
            }

            // Update price peaks for trailing logic
            if (activePos.side === SignalDirection.BUY) {
                activePos.peakPrice = Math.max(activePos.peakPrice || 0, context.currentPrice);
            } else {
                activePos.peakPrice = Math.min(activePos.peakPrice || 999999, context.currentPrice);
            }

            return; // Still in valid trade
        }

        if (result.signal === SignalDirection.NONE || !result.passed) {
            return;
        }

        // --- ENTRY LOGIC ---
        // 1. Check Global Max Open Trades Limit
        if (this.activeTraders.size >= config.MAX_OPEN_TRADES) {
            logger.info(`[Execution] 🛑 Max open trades reached (${config.MAX_OPEN_TRADES}). Skipping entry for ${symbol}.`);
            return;
        }

        // 2. Check Cooldown (Default 1 hour after exit)
        const lastExit = this.cooldowns.get(symbol) || 0;
        const cooldownPeriod = 3600000; // 1 Hour
        if (Date.now() - lastExit < cooldownPeriod) {
            logger.info(`[Execution] 💤 ${symbol} is in cooldown. Skipping entry.`);
            return;
        }

        // 3. Check for existing position on exchange (Safety Lock)
        try {
            const tradeSymbol = SymbolMapper.toTradeSymbol(symbol, config.EXECUTION_PROVIDER);
            const position = await this.exchange.getPosition(tradeSymbol);
            if (position) {
                logger.warn(`[Execution] ⚠️ Found existing position on exchange for ${symbol} (Mapped: ${tradeSymbol}). Locking local state.`);
                this.activeTraders.set(symbol, {
                    side: position.side?.toUpperCase(),
                    entryPrice: parseFloat(position.avg_entry_price),
                    size: parseFloat(position.qty),
                    stopLoss: 0,
                    takeProfit: 0,
                    peakPrice: parseFloat(position.avg_entry_price)
                });
                return;
            }
        } catch (e) {
            logger.error(`[Execution] Error checking position for ${symbol}: ${e}`);
        }

        // 4. Calculate Risk-Adjusted Position
        const riskProfile = await this.riskEngine.calculateRiskProfile(symbol, result.signal as SignalDirection, context);
        if (!riskProfile) {
            logger.warn(`[Execution] ❌ Failed to calculate risk profile for ${symbol}.`);
            return;
        }

        // 5. Place Order
        try {
            const tradeSymbol = SymbolMapper.toTradeSymbol(symbol, config.EXECUTION_PROVIDER);
            logger.info(`[Execution] 🚀 Executing ${riskProfile.action} for ${symbol} (Mapped: ${tradeSymbol})...`);
            logger.info(`[Execution] Details: Qty=${riskProfile.positionSize.toFixed(4)}, SL=${riskProfile.stopLoss.toFixed(2)}, TP Trigger=${riskProfile.takeProfit.toFixed(2)}`);

            const order = await this.exchange.placeOrder(
                tradeSymbol,
                riskProfile.action === SignalDirection.BUY ? 'buy' : 'sell',
                riskProfile.positionSize,
                'market',
                undefined,
                undefined,
                undefined
            );

            if (order) {
                const posData = {
                    side: riskProfile.action,
                    entryPrice: context.currentPrice,
                    size: riskProfile.positionSize,
                    stopLoss: riskProfile.stopLoss,
                    takeProfit: riskProfile.takeProfit, // Trigger for profit guard
                    peakPrice: context.currentPrice
                };
                this.activeTraders.set(symbol, posData);

                // Log order to database
                if (this.userId !== 'global') {
                    supabaseService.logOrder({
                        user_id: this.userId,
                        order_id: order.id || undefined,
                        symbol,
                        type: 'Market',
                        side: riskProfile.action,
                        qty: riskProfile.positionSize,
                        price: context.currentPrice,
                        status: 'Filled',
                        timestamp: Date.now()
                    });
                }

                // Update Dashboard Orders
                if (this.apiServer) {
                    this.apiServer.updateOrders([{
                        id: Math.random().toString(36).substring(7),
                        symbol: symbol,
                        type: 'Market',
                        side: riskProfile.action,
                        qty: riskProfile.positionSize,
                        price: context.currentPrice,
                        status: 'Filled',
                        timestamp: Date.now()
                    }]);
                }

                await this.notifier.sendAlert(`🚀 **TRADE EXECUTED**\nSymbol: ${symbol}\nAction: ${riskProfile.action}\nQty: ${riskProfile.positionSize.toFixed(4)}\nSL: ${riskProfile.stopLoss.toFixed(2)}\nMonitoring for 1%+ Runs...`);
            }

        } catch (error) {
            logger.error(`[Execution] ❌ Failed to execute trade for ${symbol}: ${error}`);
            await this.notifier.sendAlert(`❌ **EXECUTION FAILED**\nSymbol: ${symbol}\nError: ${error}`);
        }
    }

    /**
     * Forcefully exits a trade (Market Order).
     */
    public async executeExit(symbol: string, currentPrice: number, reason: string = 'Target Reached') {
        const pos = this.activeTraders.get(symbol);
        if (!pos) return;

        try {
            const tradeSymbol = SymbolMapper.toTradeSymbol(symbol, config.EXECUTION_PROVIDER);
            logger.info(`[Execution] 🚪 EXITING ${symbol} (Mapped: ${tradeSymbol}) | Reason: ${reason} | Price: ${currentPrice}`);

            const order = await this.exchange.placeOrder(
                tradeSymbol,
                pos.side === SignalDirection.BUY ? 'sell' : 'buy',
                pos.size,
                'market',
                undefined,
                undefined,
                undefined
            );

            if (order) {
                await this.notifier.sendAlert(`🚪 **TRADE CLOSED**\nSymbol: ${symbol}\nReason: ${reason}\nPrice: ${currentPrice}`);
                this.markTradeComplete(symbol, currentPrice, reason);
            }
        } catch (error) {
            logger.error(`[Execution] ❌ Failed to execute exit for ${symbol}: ${error}`);
            await this.notifier.sendAlert(`❌ **EXIT FAILED**\nSymbol: ${symbol}\nError: ${error}`);
        }
    }

    /**
     * Call this when a trade exit is detected (via TradeMonitor or Manual)
     */
    public markTradeComplete(symbol: string, exitPrice?: number, reason: string = 'Target Reached') {
        const pos = this.activeTraders.get(symbol);
        if (pos && exitPrice && this.apiServer) {
            const pnl = (exitPrice - pos.entryPrice) * pos.size * (pos.side === SignalDirection.BUY ? 1 : -1);
            const pnlPercent = ((exitPrice - pos.entryPrice) / pos.entryPrice) * 100 * (pos.side === SignalDirection.BUY ? 1 : -1);

            this.apiServer.addHistory({
                symbol,
                side: pos.side,
                entryPrice: pos.entryPrice,
                exitPrice,
                size: pos.size,
                pnl,
                pnlPercent,
                reason: reason,
                timestamp: Date.now()
            });
        }

        if (pos && exitPrice) {
            const pnl = (exitPrice - pos.entryPrice) * pos.size * (pos.side === SignalDirection.BUY ? 1 : -1);
            const pnlPercent = ((exitPrice - pos.entryPrice) / pos.entryPrice) * 100 * (pos.side === SignalDirection.BUY ? 1 : -1);

            // Log to Supabase
            if (this.userId !== 'global') {
                supabaseService.logTransaction({
                    user_id: this.userId,
                    symbol,
                    side: pos.side,
                    entry_price: pos.entryPrice,
                    exit_price: exitPrice,
                    size: pos.size,
                    pnl,
                    pnl_percent: pnlPercent,
                    reason,
                    timestamp: Date.now()
                });
            }
        }

        this.activeTraders.delete(symbol);
        this.cooldowns.set(symbol, Date.now());
        logger.info(`[Execution] ✅ Trade complete for ${symbol}. Cooldown started.`);
    }

    /**
     * Synchronizes local state with exchange positions (Startup Recovery)
     */
    public async syncPositions(symbols: string[]) {
        logger.info(`[Execution] 🔄 Synchronizing positions on startup for ${symbols.length} symbols...`);
        for (const symbol of symbols) {
            try {
                const tradeSymbol = SymbolMapper.toTradeSymbol(symbol, config.EXECUTION_PROVIDER);
                logger.info(`[Execution] Checking exchange for ${symbol} (Mapped: ${tradeSymbol})...`);
                const position = await this.exchange.getPosition(tradeSymbol);

                if (position) {
                    const side = position.side?.toLowerCase();
                    const finalSide = (side === 'long' || side === 'buy') ? SignalDirection.BUY : SignalDirection.SELL;

                    logger.info(`[Execution] ✅ Found existing ${side} position for ${symbol}. Recovering as ${finalSide} | Price: ${position.avg_entry_price} | Qty: ${position.qty}`);
                    this.activeTraders.set(symbol, {
                        side: finalSide,
                        entryPrice: parseFloat(position.avg_entry_price),
                        size: parseFloat(position.qty),
                        stopLoss: 0, // Recalculated locally during loop
                        takeProfit: 0,
                        peakPrice: parseFloat(position.avg_entry_price)
                    });
                } else {
                    logger.info(`[Execution] No active position found for ${symbol} on exchange.`);
                }
            } catch (e) {
                logger.error(`[Execution] Failed to sync ${symbol}: ${e}`);
            }
        }
    }

    public isSymbolLocked(symbol: string): boolean {
        return this.activeTraders.has(symbol);
    }

    public getActiveSymbols(): string[] {
        return Array.from(this.activeTraders.keys());
    }

    /**
     * Executes a manual trade triggered via API/Dashboard.
     */
    public async executeManualTrade(symbol: string, side: 'buy' | 'sell', qty: number, type: 'market' | 'limit' = 'market', price?: number) {
        logger.info(`[Execution] 🖐️ Manual Trade Request: ${side.toUpperCase()} ${symbol} | Qty: ${qty} | Type: ${type}`);

        try {
            const tradeSymbol = SymbolMapper.toTradeSymbol(symbol, config.EXECUTION_PROVIDER);

            // Check for existing position if opening a new one
            // (Optional: You might want to allow adding to a position manually, so we won't strictly block it, but we update tracking)

            const order = await this.exchange.placeOrder(
                tradeSymbol,
                side,
                qty,
                type,
                price,
                undefined,
                undefined,
                undefined
            );

            if (order) {
                const signalSide = side === 'buy' ? SignalDirection.BUY : SignalDirection.SELL;

                // If this is an ENTRY or Adding to position
                const currentPrice = price || order.filled_avg_price || 0; // Fallback if market order filling isn't instant

                // Update local tracking
                this.activeTraders.set(symbol, {
                    side: signalSide,
                    entryPrice: currentPrice, // Note: This might overwrite existing average entry price tracking, effectively averaging it loosely
                    size: qty, // Simplified: In a real system we'd sum it up
                    stopLoss: 0,
                    takeProfit: 0,
                    peakPrice: currentPrice
                });

                // Log to Supabase
                if (this.userId !== 'global') {
                    supabaseService.logOrder({
                        user_id: this.userId,
                        order_id: order.id,
                        symbol,
                        type: type === 'market' ? 'Market' : 'Limit',
                        side: signalSide,
                        qty: qty,
                        price: currentPrice,
                        status: 'Filled', // Simplified assumption for market orders
                        timestamp: Date.now()
                    });
                }

                // Update Dashboard
                if (this.apiServer) {
                    this.apiServer.updateOrders([{
                        id: order.id || Math.random().toString(36).substring(7),
                        symbol: symbol,
                        type: type === 'market' ? 'Market' : 'Limit',
                        side: signalSide,
                        qty: qty,
                        price: currentPrice,
                        status: order.repliesstatus || 'Filled',
                        timestamp: Date.now()
                    }]);
                }

                await this.notifier.sendAlert(`🖐️ **MANUAL TRADE EXECUTED**\nSymbol: ${symbol}\nSide: ${side.toUpperCase()}\nQty: ${qty}`);
                return { success: true, orderId: order.id };
            }
        } catch (error: any) {
            logger.error(`[Execution] ❌ Manual Execution Failed: ${error.message}`);
            return { success: false, error: error.message };
        }
    }
    public getActivePosition(symbol: string): any {
        return this.activeTraders.get(symbol) || null;
    }
}
