import { EntryTriggerRule } from '../src/strategies/rules/EntryTriggerRule.js';
import { ProStrategyEngine } from '../src/strategies/ProStrategyEngine.js';
import { IExchangeConnector, Candle } from '../src/connectors/types.js';
import { config } from '../src/config/index.js';
import logger from '../src/utils/logger.js';

// --- MOCK EXCHANGE ---
class MockExchange implements IExchangeConnector {
    private candles4h: Candle[];
    private candles1h: Candle[];
    private candles15m: Candle[]; // Added for 15m specific data

    constructor(data: { '4h': Candle[], '1h': Candle[], '15m'?: Candle[] }) {
        this.candles4h = data['4h'];
        this.candles1h = data['1h'];
        this.candles15m = data['15m'] || []; // Initialize 15m, default to empty array if not provided
    }

    async fetchOHLCV(symbol: string, timeframe: string, since?: number): Promise<Candle[]> {
        if (timeframe === '4h') return this.candles4h;
        if (timeframe === '1h') return this.candles1h;
        if (timeframe === '15m') return this.candles15m.length > 0 ? this.candles15m : this.candles1h; // Mock 15m with 1h data if no specific 15m data is provided
        return [];
    }
    async placeOrder(symbol: string, side: 'buy' | 'sell', qty: number, type: 'market' | 'limit'): Promise<any> {
        return { id: 'mock-order' };
    }

    async getPosition(symbol: string): Promise<any> {
        return null;
    }
}

// --- HELPER TO GENERATE CANDLES ---
function generateTrend(length: number, startPrice: number, trend: 'up' | 'down' | 'flat', noise: number = 0.001): Candle[] {
    const candles: Candle[] = [];
    let price = startPrice;
    const now = Date.now();

    for (let i = 0; i < length; i++) {
        let change = 0;
        if (trend === 'up') change = price * 0.0005; // +0.05% per candle (Steady trend, not parabolic)
        if (trend === 'down') change = -price * 0.0005;

        // Add minimal noise
        price += change + (Math.random() - 0.5) * price * noise;

        candles.push({
            timestamp: now - (length - i) * 60 * 60 * 1000,
            open: price,
            high: price * 1.001,
            low: price * 0.999,
            close: price,
            volume: 1000
        });
    }
    return candles;
}

