import { PrismaClient } from '@prisma/client'
import { aiAnalysisService, AnalysisResult } from './ai-analysis'
import { jupiterDEXService } from './jupiter-dex-service'
import { TradingViewCredentials } from './tradingview-automation'

const prisma = new PrismaClient()

export interface AutomationConfig {
  userId: string
  mode: 'SIMULATION' | 'LIVE'
  symbol: string
  timeframe: string
  selectedTimeframes: string[] // Multi-timeframe support
  tradingAmount: number
  maxLeverage: number
  // stopLossPercent and takeProfitPercent removed - AI calculates these automatically
  maxDailyTrades: number
  riskPercentage: number
  dexProvider: 'JUPITER' | 'DRIFT'
}

export interface AutomationStatus {
  isActive: boolean
  mode: 'SIMULATION' | 'LIVE'
  symbol: string
  timeframe: string
  totalTrades: number
  successfulTrades: number
  winRate: number
  totalPnL: number
  lastAnalysis?: Date
  lastTrade?: Date
  nextScheduled?: Date
  errorCount: number
  lastError?: string
}

export class AutomationService {
  private activeSession: any = null
  private intervalId: NodeJS.Timeout | null = null
  private isRunning = false
  private credentials: TradingViewCredentials | null = null

  constructor() {
    this.initialize()
  }

  private async initialize() {
    // Load credentials from environment or database
    this.credentials = {
      email: process.env.TRADINGVIEW_EMAIL || '',
      password: process.env.TRADINGVIEW_PASSWORD || ''
    }
  }

  async startAutomation(config: AutomationConfig): Promise<boolean> {
    try {
      if (this.isRunning) {
        throw new Error('Automation is already running')
      }

      // Validate configuration
      if (!config.userId || !config.symbol || !config.timeframe) {
        throw new Error('Invalid automation configuration')
      }

      // Create or update automation session
      const existingSession = await prisma.automationSession.findFirst({
        where: { 
          userId: config.userId,
          symbol: config.symbol,
          timeframe: config.timeframe
        }
      })

      let session
      if (existingSession) {
        session = await prisma.automationSession.update({
          where: { id: existingSession.id },
          data: {
            status: 'ACTIVE',
            mode: config.mode,
            settings: config as any,
            updatedAt: new Date()
          }
        })
      } else {
        session = await prisma.automationSession.create({
          data: {
            userId: config.userId,
            status: 'ACTIVE',
            mode: config.mode,
            symbol: config.symbol,
            timeframe: config.timeframe,
            settings: config as any
          }
        })
      }

      this.activeSession = session
      this.isRunning = true

      // Start the automation loop
      this.startAutomationLoop(config)

      console.log(`🤖 Automation started for ${config.symbol} ${config.timeframe} in ${config.mode} mode`)
      return true

    } catch (error) {
      console.error('Failed to start automation:', error)
      return false
    }
  }

  async stopAutomation(): Promise<boolean> {
    try {
      if (!this.isRunning) {
        return true
      }

      // Clear interval
      if (this.intervalId) {
        clearInterval(this.intervalId)
        this.intervalId = null
      }

      // Update session status
      if (this.activeSession) {
        await prisma.automationSession.update({
          where: { id: this.activeSession.id },
          data: { 
            status: 'STOPPED',
            updatedAt: new Date()
          }
        })
      }

      this.isRunning = false
      this.activeSession = null

      console.log('🛑 Automation stopped')
      return true

    } catch (error) {
      console.error('Failed to stop automation:', error)
      return false
    }
  }

  async pauseAutomation(): Promise<boolean> {
    try {
      if (!this.isRunning || !this.activeSession) {
        return false
      }

      // Clear interval but keep session
      if (this.intervalId) {
        clearInterval(this.intervalId)
        this.intervalId = null
      }

      // Update session status
      await prisma.automationSession.update({
        where: { id: this.activeSession.id },
        data: { 
          status: 'PAUSED',
          updatedAt: new Date()
        }
      })

      console.log('⏸️ Automation paused')
      return true

    } catch (error) {
      console.error('Failed to pause automation:', error)
      return false
    }
  }

