#!/usr/bin/env node

/**
 * Stop Loss Decision Learning System
 * 
 * This system makes the AI learn from its own decision-making process near stop loss.
 * It records every decision, tracks outcomes, and continuously improves decision-making.
 */

const { getDB } = require('./db');

class StopLossDecisionLearner {
  constructor() {
    this.decisionHistory = [];
    this.learningThresholds = {
      emergencyDistance: 1.0,
      highRiskDistance: 2.0,
      mediumRiskDistance: 5.0
    };
  }

  async getPrisma() {
    return await getDB();
  }

  async log(message) {
    const timestamp = new Date().toISOString();
    console.log(`[${timestamp}] 🧠 SL Learner: ${message}`);
  }

  /**
   * Record an AI decision made near stop loss for learning purposes
   */
  async recordDecision(decisionData) {
    try {
      const decision = {
        id: `decision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        tradeId: decisionData.tradeId,
        symbol: decisionData.symbol,
        decisionType: decisionData.decision, // 'HOLD', 'EXIT', 'TIGHTEN_SL', 'PARTIAL_EXIT', 'EMERGENCY_EXIT'
        distanceFromSL: decisionData.distanceFromSL,
        reasoning: decisionData.reasoning,
        marketConditions: {
          price: decisionData.currentPrice,
          trend: await this.analyzeMarketTrend(decisionData.symbol),
          volatility: await this.calculateVolatility(decisionData.symbol),
          volume: decisionData.volume || 'unknown',
          timeOfDay: new Date().getHours(),
          dayOfWeek: new Date().getDay()
        },
        confidenceScore: decisionData.confidenceScore || 0.7,
        expectedOutcome: decisionData.expectedOutcome || 'BETTER_RESULT',
        decisionTimestamp: new Date(),
        status: 'PENDING_OUTCOME'
      };

      // Store in database
      const prisma = await this.getPrisma();
      await prisma.sLDecision.create({
        data: {
          id: decision.id,
          tradeId: decision.tradeId,
          symbol: decision.symbol,
          decisionType: decision.decisionType,
          distanceFromSL: decision.distanceFromSL,
          reasoning: decision.reasoning,
          marketConditions: JSON.stringify(decision.marketConditions),
          confidenceScore: decision.confidenceScore,
          expectedOutcome: decision.expectedOutcome,
          decisionTimestamp: decision.decisionTimestamp,
          status: decision.status
        }
      });

      // Keep in memory for quick access
      this.decisionHistory.push(decision);

      await this.log(`📝 Recorded decision: ${decision.decisionType} at ${decision.distanceFromSL}% from SL - ${decision.reasoning}`);
      
      return decision.id;
    } catch (error) {
      await this.log(`❌ Error recording decision: ${error.message}`);
      return null;
    }
  }

  /**
   * Assess the outcome of a previous decision when trade closes or conditions change
   */
  async assessDecisionOutcome(assessmentData) {
    try {
      const { decisionId, actualOutcome, timeToOutcome, pnlImpact, additionalContext } = assessmentData;

      // Determine if the decision was correct
      const wasCorrect = this.evaluateDecisionCorrectness(actualOutcome, pnlImpact);
      const learningScore = this.calculateLearningScore(wasCorrect, pnlImpact, timeToOutcome);

      // Update decision record
      const prisma = await this.getPrisma();
      await prisma.sLDecision.update({
        where: { id: decisionId },
        data: {
          outcome: actualOutcome,
          outcomeTimestamp: new Date(),
          timeToOutcome,
          pnlImpact,
          wasCorrect,
          learningScore,
          additionalContext: JSON.stringify(additionalContext || {}),
          status: 'ASSESSED'
        }
      });

      // Update in-memory history
      const decision = this.decisionHistory.find(d => d.id === decisionId);
      if (decision) {
        Object.assign(decision, {
          outcome: actualOutcome,
          outcomeTimestamp: new Date(),
          wasCorrect,
          learningScore,
          status: 'ASSESSED'
        });
      }

      await this.log(`✅ Assessed decision ${decisionId}: ${wasCorrect ? 'CORRECT' : 'INCORRECT'} - Score: ${learningScore.toFixed(2)}`);

      // Trigger learning update
      await this.updateLearningModel();

      return { wasCorrect, learningScore };
    } catch (error) {
      await this.log(`❌ Error assessing decision outcome: ${error.message}`);
      return null;
    }
  }

  /**
   * Analyze historical decisions to identify patterns and optimize future decisions
   */
  async analyzeDecisionPatterns() {
    try {
      const prisma = await this.getPrisma();
      const decisions = await prisma.sLDecision.findMany({
        where: { status: 'ASSESSED' },
        orderBy: { decisionTimestamp: 'desc' },
        take: 100 // Analyze last 100 decisions
      });

      const patterns = {
        successfulPatterns: [],
        failurePatterns: [],
        optimalTiming: {},
        contextFactors: {},
        distanceOptimization: {}
      };

      // Analyze success patterns by decision type
      const decisionTypes = ['HOLD', 'EXIT', 'TIGHTEN_SL', 'PARTIAL_EXIT', 'EMERGENCY_EXIT'];
      
      for (const type of decisionTypes) {
        const typeDecisions = decisions.filter(d => d.decisionType === type);
        const successRate = typeDecisions.length > 0 ? 
          typeDecisions.filter(d => d.wasCorrect).length / typeDecisions.length : 0;
        
        const avgScore = typeDecisions.length > 0 ?
          typeDecisions.reduce((sum, d) => sum + (d.learningScore || 0), 0) / typeDecisions.length : 0;

        if (successRate > 0.6) { // 60%+ success rate
          patterns.successfulPatterns.push({
            decisionType: type,
            successRate: successRate * 100,
            avgScore,
            sampleSize: typeDecisions.length,
            optimalConditions: this.identifyOptimalConditions(typeDecisions.filter(d => d.wasCorrect))
          });
        } else if (typeDecisions.length >= 5) {
          patterns.failurePatterns.push({
            decisionType: type,
            successRate: successRate * 100,
            avgScore,
            sampleSize: typeDecisions.length,
            commonFailureReasons: this.identifyFailureReasons(typeDecisions.filter(d => !d.wasCorrect))
          });
        }
      }

      // Analyze optimal distance thresholds
      patterns.distanceOptimization = await this.optimizeDistanceThresholds(decisions);

      // Analyze timing patterns
      patterns.optimalTiming = await this.analyzeTimingPatterns(decisions);

      await this.log(`📊 Pattern analysis complete: ${patterns.successfulPatterns.length} successful patterns, ${patterns.failurePatterns.length} failure patterns identified`);

      return patterns;
    } catch (error) {
      await this.log(`❌ Error analyzing decision patterns: ${error.message}`);
      return null;
    }
  }

  /**
   * Get AI recommendation for current situation based on learned patterns
   */
  async getSmartRecommendation(situationData) {
    try {
      const { distanceFromSL, symbol, marketConditions } = situationData;
      
      // Get historical patterns for similar situations
      const patterns = await this.analyzeDecisionPatterns();
      const currentConditions = marketConditions || await this.getCurrentMarketConditions(symbol);

      // Find most similar historical situations
      const similarSituations = await this.findSimilarSituations({
        distanceFromSL,
        marketConditions: currentConditions
      });

      // Generate recommendation based on learned patterns
      const recommendation = {
        suggestedAction: 'HOLD', // Default
        confidence: 0.5,
        reasoning: 'Insufficient learning data',
        learningBased: false,
        supportingData: {}
      };

      if (similarSituations.length >= 3) {
        const successfulActions = similarSituations
          .filter(s => s.wasCorrect)
          .map(s => s.decisionType);
        
        const mostSuccessfulAction = this.getMostCommonAction(successfulActions);
        const successRate = successfulActions.length / similarSituations.length;

        recommendation.suggestedAction = mostSuccessfulAction;
        recommendation.confidence = Math.min(0.95, successRate + 0.1);
        recommendation.reasoning = `Based on ${similarSituations.length} similar situations, ${mostSuccessfulAction} succeeded ${(successRate * 100).toFixed(1)}% of the time`;
        recommendation.learningBased = true;
        recommendation.supportingData = {
          historicalSamples: similarSituations.length,
          successRate: successRate * 100,
          avgPnlImpact: similarSituations.reduce((sum, s) => sum + (s.pnlImpact || 0), 0) / similarSituations.length
        };
      }

      await this.log(`🎯 Smart recommendation: ${recommendation.suggestedAction} (${(recommendation.confidence * 100).toFixed(1)}% confidence) - ${recommendation.reasoning}`);

      return recommendation;
    } catch (error) {
      await this.log(`❌ Error generating smart recommendation: ${error.message}`);
      return {
        suggestedAction: 'HOLD',
        confidence: 0.3,
        reasoning: `Error in recommendation system: ${error.message}`,
        learningBased: false
      };
    }
  }

  /**
   * Update learning model based on new decision outcomes
   */
  async updateLearningModel() {
    try {
      const patterns = await this.analyzeDecisionPatterns();
      
      if (patterns && patterns.distanceOptimization) {
        // Update decision thresholds based on learning
        this.learningThresholds = {
          emergencyDistance: patterns.distanceOptimization.optimalEmergencyThreshold || 1.0,
          highRiskDistance: patterns.distanceOptimization.optimalHighRiskThreshold || 2.0,
          mediumRiskDistance: patterns.distanceOptimization.optimalMediumRiskThreshold || 5.0
        };

        await this.log(`🔄 Updated learning thresholds: Emergency=${this.learningThresholds.emergencyDistance}%, High Risk=${this.learningThresholds.highRiskDistance}%, Medium Risk=${this.learningThresholds.mediumRiskDistance}%`);
      }

      return true;
    } catch (error) {
      await this.log(`❌ Error updating learning model: ${error.message}`);
      return false;
    }
  }

  /**
   * Helper methods for analysis
   */
  evaluateDecisionCorrectness(actualOutcome, pnlImpact) {
    // Define what constitutes a "correct" decision
    const correctOutcomes = [
      'BETTER_THAN_ORIGINAL_SL',
      'AVOIDED_LOSS',
      'IMPROVED_PROFIT',
      'SUCCESSFUL_EXIT'
    ];

    return correctOutcomes.includes(actualOutcome) || (pnlImpact && pnlImpact > 0);
  }

  calculateLearningScore(wasCorrect, pnlImpact, timeToOutcome) {
    let score = wasCorrect ? 0.7 : 0.3; // Base score
    
    // Adjust for P&L impact
    if (pnlImpact) {
      score += Math.min(0.2, pnlImpact / 100); // Max 0.2 bonus for positive P&L
    }
    
    // Adjust for timing (faster good decisions are better)
    if (timeToOutcome && wasCorrect) {
      const timingBonus = Math.max(0, 0.1 - (timeToOutcome / 3600)); // Bonus for decisions resolved within an hour
      score += timingBonus;
    }

    return Math.max(0, Math.min(1, score));
  }

  identifyOptimalConditions(successfulDecisions) {
    // Analyze common conditions in successful decisions
    const conditions = {};
    
    successfulDecisions.forEach(decision => {
      try {
        const market = JSON.parse(decision.marketConditions || '{}');
        
        // Track successful decision contexts
        if (market.trend) {
          conditions.trend = conditions.trend || {};
          conditions.trend[market.trend] = (conditions.trend[market.trend] || 0) + 1;
        }
        
        if (market.timeOfDay) {
          conditions.timeOfDay = conditions.timeOfDay || {};
          const hour = market.timeOfDay;
          conditions.timeOfDay[hour] = (conditions.timeOfDay[hour] || 0) + 1;
        }
      } catch (error) {
        // Skip malformed data
      }
    });

    return conditions;
  }

  identifyFailureReasons(failedDecisions) {
    // Analyze what went wrong in failed decisions
    return failedDecisions.map(decision => ({
      reasoning: decision.reasoning,
      distanceFromSL: decision.distanceFromSL,
      outcome: decision.outcome,
      pnlImpact: decision.pnlImpact
    }));
  }

  async optimizeDistanceThresholds(decisions) {
    // Analyze optimal distance thresholds for different decision types
    const optimization = {};
    
    // Group decisions by distance ranges
    const ranges = [
      { min: 0, max: 1, label: 'emergency' },
      { min: 1, max: 2, label: 'highRisk' },
      { min: 2, max: 5, label: 'mediumRisk' },
      { min: 5, max: 100, label: 'safe' }
    ];

    for (const range of ranges) {
      const rangeDecisions = decisions.filter(d => 
        d.distanceFromSL >= range.min && d.distanceFromSL < range.max
      );
      
      if (rangeDecisions.length >= 3) {
        const successRate = rangeDecisions.filter(d => d.wasCorrect).length / rangeDecisions.length;
        const avgScore = rangeDecisions.reduce((sum, d) => sum + (d.learningScore || 0), 0) / rangeDecisions.length;
        
        optimization[`${range.label}Range`] = {
          successRate: successRate * 100,
          avgScore,
          sampleSize: rangeDecisions.length,
          optimalThreshold: this.calculateOptimalThreshold(rangeDecisions)
        };
      }
    }

    return optimization;
  }

  calculateOptimalThreshold(decisions) {
    // Find the distance threshold that maximizes success rate
    const sortedDecisions = decisions.sort((a, b) => a.distanceFromSL - b.distanceFromSL);
    let bestThreshold = 1.0;
    let bestScore = 0;

    for (let i = 0; i < sortedDecisions.length - 1; i++) {
      const threshold = sortedDecisions[i].distanceFromSL;
      const aboveThreshold = sortedDecisions.slice(i);
      const successRate = aboveThreshold.filter(d => d.wasCorrect).length / aboveThreshold.length;
      
      if (successRate > bestScore && aboveThreshold.length >= 3) {
        bestScore = successRate;
        bestThreshold = threshold;
      }
    }

    return bestThreshold;
  }

  async analyzeTimingPatterns(decisions) {
    // Analyze when decisions work best (time of day, day of week, etc.)
    const timing = {
      timeOfDay: {},
      dayOfWeek: {},
      marketSession: {}
    };

    decisions.forEach(decision => {
      try {
        const market = JSON.parse(decision.marketConditions || '{}');
        const wasCorrect = decision.wasCorrect;
        
        if (market.timeOfDay !== undefined) {
          const hour = market.timeOfDay;
          timing.timeOfDay[hour] = timing.timeOfDay[hour] || { total: 0, correct: 0 };
          timing.timeOfDay[hour].total++;
          if (wasCorrect) timing.timeOfDay[hour].correct++;
        }
        
        if (market.dayOfWeek !== undefined) {
          const day = market.dayOfWeek;
          timing.dayOfWeek[day] = timing.dayOfWeek[day] || { total: 0, correct: 0 };
          timing.dayOfWeek[day].total++;
          if (wasCorrect) timing.dayOfWeek[day].correct++;
        }
      } catch (error) {
        // Skip malformed data
      }
    });

    return timing;
  }

  async findSimilarSituations(currentSituation) {
    const { distanceFromSL, marketConditions } = currentSituation;
    const tolerance = 0.5; // 0.5% tolerance for distance matching

    const prisma = await this.getPrisma();
    const decisions = await prisma.sLDecision.findMany({
      where: {
        status: 'ASSESSED',
        distanceFromSL: {
          gte: distanceFromSL - tolerance,
          lte: distanceFromSL + tolerance
        }
      },
      orderBy: { decisionTimestamp: 'desc' },
      take: 20
    });

    return decisions;
  }

  getMostCommonAction(actions) {
    const counts = {};
    actions.forEach(action => {
      counts[action] = (counts[action] || 0) + 1;
    });

    return Object.entries(counts).reduce((a, b) => counts[a] > counts[b] ? a : b)[0] || 'HOLD';
  }

  async analyzeMarketTrend(symbol) {
    // Simplified trend analysis - in real implementation, use technical indicators
    try {
      const response = await fetch(`http://localhost:9001/api/automation/position-monitor`);
      const data = await response.json();
      
      if (data.success && data.monitor && data.monitor.position) {
        const pnl = data.monitor.position.unrealizedPnl;
        if (pnl > 0) return 'BULLISH';
        if (pnl < 0) return 'BEARISH';
        return 'SIDEWAYS';
      }
    } catch (error) {
      // Fallback
    }
    
    return 'UNKNOWN';
  }

  async calculateVolatility(symbol) {
    // Simplified volatility calculation
    // In real implementation, calculate based on price history
    return Math.random() * 0.1; // Mock volatility 0-10%
  }

  async getCurrentMarketConditions(symbol) {
    return {
      trend: await this.analyzeMarketTrend(symbol),
      volatility: await this.calculateVolatility(symbol),
      timeOfDay: new Date().getHours(),
      dayOfWeek: new Date().getDay()
    };
  }

  /**
   * Generate learning insights report
   */
  async generateLearningReport() {
    try {
      const patterns = await this.analyzeDecisionPatterns();
      
      const report = {
        timestamp: new Date().toISOString(),
        summary: {
          totalDecisions: this.decisionHistory.length,
          successfulPatterns: patterns?.successfulPatterns?.length || 0,
          learningThresholds: this.learningThresholds,
          systemConfidence: this.calculateSystemConfidence()
        },
        insights: patterns,
        recommendations: await this.generateSystemRecommendations(patterns)
      };

      await this.log(`📊 Learning report generated: ${report.summary.totalDecisions} decisions analyzed`);
      
      return report;
    } catch (error) {
      await this.log(`❌ Error generating learning report: ${error.message}`);
      return null;
    }
  }

  calculateSystemConfidence() {
    const recentDecisions = this.decisionHistory.slice(-20); // Last 20 decisions
    if (recentDecisions.length < 5) return 0.3; // Low confidence with insufficient data
    
    const successRate = recentDecisions.filter(d => d.wasCorrect).length / recentDecisions.length;
    return Math.min(0.95, successRate + 0.1); // Cap at 95%
  }

  async generateSystemRecommendations(patterns) {
    const recommendations = [];

    if (patterns?.failurePatterns?.length > 0) {
      patterns.failurePatterns.forEach(pattern => {
        recommendations.push({
          type: 'IMPROVEMENT',
          priority: 'HIGH',
          message: `Consider avoiding ${pattern.decisionType} decisions - only ${pattern.successRate.toFixed(1)}% success rate`,
          actionable: true
        });
      });
    }

    if (patterns?.successfulPatterns?.length > 0) {
      const bestPattern = patterns.successfulPatterns.reduce((best, current) => 
        current.successRate > best.successRate ? current : best
      );
      
      recommendations.push({
        type: 'OPTIMIZATION',
        priority: 'MEDIUM',
        message: `${bestPattern.decisionType} decisions show ${bestPattern.successRate.toFixed(1)}% success rate - consider using more often`,
        actionable: true
      });
    }

    return recommendations;
  }
}

// Export for use in other modules
module.exports = StopLossDecisionLearner;

// Direct execution for testing
if (require.main === module) {
  const learner = new StopLossDecisionLearner();
  
  console.log('🧠 Stop Loss Decision Learning System');
  console.log('📊 Ready to make your AI smarter with every decision!');
  
  // Demo decision recording
  setTimeout(async () => {
    await learner.recordDecision({
      tradeId: 'demo_001',
      symbol: 'SOL-PERP',
      decision: 'TIGHTEN_SL',
      distanceFromSL: 2.3,
      reasoning: 'Market showing weakness, reducing risk exposure',
      currentPrice: 182.45,
      confidenceScore: 0.8,
      expectedOutcome: 'BETTER_RESULT'
    });
    
    const report = await learner.generateLearningReport();
    console.log('\n📊 LEARNING REPORT:', JSON.stringify(report, null, 2));
  }, 1000);
}