// --- TEST RUNNER ---
async function runTest() {
    logger.info('--- STARTING PRO STRATEGY VERIFICATION ---');

    // SCENARIO 1: STRONG UPTREND (Expect BUY)
    // 4H: Uptrend
    // 1H: Uptrend + Pullback? 
    // Session: Force Session window to active

    // Mock Session & RSI Limits for Test
    const currentHour = new Date().getUTCHours();
    config.PRO_STRATEGY.PARAMETERS.SESSION_WINDOWS = [{ start: 0, end: 24 }];
    config.PRO_STRATEGY.PARAMETERS.RSI_OVERBOUGHT = 101; // Allow strong trend for test case

    const candles4h_up = generateTrend(300, 50000, 'up'); // 50000 -> 80000
    const candles1h_up = generateTrend(100, 80000, 'up');

    // Inject Pullback in last few candles of 1H to satisfy ZoneRule if purely momentum
    // Actually ZoneRule says "Price <= EMA20 * 1.5%".
    // MomentumRule says "RSI > 50".

    // Creating Mock Exchange
    const mockExchange = new MockExchange({
        '4h': candles4h_up,
        '1h': candles1h_up
    });

    const engine = new ProStrategyEngine(mockExchange);
    const result = await engine.execute('BTC/USD');

    logger.info(`\n[TEST 1] UPTREND SCENARIO RESULT:`);
    if (result?.passed) {
        logger.info(`✅ PASSED -> Signal: ${result.signal}`);
        logger.info(`\n--- DETAILED RULE BREAKDOWN ---`);
        logger.info(result.reason);
        logger.info(`-------------------------------\n`);
    } else {
        logger.error(`❌ FAILED -> ${result?.ruleName}: ${result?.reason}`);
    }

    // SCENARIO 2: MOMENTUM MISMATCH (Expect FAIL)
    // 4H: Uptrend
    // 1H: Sharp Drop (Bearish Momentum)
    const candles1h_crash = generateTrend(100, 80000, 'down');

    const mockExchange2 = new MockExchange({
        '4h': candles4h_up, // 4H says UP
        '1h': candles1h_crash // 1H says DOWN
    });

    const engine2 = new ProStrategyEngine(mockExchange2);
    const resultConflict = await engine2.execute('BTC/USD');

    logger.info(`\n[TEST 2] CONFLICT SCENARIO RESULT:`);
    if (resultConflict?.passed) {
        logger.error(`❌ FAILED -> Should have skipped due to conflict!`);
    } else {
        logger.info(`✅ CORRECTLY FAILED -> ${resultConflict?.ruleName}: ${resultConflict?.reason}`);
    }

    // --- TEST 3: ENTRY TRIGGER SCENARIO ---
    logger.info(`\n[TEST 3] ENTRY TRIGGER SCENARIO: EMA RECLAIM`);

    // 1. Uptrend Base
    const candlesReclaim = generateTrend(100, 80000, 'up');
    const len = candlesReclaim.length;

    // 2. Manipulate candles for Reclaim (Dip -> Reclaim)
    // We strictly test CLOSED candles (Engine slices off the last one)
    // So we need to modify [len-3] (Prev Closed) and [len-2] (Last Closed)

    candlesReclaim[len - 3].close = 79000; // Dip below EMA (~83k)
    candlesReclaim[len - 2].close = 85000; // Reclaim above EMA

    // Build Context
    // Now expects (4h, 1h, 15m)
    // For this test, we can just pass the "Trend" candles as 15m instructions too if we wanted, 
    // or just pass dummy data for 15m if the rule we are testing (EntryTrigger) uses the "Execution" timeframe using 1h for now?
    // Wait, we haven't updated the rules yet. They probably still use 1h explicitly.
    // Ideally we pass valid 15m data. Let's reuse the Reclaim candles for 15m slot if we are testing the reclaim logic.

    const contextReclaim = engine['buildContext'](candles4h_up, candlesReclaim, candlesReclaim);
    // Force Context Price to match last closed candle
    contextReclaim.currentPrice = 85000;

    // Verify Trigger
    const resultReclaim = await new EntryTriggerRule().check(contextReclaim);

    if (resultReclaim.passed) {
        logger.info(`✅ PASSED -> Signal: ${resultReclaim.signal} | ${resultReclaim.reason}`);
    } else {
        // Debug info
        logger.error(`❌ FAILED -> ${resultReclaim.reason}`);
        logger.info(`Debug: Prev Close: ${candlesReclaim[len - 2].close}, Curr Close: ${candlesReclaim[len - 1].close}, EMA: ${contextReclaim.ema20_1h}`);
    }

    // --- TEST 4: SCALPING 15m VERIFICATION ---
    logger.info(`\n[TEST 4] SCALPING 15m VERIFICATION`);

    // Switch to 15m Execution
    config.PRO_STRATEGY.PARAMETERS.EXECUTION_TIMEFRAME = '15m';

    // Scenario:
    // 1H is Bearish (RSI < 50) -> If we used 1H, Momentum would Fail.
    // 15m is Bullish (RSI > 50) -> If we use 15m, Momentum should Pass.

    // Generate 1H Bearish Trend
    const candles1h_bearish = generateTrend(100, 80000, 'down'); // RSI likely < 30 or low

    // Generate 15m Bullish Trend (Rally)
    const candles15m_bullish = generateTrend(100, 80000, 'up'); // RSI likely > 70

    // Need to ensure 15m RSI is healthy (e.g. 60), not Overbought (>70 is default overbought check?)
    // generateTrend 'up' usually creates very strong trend (RSI 100).
    // Let's temper the 15m trend to be "Healthy" (RSI ~ 60).
    // Or just check that it passes "Bullish Momentum" but fails "Overbought".
    // Wait, generateTrend makes RSI 100 which is Overbought.
    // Let's manually set the 15m candles to be flat then rising slightly.

    // Actually, simply:
    // 1H = RSI 40 (Sell)
    // 15m = RSI 60 (Buy)

    // Let's trust that Momentum Rule checks RSI nicely.
    // If Logic is: Bias BUY -> Need Momentum BUY.
    // Bias is based on 4H (which is Uptrend in our context).
    // So we need Momentum to be Bullish.

    // If we use 1H (Bearish), it will fail.
    // If we use 15m (Bullish), it will pass.

    const contextScalp = engine['buildContext'](candles4h_up, candles1h_bearish, candles15m_bullish);
    // Overwrite RSI to be explicit to avoid "generateTrend" randomness
    contextScalp.rsi_1h = 40;   // Bearish
    contextScalp.rsi_15m = 60;  // Bullish

    // Also Zone Rule uses 15m.
    // 1H might be away from EMA, 15m might be close.
    contextScalp.ema20_1h = 90000; // Price 80000 -> Far away
    contextScalp.ema20_15m = 80000; // Price 80000 -> Close
    contextScalp.currentPrice = 80000;

    const momentumRule = engine['rules'].find((r: any) => r.name === 'MomentumRule');
    if (!momentumRule) throw new Error('MomentumRule not found');
    const resultScalp = await momentumRule.check(contextScalp);

    if (resultScalp.passed && resultScalp.signal === 'BUY') {
        logger.info(`✅ PASSED -> Scalping Logic used 15m RSI (${contextScalp.rsi_15m}) correctly.`);
    } else {
        logger.error(`❌ FAILED -> Scalping Logic failed. Reason: ${resultScalp.reason}`);
        logger.info(`Debug: 1H RSI: ${contextScalp.rsi_1h}, 15m RSI: ${contextScalp.rsi_15m}`);
    }
}

runTest().catch(console.error);
