import { config } from '../../config/index.js';
import { IRule, MarketContext, RuleResult, SignalDirection } from './types.js';
import { AIClient } from '../../services/aiClient.js';
import logger from '../../utils/logger.js';

export class AIAnalysisRule implements IRule {
    public name = 'AIAnalysisRule';
    private aiClient: AIClient;
    // Cache: Map<symbol, { evaluation: any, timestamp: number }>
    private static cache = new Map<string, { evaluation: any, timestamp: number }>();

    constructor() {
        this.aiClient = new AIClient();
    }

    public isEnabled(): boolean {
        // Only enabled if explicitly in list AND at least one API key exists for a provider in the fallback list
        const hasKey = !!config.AI.OPENAI_API_KEY || !!config.AI.GEMINI_API_KEY || !!config.AI.PERPLEXITY_API_KEY;
        return config.PRO_STRATEGY.ENABLED_RULES.includes('ai_analysis') && hasKey;
    }

    public async check(context: MarketContext, params?: Record<string, any>): Promise<RuleResult> {
        const symbol = context.symbol;
        const now = Date.now();
        const cacheEntry = AIAnalysisRule.cache.get(symbol);
        const cacheDuration = (config.AI.CACHE_HOURS || 4) * 60 * 60 * 1000;

        let evaluation: { action: string, confidence: number, reasoning: string };
        let source = 'Fresh';

        if (cacheEntry && (now - cacheEntry.timestamp < cacheDuration)) {
            evaluation = cacheEntry.evaluation;
            source = 'Cached';
            logger.info(`[AI] Using cached analysis for ${symbol} (from ${new Date(cacheEntry.timestamp).toISOString().split('T')[1].split('.')[0]} UTC)`);
        } else {
            const prompt = this.buildPrompt(context);
            logger.info(`[AI] Requesting fresh analysis for ${symbol}...`);
            const aiResponse = await this.aiClient.generateAnalysis(prompt);

            if (!aiResponse) {
                if (config.AI.FAIL_OPEN) {
                    return {
                        ruleName: this.name,
                        passed: true,
                        signal: SignalDirection.NONE,
                        reason: 'AI service unavailable; fail-open enabled, bypassing AI gate.',
                        metadata: {
                            ai_source: 'unavailable',
                            ai_fail_open: true
                        }
                    };
                }
                return {
                    ruleName: this.name,
                    passed: false,
                    signal: SignalDirection.NONE,
                    reason: 'AI service unavailable; AIAnalysisRule blocking per configured threshold.'
                };
            }

            evaluation = this.parseResponse(aiResponse);
            AIAnalysisRule.cache.set(symbol, { evaluation, timestamp: now });
        }

        const action = String(evaluation.action || '').toUpperCase();
        const signal = action === 'BUY' ? SignalDirection.BUY : (action === 'SELL' ? SignalDirection.SELL : SignalDirection.NONE);
        const threshold = this.normalizeConfidenceToPct(
            Number(params?.minConfidence ?? params?.confidenceThreshold ?? config.AI.CONFIDENCE_THRESHOLD ?? 0)
        );
        const confidence = this.normalizeConfidenceToPct(Number(evaluation.confidence || 0));
        const rawPassed = signal !== SignalDirection.NONE && confidence >= threshold;
        const passThrough = config.AI.FAIL_OPEN && signal === SignalDirection.NONE;
        const passed = rawPassed || passThrough;

        return {
            ruleName: this.name,
            passed,
            signal: signal,
            reason: passed
                ? passThrough
                    ? `[${source}] AI returned non-directional output; fail-open bypass applied.`
                    : `[${source}] AI Sentiment accepted: ${evaluation.action} (${confidence}%, threshold ${threshold}%)`
                : `[${source}] AI Sentiment rejected: ${evaluation.action} (${confidence}%, threshold ${threshold}%)`,
            metadata: {
                confidence,
                threshold,
                reasoning: evaluation.reasoning,
                ai_source: source,
                ai_timestamp: cacheEntry?.timestamp || now,
                ai_fail_open: passThrough
            }
        };
    }

    private buildPrompt(ctx: MarketContext): string {
        // Summarize data
        const candles = ctx.candles1h; // Use 1h for general context
        const last5 = candles.slice(-5).map(c =>
            `Time: ${new Date(c.timestamp).toISOString().split('T')[1]}, Close: ${c.close.toFixed(2)}, Vol: ${c.volume}`
        ).join('\n');

        return `
        Analyze the following Crypto Market Data (${ctx.symbol}) and provide a trading confidence score (0-100) for a TREND FOLLOWING trade.
        
        CONTEXT:
        - Trend (4H): ${ctx.ema50_4h && ctx.ema200_4h ? (ctx.ema50_4h > ctx.ema200_4h ? 'UPTREND' : 'DOWNTREND') : 'UNKNOWN'}
        - RSI (1H): ${ctx.rsi_1h?.toFixed(2)}
        - Current Price: ${ctx.currentPrice}
        - EMA20 (1H): ${ctx.ema20_1h?.toFixed(2)}
        
        RECENT PRICE ACTION (Last 5 Hours):
        ${last5}

        TASK:
        Return a JSON object with:
        - "action": "BUY", "SELL", or "HOLD"
        - "confidence": number (0-100)
        - "reasoning": string (short summary)
        
        JSON ONLY. NO MARKDOWN.
        `;
    }

    private normalizeConfidenceToPct(rawValue: number): number {
        if (!Number.isFinite(rawValue) || rawValue < 0) return 0;
        if (rawValue <= 1) return Math.round(rawValue * 10000) / 100;
        return Math.min(100, rawValue);
    }

    private parseResponse(response: string): { action: string, confidence: number, reasoning: string } {
        try {
            // Clean up markdown code blocks if present
            const clean = response.replace(/```json/g, '').replace(/```/g, '').trim();
            return JSON.parse(clean);
        } catch (e) {
            logger.error('[AI] Failed to parse JSON response', e);
            return { action: 'HOLD', confidence: 0, reasoning: 'Parse Error' };
        }
    }
}