  async resumeAutomation(): Promise<boolean> {
    try {
      if (!this.activeSession) {
        return false
      }

      // Update session status
      await prisma.automationSession.update({
        where: { id: this.activeSession.id },
        data: { 
          status: 'ACTIVE',
          updatedAt: new Date()
        }
      })

      // Restart automation loop
      const config = this.activeSession.settings as AutomationConfig
      this.startAutomationLoop(config)

      console.log('▶️ Automation resumed')
      return true

    } catch (error) {
      console.error('Failed to resume automation:', error)
      return false
    }
  }

  private startAutomationLoop(config: AutomationConfig) {
    // Calculate interval based on timeframe
    const intervalMs = this.getIntervalFromTimeframe(config.timeframe)
    
    console.log(`🔄 Starting automation loop every ${intervalMs/1000/60} minutes`)

    this.intervalId = setInterval(async () => {
      try {
        await this.executeAutomationCycle(config)
      } catch (error) {
        console.error('Automation cycle error:', error)
        await this.handleAutomationError(error)
      }
    }, intervalMs)

    // Execute first cycle immediately
    setTimeout(async () => {
      try {
        await this.executeAutomationCycle(config)
      } catch (error) {
        console.error('Initial automation cycle error:', error)
        await this.handleAutomationError(error)
      }
    }, 5000) // 5 second delay for initialization
  }

  private async executeAutomationCycle(config: AutomationConfig) {
    console.log(`🔄 Executing automation cycle for ${config.symbol} ${config.timeframe}`)

    // Check for open positions first (instead of daily trade limit)
    const hasOpenPosition = await this.checkForOpenPositions(config)
    if (hasOpenPosition) {
      console.log(`📊 Open position detected for ${config.symbol}, monitoring only`)
      return
    }

    // Generate session ID for progress tracking
    const sessionId = `auto_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`
    
    // Step 1: Capture screenshot and analyze
    const screenshotConfig = {
      symbol: config.symbol,
      timeframe: config.timeframe,
      layouts: ['ai', 'diy'],
      sessionId,
      analyze: true
    }

    const result = await aiAnalysisService.captureAndAnalyzeWithConfig(screenshotConfig)
    
    if (!result.analysis || result.screenshots.length === 0) {
      console.log('❌ Failed to capture or analyze chart')
      return
    }

    // Step 2: Store analysis in database for learning (only if analysis exists)
    if (result.analysis) {
      await this.storeAnalysisForLearning(config, { ...result, analysis: result.analysis }, sessionId)
    }

    // Step 3: Check if we should execute trade
    const shouldTrade = await this.shouldExecuteTrade(result.analysis, config)
    
    if (!shouldTrade) {
      console.log('📊 Analysis does not meet trading criteria')
      return
    }

    // Step 4: Execute trade based on analysis
    await this.executeTrade(config, result.analysis, result.screenshots[0])

    // Step 5: Update session statistics
    await this.updateSessionStats(config.userId)
  }

  private async storeAnalysisForLearning(
    config: AutomationConfig, 
    result: { screenshots: string[], analysis: AnalysisResult }, 
    sessionId: string
  ) {
    try {
      // Store in trading journal
      await prisma.tradingJournal.create({
        data: {
          userId: config.userId,
          screenshotUrl: result.screenshots.join(','),
          aiAnalysis: JSON.stringify(result.analysis),
          marketSentiment: result.analysis.marketSentiment,
          keyLevels: result.analysis.keyLevels,
          recommendation: result.analysis.recommendation,
          confidence: result.analysis.confidence,
          symbol: config.symbol,
          timeframe: config.timeframe,
          tradingMode: config.mode,
          sessionId: sessionId,
          priceAtAnalysis: result.analysis.entry?.price
        }
      })

      // Store in AI learning data
      await prisma.aILearningData.create({
        data: {
          userId: config.userId,
          sessionId: sessionId,
          analysisData: result.analysis as any,
          marketConditions: {
            timeframe: config.timeframe,
            symbol: config.symbol,
            timestamp: new Date().toISOString()
          },
          confidenceScore: result.analysis.confidence,
          timeframe: config.timeframe,
          symbol: config.symbol,
          screenshot: result.screenshots[0],
          predictedPrice: result.analysis.entry?.price
        }
      })

      console.log('📚 Analysis stored for learning')
    } catch (error) {
      console.error('Failed to store analysis for learning:', error)
    }
  }

