import { Connection, Keypair, VersionedTransaction, PublicKey } from '@solana/web3.js'
import fetch from 'cross-fetch'

export interface JupiterQuote {
  inputMint: string
  inAmount: string
  outputMint: string
  outAmount: string
  otherAmountThreshold: string
  swapMode: string
  slippageBps: number
  priceImpactPct: string
  routePlan: any[]
}

export interface TradeOrder {
  id: string
  symbol: string
  side: 'BUY' | 'SELL'
  amount: number
  entryPrice?: number
  stopLoss?: number
  takeProfit?: number
  status: 'PENDING' | 'FILLED' | 'CANCELLED' | 'MONITORING'
  txId?: string
  timestamp: number
}

class JupiterDEXService {
  private connection: Connection
  private keypair: Keypair | null = null
  private activeOrders: TradeOrder[] = []
  
  // Token mint addresses
  private tokens = {
    SOL: 'So11111111111111111111111111111111111111112', // Wrapped SOL
    USDC: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
    USDT: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
  }

  constructor() {
    const rpcUrl = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'
    this.connection = new Connection(rpcUrl, 'confirmed')
    this.initializeWallet()
  }

  private initializeWallet() {
    try {
      if (process.env.SOLANA_PRIVATE_KEY) {
        const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY)
        this.keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray))
        console.log('✅ Jupiter DEX wallet initialized:', this.keypair.publicKey.toString())
      } else {
        console.warn('⚠️ No SOLANA_PRIVATE_KEY found for Jupiter DEX')
      }
    } catch (error) {
      console.error('❌ Failed to initialize Jupiter DEX wallet:', error)
    }
  }

  async getQuote(
    inputMint: string,
    outputMint: string,
    amount: number,
    slippageBps: number = 50 // 0.5% slippage
  ): Promise<JupiterQuote | null> {
    try {
      const url = `https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${amount}&slippageBps=${slippageBps}`
      
      console.log('🔍 Getting Jupiter quote:', { inputMint, outputMint, amount })
      
      const response = await fetch(url)
      if (!response.ok) {
        throw new Error(`Jupiter API error: ${response.status}`)
      }
      
      const quote = await response.json()
      console.log('📊 Jupiter quote received:', quote)
      
      return quote
    } catch (error) {
      console.error('❌ Failed to get Jupiter quote:', error)
      return null
    }
  }

  async executeSwap(
    inputMint: string,
    outputMint: string,
    amount: number,
    slippageBps: number = 50
  ): Promise<{
    success: boolean
    txId?: string
    error?: string
    executionPrice?: number
    inputAmount?: number
    outputAmount?: number
    fees?: number
    slippage?: number
  }> {
    if (!this.keypair) {
      return { success: false, error: 'Wallet not initialized' }
    }

    try {
      console.log('🔄 Executing Jupiter swap:', { inputMint, outputMint, amount })

      // 1. Get quote
      const quote = await this.getQuote(inputMint, outputMint, amount, slippageBps)
      if (!quote) {
        return { success: false, error: 'Failed to get quote' }
      }

      // 2. Get swap transaction
      const swapResponse = await fetch('https://quote-api.jup.ag/v6/swap', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          quoteResponse: quote,
          userPublicKey: this.keypair.publicKey.toString(),
          wrapAndUnwrapSol: true,
        })
      })

      if (!swapResponse.ok) {
        throw new Error(`Swap API error: ${swapResponse.status}`)
      }

      const { swapTransaction } = await swapResponse.json()

      // 3. Deserialize and sign transaction
      const swapTransactionBuf = Buffer.from(swapTransaction, 'base64')
      const transaction = VersionedTransaction.deserialize(swapTransactionBuf)
      transaction.sign([this.keypair])

      // 4. Submit transaction
      const txId = await this.connection.sendTransaction(transaction)
      
      // 5. Confirm transaction
      const confirmation = await this.connection.confirmTransaction(txId, 'confirmed')
      
      if (confirmation.value.err) {
        return { success: false, error: `Transaction failed: ${confirmation.value.err}` }
      }

      // 6. Calculate execution details from quote
      const inputAmount = parseFloat(quote.inAmount)
      const outputAmount = parseFloat(quote.outAmount)
      
      // Calculate execution price based on swap direction
      let executionPrice: number
      if (inputMint === this.tokens.USDC) {
        // Buying SOL with USDC: price = USDC spent / SOL received
        executionPrice = (inputAmount / 1e6) / (outputAmount / 1e9) // Convert from token units
      } else {
        // Selling SOL for USDC: price = USDC received / SOL spent  
        executionPrice = (outputAmount / 1e6) / (inputAmount / 1e9) // Convert from token units
      }

      console.log('✅ Jupiter swap successful:', txId)
      return { 
        success: true, 
        txId,
        executionPrice,
        inputAmount: inputAmount / (inputMint === this.tokens.USDC ? 1e6 : 1e9),
        outputAmount: outputAmount / (outputMint === this.tokens.USDC ? 1e6 : 1e9),
        fees: (inputAmount / (inputMint === this.tokens.USDC ? 1e6 : 1e9)) * 0.001, // Estimate 0.1% fees
        slippage: parseFloat(quote.priceImpactPct || '0')
      }

    } catch (error: any) {
      console.error('❌ Jupiter swap failed:', error)
      return { success: false, error: error.message }
    }
  }

  async executeTrade(params: {
    symbol: string
    side: 'BUY' | 'SELL'
    amount: number
    stopLoss?: number
    takeProfit?: number
    tradingPair?: string
    quickSwap?: boolean
  }): Promise<{
    success: boolean
    orderId?: string
    txId?: string
    error?: string
  }> {
    try {
      const { symbol, side, amount, stopLoss, takeProfit, tradingPair, quickSwap } = params
      
      console.log('🎯 Executing real DEX trade:', params)

      // Handle different trading pairs
      let inputMint: string
      let outputMint: string
      let amountLamports: number

      if (quickSwap || tradingPair === 'SOL/USDC') {
        // SOL to USDC swap
        inputMint = this.tokens.SOL
        outputMint = this.tokens.USDC
        amountLamports = Math.floor(amount * 1000000000) // SOL has 9 decimals
      } else if (tradingPair === 'USDC/SOL') {
        // USDC to SOL swap
        inputMint = this.tokens.USDC
        outputMint = this.tokens.SOL
        amountLamports = Math.floor(amount * 1000000) // USDC has 6 decimals
      } else {
        // Default behavior based on side
        inputMint = side === 'BUY' ? this.tokens.USDC : this.tokens.SOL
        outputMint = side === 'BUY' ? this.tokens.SOL : this.tokens.USDC
        amountLamports = side === 'BUY' 
          ? Math.floor(amount * 1000000) // USDC has 6 decimals
          : Math.floor(amount * 1000000000) // SOL has 9 decimals
      }

      // Execute the swap
      const swapResult = await this.executeSwap(inputMint, outputMint, amountLamports)
      
      if (!swapResult.success) {
        return { success: false, error: swapResult.error }
      }

      // Create order tracking
      const orderId = `jupiter_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`
      const order: TradeOrder = {
        id: orderId,
        symbol,
        side,
        amount,
        stopLoss,
        takeProfit,
        status: stopLoss || takeProfit ? 'MONITORING' : 'FILLED',
        txId: swapResult.txId,
        timestamp: Date.now()
      }

      this.activeOrders.push(order)

      // Start monitoring for TP/SL if needed
      if (stopLoss || takeProfit) {
        this.startOrderMonitoring(order)
      }

      return {
        success: true,
        orderId,
        txId: swapResult.txId
      }

    } catch (error: any) {
      console.error('❌ DEX trade execution failed:', error)
      return { success: false, error: error.message }
    }
  }

  private async startOrderMonitoring(order: TradeOrder) {
    console.log('👁️ Starting TP/SL monitoring for order:', order.id)
    
    // This would run in a background process
    // For now, we'll log that monitoring started
    
    // TODO: Implement continuous price monitoring
    // - Check current price every few seconds
    // - Execute reverse trade when TP/SL is hit
    // - Update order status
  }

  getActiveOrders(): TradeOrder[] {
    return this.activeOrders
  }

  async cancelOrder(orderId: string): Promise<boolean> {
    const orderIndex = this.activeOrders.findIndex(o => o.id === orderId)
    if (orderIndex >= 0) {
      this.activeOrders[orderIndex].status = 'CANCELLED'
      return true
    }
    return false
  }

  isConfigured(): boolean {
    return this.keypair !== null
  }
}

export const jupiterDEXService = new JupiterDEXService()
export default JupiterDEXService
