#!/usr/bin/env node
/**
 * On-Chain Verification Script
 * Verifies token list entries against on-chain contracts using RPC calls
 * 
 * RPC endpoints (fallback order):
 * 1. https://rpc-http-pub.d-bis.org (primary)
 * 2. https://rpc-core.d-bis.org (fallback)
 */

import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
import { ethers } from 'ethers';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const RPC_ENDPOINTS = [
  'https://rpc-http-pub.d-bis.org',
  'https://rpc-core.d-bis.org'
];

const REQUIRED_CHAIN_ID = 138;
const REQUIRED_CHAIN_ID_HEX = '0x8a';

// ERC-20 ABI (minimal)
const ERC20_ABI = [
  'function decimals() view returns (uint8)',
  'function symbol() view returns (string)',
  'function name() view returns (string)',
  'function totalSupply() view returns (uint256)'
];

// Oracle ABI (Chainlink-compatible)
const ORACLE_ABI = [
  'function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',
  'function decimals() view returns (uint8)',
  'function description() view returns (string)'
];

async function getProvider() {
  let lastError;
  
  for (const rpcUrl of RPC_ENDPOINTS) {
    try {
      const provider = new ethers.JsonRpcProvider(rpcUrl);
      
      // Verify chain ID
      const network = await provider.getNetwork();
      const chainId = Number(network.chainId);
      
      if (chainId !== REQUIRED_CHAIN_ID) {
        throw new Error(`Chain ID mismatch: expected ${REQUIRED_CHAIN_ID}, got ${chainId}`);
      }
      
      // Test connection
      await provider.getBlockNumber();
      
      console.log(`✅ Connected to RPC: ${rpcUrl} (Chain ID: ${chainId})\n`);
      return provider;
    } catch (error) {
      lastError = error;
      console.log(`⚠️  Failed to connect to ${rpcUrl}: ${error.message}`);
      continue;
    }
  }
  
  throw new Error(`Failed to connect to any RPC endpoint. Last error: ${lastError?.message}`);
}

async function verifyERC20Token(provider, token, index) {
  const results = [];
  const prefix = `Token[${index}] ${token.symbol || token.name}`;
  
  try {
    // Check if contract exists
    const code = await provider.getCode(token.address);
    if (code === '0x') {
      results.push({ type: 'error', message: `${prefix}: No contract code at address ${token.address}` });
      return results;
    }
    
    const contract = new ethers.Contract(token.address, ERC20_ABI, provider);
    
    // Verify decimals
    try {
      const onChainDecimals = await contract.decimals();
      if (Number(onChainDecimals) !== token.decimals) {
        results.push({
          type: 'error',
          message: `${prefix}: Decimals mismatch - list: ${token.decimals}, on-chain: ${onChainDecimals}`
        });
      } else {
        results.push({ type: 'success', message: `${prefix}: Decimals verified (${token.decimals})` });
      }
    } catch (error) {
      results.push({ type: 'warning', message: `${prefix}: Failed to read decimals: ${error.message}` });
    }
    
    // Verify symbol (warn if different)
    try {
      const onChainSymbol = await contract.symbol();
      if (onChainSymbol !== token.symbol) {
        results.push({
          type: 'warning',
          message: `${prefix}: Symbol mismatch - list: "${token.symbol}", on-chain: "${onChainSymbol}"`
        });
      }
    } catch (error) {
      results.push({ type: 'warning', message: `${prefix}: Failed to read symbol: ${error.message}` });
    }
    
    // Verify name (warn if different)
    try {
      const onChainName = await contract.name();
      if (onChainName !== token.name) {
        results.push({
          type: 'warning',
          message: `${prefix}: Name mismatch - list: "${token.name}", on-chain: "${onChainName}"`
        });
      }
    } catch (error) {
      results.push({ type: 'warning', message: `${prefix}: Failed to read name: ${error.message}` });
    }
    
    // Verify totalSupply (optional)
    try {
      await contract.totalSupply();
      results.push({ type: 'success', message: `${prefix}: totalSupply() callable` });
    } catch (error) {
      results.push({ type: 'warning', message: `${prefix}: totalSupply() failed: ${error.message}` });
    }
    
  } catch (error) {
    results.push({ type: 'error', message: `${prefix}: Verification failed: ${error.message}` });
  }
  
  return results;
}

