/**
 * Chain State Setup Script
 * 
 * Prepares a realistic staking environment BEFORE tests run:
 * 1. Funds test wallets (Anvil #3, #4, #5) with ETH + KRK
 * 2. Creates active positions at different tax rates
 * 3. Triggers a recenter to update pool state
 * 4. Advances time to simulate position age
 * 5. Takes chain snapshot for test resets
 */

import { ethers } from 'ethers';
import { readFileSync } from 'fs';
import { join } from 'path';

const RPC_URL = process.env.STACK_RPC_URL ?? 'http://localhost:8545';

// Anvil test accounts (private keys)
const DEPLOYER_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // Anvil #0
const MARCUS_KEY = '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6'; // Anvil #3
const SARAH_KEY = '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a'; // Anvil #4
const PRIYA_KEY = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'; // Anvil #5

interface ContractAddresses {
  Kraiken: string;
  Stake: string;
  LiquidityManager: string;
}

async function loadContracts(): Promise<ContractAddresses> {
  const deploymentsPath = join(process.cwd(), 'onchain', 'deployments-local.json');
  const deploymentsJson = readFileSync(deploymentsPath, 'utf-8');
  const deployments = JSON.parse(deploymentsJson);
  return deployments.contracts;
}

async function sendRpc(method: string, params: unknown[]): Promise<any> {
  const resp = await fetch(RPC_URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
  });
  const data = await resp.json();
  if (data.error) throw new Error(`RPC ${method} failed: ${data.error.message}`);
  return data.result;
}