  private async shouldExecuteTrade(analysis: AnalysisResult, config: AutomationConfig): Promise<boolean> {
    // Check minimum confidence threshold
    if (analysis.confidence < 70) {
      console.log(`📊 Confidence too low: ${analysis.confidence}%`)
      return false
    }

    // Check if recommendation is actionable
    if (analysis.recommendation === 'HOLD') {
      console.log('📊 Recommendation is HOLD')
      return false
    }

    // Check if we have required trading levels
    if (!analysis.entry || !analysis.stopLoss) {
      console.log('📊 Missing entry or stop loss levels')
      return false
    }

    // Check risk/reward ratio
    if (analysis.riskToReward) {
      const rr = this.parseRiskReward(analysis.riskToReward)
      if (rr < 2) {
        console.log(`📊 Risk/reward ratio too low: ${rr}`)
        return false
      }
    }

    // Check recent performance for dynamic adjustments
    const recentPerformance = await this.getRecentPerformance(config.userId)
    if (recentPerformance.winRate < 0.4 && recentPerformance.totalTrades > 10) {
      console.log('📊 Recent performance too poor, requiring higher confidence')
      return analysis.confidence > 80
    }

    return true
  }

  private async checkForOpenPositions(config: AutomationConfig): Promise<boolean> {
    try {
      console.log(`🔍 Checking for open positions for ${config.symbol}`)
      
      // For Jupiter DEX, we don't have persistent positions like in Drift
      // This method would need to be implemented based on your specific needs
      // For now, return false to allow trading
      
      if (config.dexProvider === 'DRIFT') {
        // Check Drift positions via API
        const response = await fetch('http://localhost:3000/api/drift/positions')
        if (!response.ok) {
          console.warn('⚠️ Could not fetch Drift positions, assuming no open positions')
          return false
        }
        
        const data = await response.json()
        if (!data.success || !data.positions) {
          return false
        }
        
        // Check if there's an open position for the current symbol
        const symbolBase = config.symbol.replace('USD', '') // SOLUSD -> SOL
        const openPosition = data.positions.find((pos: any) => 
          pos.symbol.includes(symbolBase) && pos.size > 0.001
        )
        
        if (openPosition) {
          console.log(`📊 Found open ${openPosition.side} position: ${openPosition.symbol} ${openPosition.size}`)
          return true
        }
      }
      
      return false
      
    } catch (error) {
      console.error('❌ Error checking positions:', error)
      // On error, assume no positions to allow trading
      return false
    }
  }