async function verifyOracleToken(provider, token, index) {
  const results = [];
  const prefix = `Token[${index}] ${token.symbol || token.name} (Oracle)`;
  
  try {
    // Check if contract exists
    const code = await provider.getCode(token.address);
    if (code === '0x') {
      results.push({ type: 'error', message: `${prefix}: No contract code at address ${token.address}` });
      return results;
    }
    
    const contract = new ethers.Contract(token.address, ORACLE_ABI, provider);
    
    // Verify latestRoundData
    try {
      await contract.latestRoundData();
      results.push({ type: 'success', message: `${prefix}: latestRoundData() callable` });
    } catch (error) {
      results.push({ type: 'error', message: `${prefix}: latestRoundData() failed: ${error.message}` });
    }
    
    // Verify decimals
    try {
      const onChainDecimals = await contract.decimals();
      if (Number(onChainDecimals) !== token.decimals) {
        results.push({
          type: 'error',
          message: `${prefix}: Decimals mismatch - list: ${token.decimals}, on-chain: ${onChainDecimals}`
        });
      } else {
        results.push({ type: 'success', message: `${prefix}: Decimals verified (${token.decimals})` });
      }
    } catch (error) {
      results.push({ type: 'warning', message: `${prefix}: Failed to read decimals: ${error.message}` });
    }
    
  } catch (error) {
    results.push({ type: 'error', message: `${prefix}: Verification failed: ${error.message}` });
  }
  
  return results;
}

function isOracleToken(token) {
  return token.tags && (token.tags.includes('oracle') || token.tags.includes('pricefeed'));
}

async function verifyOnChain(filePath, required = false) {
  console.log(`\n🔗 Verifying on-chain contracts: ${filePath}\n`);

  // Read token list file
  let tokenList;
  try {
    const fileContent = readFileSync(filePath, 'utf-8');
    tokenList = JSON.parse(fileContent);
  } catch (error) {
    console.error('❌ Error reading or parsing token list file:');
    console.error(`   ${error.message}`);
    process.exit(1);
  }

  let provider;
  try {
    provider = await getProvider();
  } catch (error) {
    if (required) {
      console.error(`❌ ${error.message}`);
      console.error('On-chain verification is required but RPC connection failed.');
      process.exit(1);
    } else {
      console.log(`⚠️  ${error.message}`);
      console.log('Skipping on-chain verification (optional mode)\n');
      return 0;
    }
  }

  if (!tokenList.tokens || !Array.isArray(tokenList.tokens)) {
    console.log('No tokens to verify.\n');
    return 0;
  }

  const allResults = [];
  
  for (const [index, token] of tokenList.tokens.entries()) {
    let results;
    if (isOracleToken(token)) {
      results = await verifyOracleToken(provider, token, index);
    } else {
      results = await verifyERC20Token(provider, token, index);
    }
    allResults.push(...results);
  }

  // Report results
  const errors = allResults.filter(r => r.type === 'error');
  const warnings = allResults.filter(r => r.type === 'warning');
  const successes = allResults.filter(r => r.type === 'success');

  if (errors.length > 0) {
    console.log('❌ Errors:');
    errors.forEach(r => console.log(`   ${r.message}`));
    console.log('');
  }

  if (warnings.length > 0) {
    console.log('⚠️  Warnings:');
    warnings.forEach(r => console.log(`   ${r.message}`));
    console.log('');
  }

  if (successes.length > 0) {
    console.log('✅ Verified:');
    successes.forEach(r => console.log(`   ${r.message}`));
    console.log('');
  }

  if (errors.length > 0) {
    console.log(`❌ Verification failed with ${errors.length} error(s)\n`);
    return 1;
  }

  console.log('✅ All on-chain verifications passed!\n');
  return 0;
}

// Main
const args = process.argv.slice(2);
const filePath = args.find(arg => !arg.startsWith('--')) || resolve(__dirname, '../lists/dbis-138.tokenlist.json');
const required = args.includes('--required');

if (!filePath) {
  console.error('Usage: node verify-on-chain.js [path/to/token-list.json] [--required]');
  process.exit(1);
}

verifyOnChain(filePath, required).then(exitCode => {
  process.exit(exitCode);
}).catch(error => {
  console.error('Unexpected error:', error);
  process.exit(1);
});

