const { DriftClient, initialize, OrderType, PositionDirection, OrderTriggerCondition, PostOnlyParams } = require('@drift-labs/sdk');
const { Connection, Keypair } = require('@solana/web3.js');
const { Wallet } = require('@coral-xyz/anchor');

/**
 * Test minimum percentages for stop loss and take profit with real small positions
 * This script will test progressively smaller percentages to find the actual minimum
 * that Drift Protocol accepts for order placement.
 */

class DriftMinimumPercentageTester {
  constructor() {
    this.driftClient = null;
    this.connection = null;
    this.testResults = [];
  }

  async initialize() {
    console.log('🚀 Initializing Drift Protocol connection...');
    
    try {
      // Initialize connection with fallback RPC endpoints
      const rpcEndpoints = [
        process.env.SOLANA_RPC_URL_SECONDARY || 'https://api.mainnet-beta.solana.com',
        process.env.SOLANA_RPC_URL_TERTIARY || 'https://solana-mainnet.g.alchemy.com/v2/demo',
        process.env.SOLANA_RPC_URL_BACKUP || 'https://rpc.ankr.com/solana'
      ];
      
      let connection = null;
      for (const endpoint of rpcEndpoints) {
        try {
          console.log(`🔄 Trying RPC endpoint: ${endpoint}`);
          connection = new Connection(endpoint, 'confirmed');
          // Test the connection
          await connection.getVersion();
          console.log(`✅ Connected to: ${endpoint}`);
          break;
        } catch (error) {
          console.log(`❌ Failed to connect to ${endpoint}: ${error.message}`);
        }
      }
      
      if (!connection) {
        throw new Error('Could not connect to any RPC endpoint');
      }
      
      this.connection = connection;

      // Create wallet from private key
      if (!process.env.SOLANA_PRIVATE_KEY) {
        throw new Error('SOLANA_PRIVATE_KEY environment variable not set');
      }

      const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY);
      const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray));
      const wallet = new Wallet(keypair);

      console.log(`📍 Wallet address: ${keypair.publicKey.toString()}`);

      // Initialize Drift SDK
      const sdkConfig = initialize({ env: 'mainnet-beta' });
      
      this.driftClient = new DriftClient({
        connection: this.connection,
        wallet,
        programID: sdkConfig.DRIFT_PROGRAM_ID,
        opts: { 
          commitment: 'confirmed',
          skipPreflight: false,
          preflightCommitment: 'confirmed'
        }
      });

      await this.driftClient.subscribe();
      console.log('✅ Drift client initialized and subscribed');

      // Check account balance
      await this.checkAccountStatus();
      
    } catch (error) {
      console.error('❌ Initialization failed:', error.message);
      throw error;
    }
  }

  async checkAccountStatus() {
    try {
      const user = this.driftClient.getUser();
      const userAccount = user.getUserAccount();
      
      // Get collateral and free collateral
      const totalCollateral = user.getTotalCollateral();
      const freeCollateral = user.getFreeCollateral();
      
      console.log('\n💰 Account Status:');
      console.log(`  Total Collateral: $${(totalCollateral / 1e6).toFixed(2)}`);
      console.log(`  Free Collateral: $${(freeCollateral / 1e6).toFixed(2)}`);
      
      if (freeCollateral < 1e6) { // Less than $1
        console.log('⚠️  Warning: Low free collateral - tests will use very small positions');
      }

      // Get current positions
      const positions = user.getPositions().filter(pos => !pos.baseAssetAmount.eq(0));
      console.log(`  Open Positions: ${positions.length}`);
      
      positions.forEach((pos, index) => {
        const market = this.driftClient.getPerpMarketAccount(pos.marketIndex);
        const marketName = market.name || `Market ${pos.marketIndex}`;
        const size = Number(pos.baseAssetAmount) / 1e9;
        console.log(`    ${index + 1}. ${marketName}: ${size.toFixed(4)} SOL`);
      });

    } catch (error) {
      console.error('❌ Failed to check account status:', error.message);
    }
  }

  async getCurrentPrice(marketIndex = 0) {
    try {
      const perpMarketAccount = this.driftClient.getPerpMarketAccount(marketIndex);
      const currentPrice = Number(perpMarketAccount.amm.lastMarkPriceTwap) / 1e6;
      return currentPrice;
    } catch (error) {
      console.error('❌ Failed to get current price:', error.message);
      return null;
    }
  }

  async testMinimumPercentage(stopLossPercent, takeProfitPercent, marketIndex = 0) {
    console.log(`\n🧪 Testing SL: ${stopLossPercent}% / TP: ${takeProfitPercent}%`);
    
    try {
      const currentPrice = await this.getCurrentPrice(marketIndex);
      if (!currentPrice) {
        throw new Error('Could not get current price');
      }

      console.log(`  📊 Current SOL price: $${currentPrice.toFixed(4)}`);

      // Calculate prices
      const stopLossPrice = currentPrice * (1 - stopLossPercent / 100);
      const takeProfitPrice = currentPrice * (1 + takeProfitPercent / 100);
      
      console.log(`  🎯 Entry: $${currentPrice.toFixed(4)}`);
      console.log(`  🛑 Stop Loss: $${stopLossPrice.toFixed(4)} (${stopLossPercent}% below)`);
      console.log(`  💰 Take Profit: $${takeProfitPrice.toFixed(4)} (${takeProfitPercent}% above)`);

      // Use very small position size for testing
      const baseAssetAmount = Math.floor(0.001 * 1e9); // 0.001 SOL (~$0.20)
      
      const result = {
        stopLossPercent,
        takeProfitPercent,
        currentPrice,
        stopLossPrice,
        takeProfitPrice,
        baseAssetAmount,
        success: false,
        error: null,
        orderIds: []
      };

      try {
        // Step 1: Place a small long position
        console.log('  📝 Step 1: Placing small long position...');
        
        const longOrderParams = {
          orderType: OrderType.MARKET,
          marketIndex,
          direction: PositionDirection.LONG,
          baseAssetAmount,
          reduceOnly: false,
        };

        const longOrderTx = await this.driftClient.placePerpOrder(longOrderParams);
        console.log(`    ✅ Long position placed. TX: ${longOrderTx}`);
        result.orderIds.push(longOrderTx);

        // Wait a moment for order to settle
        await new Promise(resolve => setTimeout(resolve, 2000));

        // Step 2: Place stop loss order
        console.log('  📝 Step 2: Placing stop loss order...');
        
        const stopLossParams = {
          orderType: OrderType.TRIGGER_LIMIT,
          marketIndex,
          direction: PositionDirection.SHORT,
          baseAssetAmount,
          price: Math.floor(stopLossPrice * 1e6),
          triggerPrice: Math.floor(stopLossPrice * 1e6),
          triggerCondition: OrderTriggerCondition.BELOW,
          reduceOnly: true,
        };

        const stopLossTx = await this.driftClient.placePerpOrder(stopLossParams);
        console.log(`    ✅ Stop loss placed. TX: ${stopLossTx}`);
        result.orderIds.push(stopLossTx);

        // Step 3: Place take profit order
        console.log('  📝 Step 3: Placing take profit order...');
        
        const takeProfitParams = {
          orderType: OrderType.TRIGGER_LIMIT,
          marketIndex,
          direction: PositionDirection.SHORT,
          baseAssetAmount,
          price: Math.floor(takeProfitPrice * 1e6),
          triggerPrice: Math.floor(takeProfitPrice * 1e6),
          triggerCondition: OrderTriggerCondition.ABOVE,
          reduceOnly: true,
        };

        const takeProfitTx = await this.driftClient.placePerpOrder(takeProfitParams);
        console.log(`    ✅ Take profit placed. TX: ${takeProfitTx}`);
        result.orderIds.push(takeProfitTx);

        result.success = true;
        console.log(`  ✅ SUCCESS: ${stopLossPercent}%/${takeProfitPercent}% percentages work!`);

        // Cancel orders immediately to clean up
        await this.cleanupTestOrders(result.orderIds);

      } catch (orderError) {
        result.error = orderError.message;
        console.log(`  ❌ FAILED: ${orderError.message}`);
        
        // Try to clean up any partial orders
        if (result.orderIds.length > 0) {
          await this.cleanupTestOrders(result.orderIds);
        }
      }

      this.testResults.push(result);
      return result;

    } catch (error) {
      console.error(`  ❌ Test setup failed: ${error.message}`);
      return {
        stopLossPercent,
        takeProfitPercent,
        success: false,
        error: error.message,
        orderIds: []
      };
    }
  }

  async cleanupTestOrders(orderIds) {
    console.log('  🧹 Cleaning up test orders...');
    
    for (const orderId of orderIds) {
      try {
        // Cancel all open orders for the user
        const openOrders = this.driftClient.getUser().getOpenOrders();
        
        for (const order of openOrders) {
          if (order.marketIndex === 0) { // SOL-PERP market
            await this.driftClient.cancelOrder(order.orderId);
            console.log(`    🗑️ Cancelled order ${order.orderId}`);
          }
        }
        
        await new Promise(resolve => setTimeout(resolve, 1000));
        
      } catch (error) {
        console.log(`    ⚠️ Could not cancel order: ${error.message}`);
      }
    }
  }

  async runComprehensiveTest() {
    console.log('🎯 Running Comprehensive Minimum Percentage Test');
    console.log('   Goal: Find the actual minimum SL/TP percentages for Drift Protocol');
    console.log('   Method: Test with real small positions, progressively reducing percentages\n');

    // Test cases - start with current minimums and work down
    const testCases = [
      // Current minimums (should work)
      { sl: 3.0, tp: 1.0, desc: 'Current minimums' },
      
      // Moderate reductions
      { sl: 2.0, tp: 0.8, desc: 'Moderate reduction' },
      { sl: 1.5, tp: 0.6, desc: 'Moderate-aggressive' },
      
      // Aggressive reductions for scalping
      { sl: 1.0, tp: 0.5, desc: 'Aggressive scalping' },
      { sl: 0.8, tp: 0.4, desc: 'Very aggressive' },
      { sl: 0.5, tp: 0.3, desc: 'Ultra-tight scalping' },
      
      // Extreme minimums (likely to fail)
      { sl: 0.3, tp: 0.2, desc: 'Extreme tight' },
      { sl: 0.1, tp: 0.1, desc: 'Minimal possible' }
    ];

    for (let i = 0; i < testCases.length; i++) {
      const testCase = testCases[i];
      console.log(`\n📋 Test ${i + 1}/${testCases.length}: ${testCase.desc}`);
      
      const result = await this.testMinimumPercentage(testCase.sl, testCase.tp);
      
      if (!result.success) {
        console.log(`⚠️  Stopping tests - found minimum threshold at previous test`);
        break;
      }
      
      // Wait between tests to avoid rate limiting
      if (i < testCases.length - 1) {
        console.log('   ⏳ Waiting 5 seconds before next test...');
        await new Promise(resolve => setTimeout(resolve, 5000));
      }
    }

    this.generateReport();
  }

  generateReport() {
    console.log('\n📊 TEST RESULTS SUMMARY');
    console.log('=' .repeat(50));
    
    const successfulTests = this.testResults.filter(r => r.success);
    const failedTests = this.testResults.filter(r => !r.success);
    
    console.log(`✅ Successful tests: ${successfulTests.length}`);
    console.log(`❌ Failed tests: ${failedTests.length}`);
    
    if (successfulTests.length > 0) {
      const tightestSuccessful = successfulTests[successfulTests.length - 1];
      console.log('\n🎯 RECOMMENDED NEW MINIMUMS:');
      console.log(`   Stop Loss: ${tightestSuccessful.stopLossPercent}%`);
      console.log(`   Take Profit: ${tightestSuccessful.takeProfitPercent}%`);
      
      console.log('\n📝 Code Update Needed:');
      console.log('   File: app/api/drift/trade/route.js');
      console.log('   Lines 273-274:');
      console.log(`   const stopLossPercentCalc = Math.max(stopLossPercent / 100, ${(tightestSuccessful.stopLossPercent / 100).toFixed(3)}) // ${tightestSuccessful.stopLossPercent}% minimum`);
      console.log(`   const takeProfitPercentCalc = Math.max(takeProfitPercent / 100, ${(tightestSuccessful.takeProfitPercent / 100).toFixed(3)}) // ${tightestSuccessful.takeProfitPercent}% minimum`);
    }
    
    if (failedTests.length > 0) {
      console.log('\n❌ Failed Percentages:');
      failedTests.forEach(test => {
        console.log(`   ${test.stopLossPercent}%/${test.takeProfitPercent}%: ${test.error}`);
      });
    }
    
    console.log('\n💡 Next Steps:');
    console.log('1. Update the minimum percentages in the trading API');
    console.log('2. Test with real trading scenarios');
    console.log('3. Monitor order execution success rates');
    console.log('4. Consider implementing dynamic minimums based on volatility');
  }

  async cleanup() {
    if (this.driftClient) {
      try {
        await this.driftClient.unsubscribe();
        console.log('✅ Drift client unsubscribed');
      } catch (error) {
        console.error('⚠️ Error during cleanup:', error.message);
      }
    }
  }
}

// Main execution
async function main() {
  const tester = new DriftMinimumPercentageTester();
  
  try {
    await tester.initialize();
    await tester.runComprehensiveTest();
  } catch (error) {
    console.error('❌ Test execution failed:', error.message);
  } finally {
    await tester.cleanup();
  }
}

// Run if called directly
if (require.main === module) {
  main().catch(console.error);
}

module.exports = { DriftMinimumPercentageTester };