  private async executeTrade(config: AutomationConfig, analysis: AnalysisResult, screenshotUrl: string) {
    try {
      console.log(`🚀 Executing ${config.mode} trade: ${analysis.recommendation} ${config.symbol}`)

      const side = analysis.recommendation === 'BUY' ? 'BUY' : 'SELL'
      const amount = await this.calculateTradeAmount(config, analysis)
      const leverage = Math.min(config.maxLeverage, 3) // Cap at 3x for safety

      let tradeResult: any = null

      if (config.mode === 'SIMULATION') {
        // Simulate trade
        tradeResult = await this.simulateTrade({
          symbol: config.symbol,
          side,
          amount,
          price: analysis.entry?.price || 0,
          stopLoss: analysis.stopLoss?.price,
          takeProfit: analysis.takeProfits?.tp1?.price,
          leverage
        })
      } else {
        // Execute real trade via unified trading endpoint
        tradeResult = await this.executeUnifiedTrade({
          symbol: config.symbol,
          side,
          amount,
          stopLoss: analysis.stopLoss?.price,
          takeProfit: analysis.takeProfits?.tp1?.price,
          leverage,
          dexProvider: config.dexProvider
        })
      }

      // Store trade in database
      await prisma.trade.create({
        data: {
          userId: config.userId,
          symbol: config.symbol,
          side,
          amount,
          price: analysis.entry?.price || 0,
          status: tradeResult?.success ? 'FILLED' : 'FAILED',
          isAutomated: true,
          entryPrice: analysis.entry?.price,
          stopLoss: analysis.stopLoss?.price,
          takeProfit: analysis.takeProfits?.tp1?.price,
          leverage,
          timeframe: config.timeframe,
          tradingMode: config.mode,
          confidence: analysis.confidence,
          marketSentiment: analysis.marketSentiment,
          screenshotUrl,
          aiAnalysis: JSON.stringify(analysis),
          driftTxId: tradeResult?.txId,
          executedAt: new Date()
        }
      })

      console.log(`✅ Trade executed: ${tradeResult?.success ? 'SUCCESS' : 'FAILED'}`)

    } catch (error) {
      console.error('Trade execution error:', error)
      
      // Store failed trade
      await prisma.trade.create({
        data: {
          userId: config.userId,
          symbol: config.symbol,
          side: analysis.recommendation === 'BUY' ? 'BUY' : 'SELL',
          amount: config.tradingAmount,
          price: analysis.entry?.price || 0,
          status: 'FAILED',
          isAutomated: true,
          timeframe: config.timeframe,
          tradingMode: config.mode,
          confidence: analysis.confidence,
          marketSentiment: analysis.marketSentiment,
          screenshotUrl,
          aiAnalysis: JSON.stringify(analysis)
        }
      })
    }
  }

