#!/usr/bin/env node

/**
 * DRIFT FEEDBACK LOOP IMPLEMENTATION
 * Real-time feedback system for Drift Protocol trades
 * Tracks outcomes and feeds back to AI learning system
 */

const { PrismaClient } = require('@prisma/client')
const { DriftClient, initialize } = require('@drift-labs/sdk')
const { Connection, Keypair } = require('@solana/web3.js')

class DriftFeedbackLoop {
  constructor() {
    this.prisma = new PrismaClient()
    this.driftClient = null
    this.isMonitoring = false
    this.monitoringInterval = null
  }

  async initialize() {
    console.log('🔄 Initializing Drift Feedback Loop System...')
    
    try {
      // Initialize Drift client
      const connection = new Connection(
        process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
        'confirmed'
      )
      
      const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY)
      const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray))
      
      const wallet = {
        publicKey: keypair.publicKey,
        signTransaction: async (tx) => {
          tx.partialSign(keypair)
          return tx
        },
        signAllTransactions: async (txs) => {
          return txs.map(tx => {
            tx.partialSign(keypair)
            return tx
          })
        }
      }

      const env = 'mainnet-beta'
      const sdkConfig = initialize({ env })
      
      this.driftClient = new DriftClient({
        connection,
        wallet,
        programID: sdkConfig.DRIFT_PROGRAM_ID,
        opts: { commitment: 'confirmed' }
      })

      await this.driftClient.subscribe()
      console.log('✅ Drift client initialized and subscribed')
      
    } catch (error) {
      console.error('❌ Failed to initialize Drift client:', error.message)
      throw error
    }
  }

  async startMonitoring(userId = 'drift-user') {
    console.log('🎯 Starting real-time Drift trade monitoring...')
    
    this.isMonitoring = true
    
    // Monitor every 30 seconds
    this.monitoringInterval = setInterval(async () => {
      try {
        await this.checkTradeOutcomes(userId)
      } catch (error) {
        console.error('❌ Monitoring error:', error.message)
      }
    }, 30000)
    
    // Also do an immediate check
    await this.checkTradeOutcomes(userId)
    
    console.log('✅ Monitoring started - checking every 30 seconds')
  }

  async checkTradeOutcomes(userId) {
    try {
      // Get all open trades that haven't been checked recently
      const openTrades = await this.prisma.trade.findMany({
        where: {
          userId,
          status: 'EXECUTED',
          outcome: null, // Not yet determined
          driftTxId: { not: null }, // Has Drift transaction ID
          executedAt: {
            gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours
          }
        },
        orderBy: { executedAt: 'desc' }
      })

      console.log(`🔍 Checking ${openTrades.length} open Drift trades...`)

      for (const trade of openTrades) {
        await this.checkIndividualTrade(trade)
      }

    } catch (error) {
      console.error('❌ Error checking trade outcomes:', error.message)
    }
  }

  async checkIndividualTrade(trade) {
    try {
      console.log(`🔍 Checking trade ${trade.id} (${trade.symbol} ${trade.side})...`)
      
      // Get current Drift positions and account state
      const userAccount = await this.driftClient.getUserAccount()
      const currentPositions = userAccount.perpPositions || []
      
      // Find position for this trade's market
      const marketIndex = this.getMarketIndex(trade.symbol)
      const position = currentPositions.find(pos => 
        pos.marketIndex === marketIndex && !pos.baseAssetAmount.isZero()
      )

      // Check if trade has been closed (no position remaining)
      const isClosed = !position || position.baseAssetAmount.isZero()
      
      if (isClosed) {
        console.log(`✅ Trade ${trade.id} appears to be closed, analyzing outcome...`)
        await this.analyzeTradeOutcome(trade)
      } else {
        // Check if stop loss or take profit levels have been hit
        await this.checkStopLossAndTakeProfit(trade, position)
      }

    } catch (error) {
      console.error(`❌ Error checking trade ${trade.id}:`, error.message)
    }
  }

  async analyzeTradeOutcome(trade) {
    try {
      // Determine trade outcome based on current vs entry price
      const currentPrice = await this.getCurrentPrice(trade.symbol)
      const entryPrice = trade.entryPrice || trade.price
      
      let outcome, pnlPercent, exitPrice
      
      if (trade.side === 'BUY') {
        pnlPercent = ((currentPrice - entryPrice) / entryPrice) * 100
      } else {
        pnlPercent = ((entryPrice - currentPrice) / entryPrice) * 100
      }
      
      // Determine outcome
      if (pnlPercent > 0.1) {
        outcome = 'WIN'
      } else if (pnlPercent < -0.1) {
        outcome = 'LOSS'
      } else {
        outcome = 'BREAKEVEN'
      }
      
      exitPrice = currentPrice
      
      // Calculate actual risk/reward ratio
      const actualRR = this.calculateActualRiskReward(trade, exitPrice)
      
      console.log(`📊 Trade outcome: ${outcome}, P&L: ${pnlPercent.toFixed(2)}%, RR: ${actualRR.toFixed(2)}`)
      
      // Update trade record
      const updatedTrade = await this.prisma.trade.update({
        where: { id: trade.id },
        data: {
          outcome,
          pnlPercent,
          exitPrice,
          actualRR,
          closedAt: new Date(),
          status: 'CLOSED',
          learningData: JSON.stringify({
            exitReason: this.determineExitReason(trade, exitPrice),
            marketBehavior: this.analyzeMarketBehavior(trade, exitPrice),
            accuracyVsPrediction: this.calculatePredictionAccuracy(trade, exitPrice),
            driftSpecificData: {
              platformUsed: 'DRIFT_PROTOCOL',
              executionMethod: 'REAL_TRADING',
              tradeType: 'PERPETUAL_FUTURES'
            }
          })
        }
      })
      
      // Create AI learning feedback
      await this.createAILearningFeedback(updatedTrade)
      
      console.log(`✅ Updated trade ${trade.id} with outcome: ${outcome}`)
      
    } catch (error) {
      console.error(`❌ Error analyzing trade outcome:`, error.message)
    }
  }

  async checkStopLossAndTakeProfit(trade, position) {
    try {
      const currentPrice = await this.getCurrentPrice(trade.symbol)
      
      // Check if stop loss or take profit should have been triggered
      let shouldClose = false
      let exitReason = null
      
      if (trade.side === 'BUY') {
        if (trade.stopLoss && currentPrice <= trade.stopLoss) {
          shouldClose = true
          exitReason = 'STOP_LOSS'
        } else if (trade.takeProfit && currentPrice >= trade.takeProfit) {
          shouldClose = true
          exitReason = 'TAKE_PROFIT'
        }
      } else {
        if (trade.stopLoss && currentPrice >= trade.stopLoss) {
          shouldClose = true
          exitReason = 'STOP_LOSS'
        } else if (trade.takeProfit && currentPrice <= trade.takeProfit) {
          shouldClose = true
          exitReason = 'TAKE_PROFIT'
        }
      }
      
      if (shouldClose) {
        console.log(`🎯 Trade ${trade.id} hit ${exitReason} at price ${currentPrice}`)
        
        // Update trade with specific exit reason
        await this.prisma.trade.update({
          where: { id: trade.id },
          data: {
            exitPrice: currentPrice,
            learningData: JSON.stringify({
              exitReason,
              triggeredAt: new Date(),
              expectedBehavior: true // SL/TP worked as expected
            })
          }
        })
      }
      
    } catch (error) {
      console.error(`❌ Error checking SL/TP levels:`, error.message)
    }
  }

  async createAILearningFeedback(trade) {
    try {
      // Link this trade outcome back to the AI analysis that generated it
      const relatedAnalysis = await this.prisma.aILearningData.findFirst({
        where: {
          userId: trade.userId,
          symbol: trade.symbol,
          tradeId: trade.id
        },
        orderBy: { createdAt: 'desc' }
      })
      
      if (relatedAnalysis) {
        // Update the AI learning record with real trade outcome
        await this.prisma.aILearningData.update({
          where: { id: relatedAnalysis.id },
          data: {
            outcome: trade.outcome,
            actualPrice: trade.exitPrice,
            accuracyScore: this.calculateAccuracy(relatedAnalysis, trade),
            feedbackData: JSON.stringify({
              realTradeOutcome: {
                tradeId: trade.id,
                pnlPercent: trade.pnlPercent,
                actualRR: trade.actualRR,
                exitReason: JSON.parse(trade.learningData || '{}').exitReason,
                driftProtocolData: {
                  platform: 'DRIFT_PROTOCOL',
                  orderType: 'PERPETUAL_FUTURES',
                  leverage: trade.leverage,
                  fees: trade.fees
                }
              },
              aiPredictionAccuracy: {
                predictedOutcome: this.extractPredictedOutcome(relatedAnalysis),
                actualOutcome: trade.outcome,
                priceAccuracy: Math.abs((trade.exitPrice - relatedAnalysis.predictedPrice) / relatedAnalysis.predictedPrice) * 100,
                confidenceValidation: this.validateConfidence(relatedAnalysis, trade)
              }
            })
          }
        })
        
        console.log(`🧠 Created AI learning feedback for analysis ${relatedAnalysis.id}`)
      }
      
      // Create new learning insights
      await this.generateLearningInsights(trade.userId)
      
    } catch (error) {
      console.error(`❌ Error creating AI learning feedback:`, error.message)
    }
  }

  async generateLearningInsights(userId) {
    try {
      // Generate comprehensive learning insights from real Drift trades
      const recentTrades = await this.prisma.trade.findMany({
        where: {
          userId,
          outcome: { not: null },
          driftTxId: { not: null }, // Only real Drift trades
          closedAt: {
            gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days
          }
        },
        orderBy: { closedAt: 'desc' }
      })
      
      const insights = {
        totalDriftTrades: recentTrades.length,
        winRate: this.calculateWinRate(recentTrades),
        avgPnL: this.calculateAveragePnL(recentTrades),
        bestPerformingTimeframe: this.findBestTimeframe(recentTrades),
        riskRewardAnalysis: this.analyzeRiskReward(recentTrades),
        commonFailurePatterns: this.identifyFailurePatterns(recentTrades),
        driftSpecificInsights: {
          platformEfficiency: this.analyzePlatformEfficiency(recentTrades),
          optimalLeverage: this.findOptimalLeverage(recentTrades),
          stopLossEffectiveness: this.analyzeStopLossEffectiveness(recentTrades)
        }
      }
      
      console.log('📊 Generated learning insights:', insights)
      
      // Store insights for AI to use in future decisions
      await this.prisma.aILearningData.create({
        data: {
          userId,
          symbol: 'INSIGHTS',
          timeframe: '30d',
          analysisData: JSON.stringify(insights),
          marketConditions: JSON.stringify({
            dataSource: 'REAL_DRIFT_TRADES',
            analysisType: 'PERFORMANCE_INSIGHTS',
            sampleSize: recentTrades.length
          }),
          confidenceScore: insights.winRate * 100,
          createdAt: new Date()
        }
      })
      
    } catch (error) {
      console.error(`❌ Error generating learning insights:`, error.message)
    }
  }

  // Helper methods
  getMarketIndex(symbol) {
    const marketMap = {
      'SOL': 0, 'BTC': 1, 'ETH': 2, 'APT': 3, 'AVAX': 4,
      'BNB': 5, 'MATIC': 6, 'ARB': 7, 'DOGE': 8, 'OP': 9
    }
    return marketMap[symbol.toUpperCase()] || 0
  }

  async getCurrentPrice(symbol) {
    try {
      const marketIndex = this.getMarketIndex(symbol)
      const perpMarketAccount = this.driftClient.getPerpMarketAccount(marketIndex)
      return Number(perpMarketAccount.amm.lastMarkPriceTwap) / 1e6
    } catch (error) {
      console.error(`❌ Error getting current price for ${symbol}:`, error.message)
      return null
    }
  }

  calculateActualRiskReward(trade, exitPrice) {
    const entryPrice = trade.entryPrice || trade.price
    const stopLoss = trade.stopLoss
    const takeProfit = trade.takeProfit
    
    if (!stopLoss || !takeProfit) return 0
    
    const riskAmount = Math.abs(entryPrice - stopLoss)
    const rewardAmount = Math.abs(exitPrice - entryPrice)
    
    return riskAmount > 0 ? rewardAmount / riskAmount : 0
  }

  determineExitReason(trade, exitPrice) {
    if (trade.stopLoss && Math.abs(exitPrice - trade.stopLoss) < 0.01) {
      return 'STOP_LOSS'
    } else if (trade.takeProfit && Math.abs(exitPrice - trade.takeProfit) < 0.01) {
      return 'TAKE_PROFIT'
    } else {
      return 'MANUAL_CLOSE'
    }
  }

  analyzeMarketBehavior(trade, exitPrice) {
    const entryPrice = trade.entryPrice || trade.price
    const priceMove = (exitPrice - entryPrice) / entryPrice * 100
    
    if (Math.abs(priceMove) < 0.5) return 'SIDEWAYS'
    if (priceMove > 0) return 'BULLISH'
    return 'BEARISH'
  }

  calculatePredictionAccuracy(trade, exitPrice) {
    const entryPrice = trade.entryPrice || trade.price
    const expectedDirection = trade.side === 'BUY' ? 'UP' : 'DOWN'
    const actualDirection = exitPrice > entryPrice ? 'UP' : 'DOWN'
    
    return expectedDirection === actualDirection ? 100 : 0
  }

  calculateAccuracy(analysis, trade) {
    try {
      const predicted = analysis.predictedPrice
      const actual = trade.exitPrice
      
      if (!predicted || !actual) return null
      
      const accuracy = 1 - Math.abs(predicted - actual) / predicted
      return Math.max(0, Math.min(1, accuracy))
    } catch (error) {
      return null
    }
  }

  extractPredictedOutcome(analysis) {
    try {
      const data = JSON.parse(analysis.analysisData)
      return data.recommendation || 'UNKNOWN'
    } catch (error) {
      return 'UNKNOWN'
    }
  }

  validateConfidence(analysis, trade) {
    const confidence = analysis.confidenceScore || 0
    const wasCorrect = trade.outcome === 'WIN'
    
    return {
      confidence,
      wasCorrect,
      calibration: wasCorrect ? 'WELL_CALIBRATED' : 'OVERCONFIDENT'
    }
  }

  calculateWinRate(trades) {
    const wins = trades.filter(t => t.outcome === 'WIN').length
    return trades.length > 0 ? wins / trades.length : 0
  }

  calculateAveragePnL(trades) {
    const totalPnL = trades.reduce((sum, t) => sum + (t.pnlPercent || 0), 0)
    return trades.length > 0 ? totalPnL / trades.length : 0
  }

  findBestTimeframe(trades) {
    const timeframes = {}
    trades.forEach(trade => {
      const tf = trade.timeframe || '1h'
      if (!timeframes[tf]) timeframes[tf] = { wins: 0, total: 0 }
      timeframes[tf].total++
      if (trade.outcome === 'WIN') timeframes[tf].wins++
    })
    
    let bestTf = '1h'
    let bestRate = 0
    
    Object.entries(timeframes).forEach(([tf, data]) => {
      const rate = data.total > 0 ? data.wins / data.total : 0
      if (rate > bestRate && data.total >= 3) { // At least 3 trades
        bestRate = rate
        bestTf = tf
      }
    })
    
    return { timeframe: bestTf, winRate: bestRate }
  }

  analyzeRiskReward(trades) {
    const validTrades = trades.filter(t => t.actualRR)
    const avgRR = validTrades.reduce((sum, t) => sum + t.actualRR, 0) / validTrades.length
    
    return {
      averageRiskReward: avgRR || 0,
      tradesWithGoodRR: validTrades.filter(t => t.actualRR > 1.5).length,
      totalAnalyzedTrades: validTrades.length
    }
  }

  identifyFailurePatterns(trades) {
    const failures = trades.filter(t => t.outcome === 'LOSS')
    const patterns = []
    
    // Analyze common failure reasons
    const exitReasons = {}
    failures.forEach(trade => {
      try {
        const data = JSON.parse(trade.learningData || '{}')
        const reason = data.exitReason || 'UNKNOWN'
        exitReasons[reason] = (exitReasons[reason] || 0) + 1
      } catch (error) {
        exitReasons['UNKNOWN'] = (exitReasons['UNKNOWN'] || 0) + 1
      }
    })
    
    Object.entries(exitReasons).forEach(([reason, count]) => {
      if (count >= 2) {
        patterns.push({
          pattern: reason,
          frequency: count,
          percentage: (count / failures.length) * 100
        })
      }
    })
    
    return patterns
  }

  analyzePlatformEfficiency(trades) {
    const driftTrades = trades.filter(t => t.driftTxId)
    return {
      totalDriftTrades: driftTrades.length,
      avgExecutionTime: this.calculateAvgExecutionTime(driftTrades),
      successRate: this.calculateSuccessRate(driftTrades),
      avgFees: this.calculateAvgFees(driftTrades)
    }
  }

  findOptimalLeverage(trades) {
    const leverageGroups = {}
    trades.forEach(trade => {
      const lev = Math.floor(trade.leverage || 1)
      if (!leverageGroups[lev]) leverageGroups[lev] = { wins: 0, total: 0, totalPnL: 0 }
      leverageGroups[lev].total++
      leverageGroups[lev].totalPnL += trade.pnlPercent || 0
      if (trade.outcome === 'WIN') leverageGroups[lev].wins++
    })
    
    let optimalLeverage = 1
    let bestScore = 0
    
    Object.entries(leverageGroups).forEach(([lev, data]) => {
      if (data.total >= 3) {
        const winRate = data.wins / data.total
        const avgPnL = data.totalPnL / data.total
        const score = winRate * avgPnL
        
        if (score > bestScore) {
          bestScore = score
          optimalLeverage = parseInt(lev)
        }
      }
    })
    
    return { leverage: optimalLeverage, score: bestScore }
  }

  analyzeStopLossEffectiveness(trades) {
    const slTrades = trades.filter(t => {
      try {
        const data = JSON.parse(t.learningData || '{}')
        return data.exitReason === 'STOP_LOSS'
      } catch (error) {
        return false
      }
    })
    
    return {
      stopLossActivations: slTrades.length,
      avgLossWhenTriggered: slTrades.reduce((sum, t) => sum + (t.pnlPercent || 0), 0) / slTrades.length || 0,
      effectiveness: slTrades.length / trades.length
    }
  }

  calculateAvgExecutionTime(trades) {
    const validTrades = trades.filter(t => t.executionTime && t.createdAt)
    if (validTrades.length === 0) return 0
    
    const totalTime = validTrades.reduce((sum, trade) => {
      const diff = new Date(trade.executionTime) - new Date(trade.createdAt)
      return sum + diff
    }, 0)
    
    return totalTime / validTrades.length / 1000 // Convert to seconds
  }

  calculateSuccessRate(trades) {
    const executed = trades.filter(t => t.status === 'EXECUTED' || t.status === 'CLOSED')
    return trades.length > 0 ? executed.length / trades.length : 0
  }

  calculateAvgFees(trades) {
    const tradesWithFees = trades.filter(t => t.fees !== null && t.fees !== undefined)
    const totalFees = tradesWithFees.reduce((sum, t) => sum + t.fees, 0)
    return tradesWithFees.length > 0 ? totalFees / tradesWithFees.length : 0
  }

  async stopMonitoring() {
    console.log('⏹️ Stopping Drift feedback monitoring...')
    
    this.isMonitoring = false
    
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval)
      this.monitoringInterval = null
    }
    
    if (this.driftClient) {
      await this.driftClient.unsubscribe()
    }
    
    await this.prisma.$disconnect()
    
    console.log('✅ Monitoring stopped and resources cleaned up')
  }
}

// Export for use in other modules
module.exports = { DriftFeedbackLoop }

// CLI usage
if (require.main === module) {
  const feedbackLoop = new DriftFeedbackLoop()
  
  async function runFeedbackLoop() {
    try {
      await feedbackLoop.initialize()
      await feedbackLoop.startMonitoring('drift-user')
      
      console.log('🎯 Drift feedback loop is running...')
      console.log('Press Ctrl+C to stop')
      
      // Handle graceful shutdown
      process.on('SIGINT', async () => {
        console.log('\n🛑 Shutting down gracefully...')
        await feedbackLoop.stopMonitoring()
        process.exit(0)
      })
      
    } catch (error) {
      console.error('❌ Failed to start feedback loop:', error.message)
      process.exit(1)
    }
  }
  
  runFeedbackLoop()
}