async function main() {
  console.log('[SETUP] Starting chain state preparation...\n');

  const provider = new ethers.JsonRpcProvider(RPC_URL);
  const deployer = new ethers.Wallet(DEPLOYER_KEY, provider);
  const marcus = new ethers.Wallet(MARCUS_KEY, provider);
  const sarah = new ethers.Wallet(SARAH_KEY, provider);
  const priya = new ethers.Wallet(PRIYA_KEY, provider);

  const addresses = await loadContracts();
  console.log('[SETUP] Contract addresses loaded:');
  console.log(`  - Kraiken: ${addresses.Kraiken}`);
  console.log(`  - Stake: ${addresses.Stake}`);
  console.log(`  - LiquidityManager: ${addresses.LiquidityManager}\n`);

  // Contract ABIs (minimal required functions)
  const krkAbi = [
    'function transfer(address to, uint256 amount) returns (bool)',
    'function balanceOf(address account) view returns (uint256)',
    'function approve(address spender, uint256 amount) returns (bool)',
    'function minStake() view returns (uint256)',
  ];

  const stakeAbi = [
    'function snatch(uint256 assets, address receiver, uint32 taxRate, uint256[] calldata positionsToSnatch) returns (uint256)',
    'function getPosition(uint256 positionId) view returns (tuple(uint256 share, address owner, uint32 creationTime, uint32 lastTaxTime, uint32 taxRate))',
    // minStake() is on Kraiken, not Stake
    'function nextPositionId() view returns (uint256)',
  ];

  const lmAbi = [
    'function recenter() external returns (bool)',
  ];

  const krk = new ethers.Contract(addresses.Kraiken, krkAbi, deployer);
  const stake = new ethers.Contract(addresses.Stake, stakeAbi, deployer);
  const lm = new ethers.Contract(addresses.LiquidityManager, lmAbi, deployer);

  // Step 1: Fund test wallets with ETH
  console.log('[STEP 1] Funding test wallets with ETH...');
  const ethAmount = ethers.parseEther('100'); // 100 ETH each
  
  for (const [name, wallet] of [
    ['Marcus', marcus],
    ['Sarah', sarah],
    ['Priya', priya],
  ]) {
    await sendRpc('anvil_setBalance', [wallet.address, '0x' + ethAmount.toString(16)]);
    console.log(`  ✓ ${name} (${wallet.address}): 100 ETH`);
  }

  // Step 2: Transfer KRK from deployer to test wallets
  console.log('\n[STEP 2] Distributing KRK tokens...');
  const krkAmount = ethers.parseEther('1000'); // 1000 KRK each
  
  for (const [name, wallet] of [
    ['Marcus', marcus],
    ['Sarah', sarah],
    ['Priya', priya],
  ]) {
    const tx = await krk.transfer(wallet.address, krkAmount);
    await tx.wait();
    const balance = await krk.balanceOf(wallet.address);
    console.log(`  ✓ ${name}: ${ethers.formatEther(balance)} KRK`);
  }

  // Step 3: Create staking positions
  console.log('\n[STEP 3] Creating active staking positions...');
  
  const minStake = await krk.minStake();
  console.log(`  Minimum stake: ${ethers.formatEther(minStake)} KRK`);

  // Marcus stakes at LOW tax rate (index 2 = 5% yearly)
  console.log('\n  Creating Marcus position (LOW tax)...');
  const marcusAmount = ethers.parseEther('500');
  const marcusTaxRate = 2; // 5% yearly (0.0137% daily)
  
  const marcusKrk = krk.connect(marcus) as typeof krk;
  const marcusStake = stake.connect(marcus) as typeof stake;
  
  let approveTx = await marcusKrk.approve(addresses.Stake, marcusAmount);
  await approveTx.wait();
  
  // Explicit nonce to avoid stale nonce cache
  let nonce = await provider.getTransactionCount(marcus.address);
  let snatchTx = await marcusStake.snatch(marcusAmount, marcus.address, marcusTaxRate, [], { nonce });
  let receipt = await snatchTx.wait();
  console.log(`  ✓ Marcus position created (500 KRK @ 5% tax)`);

  // Sarah stakes at MEDIUM tax rate (index 10 = 60% yearly)
  console.log('\n  Creating Sarah position (MEDIUM tax)...');
  const sarahAmount = ethers.parseEther('500');
  const sarahTaxRate = 10; // 60% yearly (0.1644% daily)
  
  const sarahKrk = krk.connect(sarah) as typeof krk;
  const sarahStake = stake.connect(sarah) as typeof stake;
  
  approveTx = await sarahKrk.approve(addresses.Stake, sarahAmount);
  await approveTx.wait();
  
  nonce = await provider.getTransactionCount(sarah.address);
  snatchTx = await sarahStake.snatch(sarahAmount, sarah.address, sarahTaxRate, [], { nonce });
  receipt = await snatchTx.wait();
  console.log(`  ✓ Sarah position created (500 KRK @ 60% tax)`);

  // Step 4: Trigger recenter via deployer
  console.log('\n[STEP 4] Triggering recenter to update liquidity positions...');
  try {
    const recenterTx = await lm.recenter();
    await recenterTx.wait();
    console.log('  ✓ Recenter successful');
  } catch (error: any) {
    console.log(`  ⚠ Recenter failed (may be expected): ${error.message}`);
  }

  // Step 5: Advance time by 1 day
  console.log('\n[STEP 5] Advancing chain time by 1 day...');
  const oneDay = 86400; // seconds
  await sendRpc('anvil_increaseTime', [oneDay]);
  await sendRpc('anvil_mine', [1]);
  console.log('  ✓ Time advanced by 1 day');

  // Step 6: Take chain snapshot
  console.log('\n[STEP 6] Taking chain snapshot for test resets...');
  const snapshotId = await sendRpc('evm_snapshot', []);
  console.log(`  ✓ Snapshot ID: ${snapshotId}`);

  // Verify final state
  console.log('\n[VERIFICATION] Final chain state:');
  const nextPosId = await stake.nextPositionId();
  console.log(`  - Next position ID: ${nextPosId}`);
  
  for (const [name, wallet] of [
    ['Marcus', marcus],
    ['Sarah', sarah],
    ['Priya', priya],
  ]) {
    const balance = await krk.balanceOf(wallet.address);
    console.log(`  - ${name} KRK balance: ${ethers.formatEther(balance)} KRK`);
  }

  console.log('\n✅ Chain state setup complete!');
  console.log('   Tests can now run against this prepared state.\n');
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error('\n❌ Setup failed:', error);
    process.exit(1);
  });