  private async executeUnifiedTrade(params: {
    symbol: string
    side: string
    amount: number
    stopLoss?: number
    takeProfit?: number
    leverage?: number
    dexProvider: 'JUPITER' | 'DRIFT'
  }): Promise<{ success: boolean; txId?: string }> {
    try {
      console.log(`🚀 Executing ${params.dexProvider} trade: ${params.side} ${params.amount} ${params.symbol}`)
      
      const response = await fetch('http://localhost:3000/api/automation/trade', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          symbol: params.symbol,
          side: params.side,
          amount: params.amount,
          leverage: params.leverage,
          stopLoss: params.stopLoss,
          takeProfit: params.takeProfit,
          dexProvider: params.dexProvider,
          mode: 'LIVE'
        })
      })

      if (!response.ok) {
        throw new Error(`Trade request failed: ${response.statusText}`)
      }

      const result = await response.json()
      return {
        success: result.success,
        txId: result.txId || result.transactionId
      }
    } catch (error) {
      console.error('Unified trade execution error:', error)
      return { success: false }
    }
  }

  private async simulateTrade(params: {
    symbol: string
    side: string
    amount: number
    price: number
    stopLoss?: number
    takeProfit?: number
    leverage?: number
  }): Promise<{ success: boolean; txId?: string }> {
    // Simulate realistic execution with small random variation
    const priceVariation = 0.001 * (Math.random() - 0.5) // ±0.1%
    const executedPrice = params.price * (1 + priceVariation)
    
    // Simulate network delay
    await new Promise(resolve => setTimeout(resolve, 500))
    
    return {
      success: true,
      txId: `sim_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`
    }
  }

  private async calculateTradeAmount(config: AutomationConfig, analysis: AnalysisResult): Promise<number> {
    try {
      // Fetch actual account balance from Drift
      console.log('💰 Fetching account balance for position sizing...')
      const balanceResponse = await fetch(`http://localhost:3000/api/drift/balance`)
      
      if (!balanceResponse.ok) {
        console.log('⚠️ Failed to fetch balance, using fallback calculation')
        // Fallback to config amount
        let amount = Math.min(config.tradingAmount, 35) // Cap at $35 max
        const riskAdjustment = config.riskPercentage / 100
        return Math.max(amount * riskAdjustment, 5)
      }

      const balanceData = await balanceResponse.json()
      const availableBalance = parseFloat(balanceData.availableBalance || '0')
      
      console.log(`💰 Available balance: $${availableBalance}`)

      if (availableBalance <= 0) {
        throw new Error('No available balance')
      }

      // Calculate position size based on risk percentage of available balance
      const riskAmount = availableBalance * (config.riskPercentage / 100)
      
      // Adjust based on confidence (reduce risk for low confidence signals)
      const confidenceMultiplier = Math.min(analysis.confidence / 100, 1)
      let amount = riskAmount * confidenceMultiplier

      // Apply leverage to get position size
      amount *= Math.min(config.maxLeverage, 10)

      // Ensure minimum trade amount but cap at available balance
      amount = Math.max(amount, 5) // Minimum $5 position
      amount = Math.min(amount, availableBalance * 0.8) // Never use more than 80% of balance

      console.log(`📊 Position sizing calculation:`)
      console.log(`  - Available balance: $${availableBalance}`)
      console.log(`  - Risk percentage: ${config.riskPercentage}%`)
      console.log(`  - Risk amount: $${riskAmount.toFixed(2)}`)
      console.log(`  - Confidence multiplier: ${confidenceMultiplier}`)
      console.log(`  - Leverage: ${Math.min(config.maxLeverage, 10)}x`)
      console.log(`  - Final position size: $${amount.toFixed(2)}`)

      return Math.round(amount * 100) / 100 // Round to 2 decimal places
      
    } catch (error) {
      console.log(`⚠️ Error calculating trade amount: ${error instanceof Error ? error.message : String(error)}`)
      // Safe fallback - use small fixed amount
      return 5
    }
  }

  private parseRiskReward(rrString: string): number {
    // Parse "1:2.5" format
    const parts = rrString.split(':')
    if (parts.length === 2) {
      return parseFloat(parts[1]) / parseFloat(parts[0])
    }
    return 0
  }

  private getIntervalFromTimeframe(timeframe: string): number {
    const intervals: { [key: string]: number } = {
      '1m': 1 * 60 * 1000,
      '5m': 5 * 60 * 1000,
      '15m': 15 * 60 * 1000,
      '30m': 30 * 60 * 1000,
      '1h': 60 * 60 * 1000,
      '2h': 2 * 60 * 60 * 1000,
      '4h': 4 * 60 * 60 * 1000,
      '6h': 6 * 60 * 60 * 1000,
      '12h': 12 * 60 * 60 * 1000,
      '1d': 24 * 60 * 60 * 1000
    }

    return intervals[timeframe] || intervals['1h'] // Default to 1 hour
  }

  private async getRecentPerformance(userId: string): Promise<{
    winRate: number
    totalTrades: number
    avgRR: number
  }> {
    const thirtyDaysAgo = new Date()
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)

    const trades = await prisma.trade.findMany({
      where: {
        userId,
        isAutomated: true,
        createdAt: {
          gte: thirtyDaysAgo
        },
        status: 'FILLED'
      }
    })

    const totalTrades = trades.length
    const winningTrades = trades.filter(t => (t.pnlPercent || 0) > 0).length
    const winRate = totalTrades > 0 ? winningTrades / totalTrades : 0
    const avgRR = trades.reduce((sum, t) => sum + (t.actualRR || 0), 0) / Math.max(totalTrades, 1)

    return { winRate, totalTrades, avgRR }
  }

  private async updateSessionStats(userId: string) {
    try {
      if (!this.activeSession) return

      const stats = await this.getRecentPerformance(userId)
      
      await prisma.automationSession.update({
        where: { id: this.activeSession.id },
        data: {
          totalTrades: stats.totalTrades,
          successfulTrades: Math.round(stats.totalTrades * stats.winRate),
          winRate: stats.winRate,
          avgRiskReward: stats.avgRR,
          lastAnalysis: new Date()
        }
      })
    } catch (error) {
      console.error('Failed to update session stats:', error)
    }
  }

  private async handleAutomationError(error: any) {
    try {
      if (this.activeSession) {
        await prisma.automationSession.update({
          where: { id: this.activeSession.id },
          data: {
            errorCount: { increment: 1 },
            lastError: error.message || 'Unknown error',
            updatedAt: new Date()
          }
        })

        // Stop automation if too many errors
        if (this.activeSession.errorCount >= 5) {
          console.log('❌ Too many errors, stopping automation')
          await this.stopAutomation()
        }
      }
    } catch (dbError) {
      console.error('Failed to handle automation error:', dbError)
    }
  }

  async getStatus(): Promise<AutomationStatus | null> {
    try {
      if (!this.activeSession) {
        return null
      }

      const session = await prisma.automationSession.findUnique({
        where: { id: this.activeSession.id }
      })

      if (!session) {
        return null
      }

      return {
        isActive: this.isRunning,
        mode: session.mode as 'SIMULATION' | 'LIVE',
        symbol: session.symbol,
        timeframe: session.timeframe,
        totalTrades: session.totalTrades,
        successfulTrades: session.successfulTrades,
        winRate: session.winRate,
        totalPnL: session.totalPnL,
        lastAnalysis: session.lastAnalysis || undefined,
        lastTrade: session.lastTrade || undefined,
        nextScheduled: session.nextScheduled || undefined,
        errorCount: session.errorCount,
        lastError: session.lastError || undefined
      }
    } catch (error) {
      console.error('Failed to get automation status:', error)
      return null
    }
  }

  async getLearningInsights(userId: string): Promise<{
    totalAnalyses: number
    avgAccuracy: number
    bestTimeframe: string
    worstTimeframe: string
    commonFailures: string[]
    recommendations: string[]
  }> {
    try {
      const learningData = await prisma.aILearningData.findMany({
        where: { userId },
        orderBy: { createdAt: 'desc' },
        take: 100
      })

      const totalAnalyses = learningData.length
      const avgAccuracy = learningData.reduce((sum, d) => sum + (d.accuracyScore || 0), 0) / Math.max(totalAnalyses, 1)

      // Group by timeframe to find best/worst
      const timeframeStats = learningData.reduce((acc, d) => {
        if (!acc[d.timeframe]) {
          acc[d.timeframe] = { count: 0, accuracy: 0 }
        }
        acc[d.timeframe].count += 1
        acc[d.timeframe].accuracy += d.accuracyScore || 0
        return acc
      }, {} as { [key: string]: { count: number, accuracy: number } })

      const timeframes = Object.entries(timeframeStats).map(([tf, stats]) => ({
        timeframe: tf,
        avgAccuracy: stats.accuracy / stats.count
      }))

      const bestTimeframe = timeframes.sort((a, b) => b.avgAccuracy - a.avgAccuracy)[0]?.timeframe || 'Unknown'
      const worstTimeframe = timeframes.sort((a, b) => a.avgAccuracy - b.avgAccuracy)[0]?.timeframe || 'Unknown'

      return {
        totalAnalyses,
        avgAccuracy,
        bestTimeframe,
        worstTimeframe,
        commonFailures: [
          'Low confidence predictions',
          'Missed support/resistance levels',
          'Timeframe misalignment'
        ],
        recommendations: [
          'Focus on higher timeframes for better accuracy',
          'Wait for higher confidence signals',
          'Use multiple timeframe confirmation'
        ]
      }
    } catch (error) {
      console.error('Failed to get learning insights:', error)
      return {
        totalAnalyses: 0,
        avgAccuracy: 0,
        bestTimeframe: 'Unknown',
        worstTimeframe: 'Unknown',
        commonFailures: [],
        recommendations: []
      }
    }
  }
}

export const automationService = new AutomationService()
