

import { TokenInfo } from '../../types';
import { callRpc } from '../../core/rpc';
import { callExplorerApi } from '../../core/explorerApi';
import { 
  formatTokenAmount, 
  parseHexToDecimal, 
  formatDecimalToHex,
  standardizeTimestamp
} from '../../utils/formatters';
import { logger } from '../../utils/logger';

/**

 * @param {string} tokenAddress 
 * @returns {Promise<TokenInfo>} 
 */
export async function getTokenInfo(tokenAddress: string): Promise<TokenInfo> {
  try {

    const normalizedAddress = tokenAddress.startsWith('0x') ? tokenAddress : `0x${tokenAddress}`;
    
    if (normalizedAddress.length !== 42) {
      throw new Error(`Invalid token address format: ${normalizedAddress}`);
    }
    
    const code = await callRpc<string>('eth_getCode', [normalizedAddress, 'latest']);
    const isContract = code && code !== '0x' && code !== '0x0';
    
    if (!isContract) {
      throw new Error(`Address ${normalizedAddress} is not a contract`);
    }
    
    const nameSignature = '0x06fdde03';
    const symbolSignature = '0x95d89b41';
    const decimalsSignature = '0x313ce567';
    const totalSupplySignature = '0x18160ddd';
    
    const [nameResult, symbolResult, decimalsResult, totalSupplyResult] = await Promise.all([
      callRpc<string>('eth_call', [{
        to: normalizedAddress,
        data: nameSignature
      }, 'latest']).catch(() => '0x'),
      
      callRpc<string>('eth_call', [{
        to: normalizedAddress,
        data: symbolSignature
      }, 'latest']).catch(() => '0x'),
      
      callRpc<string>('eth_call', [{
        to: normalizedAddress,
        data: decimalsSignature
      }, 'latest']).catch(() => '0x0000000000000000000000000000000000000000000000000000000000000012'),

      callRpc<string>('eth_call', [{
        to: normalizedAddress,
        data: totalSupplySignature
      }, 'latest']).catch(() => '0x0')
    ]);
    
    let decimals = 18;
    if (decimalsResult && decimalsResult !== '0x') {
      decimals = parseHexToDecimal(decimalsResult);
    }
    
    let name = 'Unknown Token';
    let symbol = 'UNKNOWN';
    
    if (nameResult && nameResult !== '0x') {
      try {
        const nameHex = nameResult.slice(2);
        const nameLength = parseHexToDecimal('0x' + nameHex.slice(64, 128));
        const nameData = nameHex.slice(128, 128 + nameLength * 2);
        name = Buffer.from(nameData, 'hex').toString('utf8');
      } catch (e) {
        console.error('Error parsing token name:', e);
      }
    }
    
    if (symbolResult && symbolResult !== '0x') {
      try {
        const symbolHex = symbolResult.slice(2);
        const symbolLength = parseHexToDecimal('0x' + symbolHex.slice(64, 128));
        const symbolData = symbolHex.slice(128, 128 + symbolLength * 2);
        symbol = Buffer.from(symbolData, 'hex').toString('utf8');
      } catch (e) {
        console.error('Error parsing token symbol:', e);
      }
    }
    
    let totalSupply = '0';
    let formattedTotalSupply = '0';
    if (totalSupplyResult && totalSupplyResult !== '0x') {
      try {
        const rawSupply = parseHexToDecimal(totalSupplyResult);
        totalSupply = rawSupply.toString();
        
        formattedTotalSupply = formatTokenAmount(totalSupply, decimals, '');
      } catch (e) {
        console.error('Error parsing total supply:', e);
      }
    }
    
    let creator = null;
    let creationTx = null;
    let creationTimestamp = null;
    
    try {
      const tokenFromExplorer = await callExplorerApi<any>(`tokens/${normalizedAddress}`);
      
      if (tokenFromExplorer && tokenFromExplorer.data) {
        const tokenData = tokenFromExplorer.data;
        
        creator = tokenData.creator_address || null;
        creationTx = tokenData.creation_transaction || null;
        
        if (tokenData.creation_timestamp) {
          creationTimestamp = standardizeTimestamp(tokenData.creation_timestamp);
        } else {
          creationTimestamp = null;
        }
        
        if (tokenData.name && name === 'Unknown Token') {
          name = tokenData.name;
        }
        
        if (tokenData.symbol && symbol === 'UNKNOWN') {
          symbol = tokenData.symbol;
        }
        
        logger.debug(`Got token info from Explorer DB for ${normalizedAddress}`);
      }
    } catch (e: any) {
      console.warn(`Could not fetch token info from Explorer DB: ${e.message}`);
    }
    
    const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
    
    const latestBlockHex = await callRpc<string>('eth_blockNumber');
    
    const blockRange = 10000;
    const fromBlock = Math.max(0, parseHexToDecimal(latestBlockHex) - blockRange);
    const fromBlockHex = formatDecimalToHex(fromBlock);
    
    const logs = await callRpc<any[]>('eth_getLogs', [{
      address: normalizedAddress,
      fromBlock: fromBlockHex,
      toBlock: 'latest',
      topics: [transferEventSignature]
    }]);
    
    const addresses = new Set<string>();
    let transfersCount = logs.length;
    
    logs.forEach(log => {
      if (log.topics && log.topics.length >= 3) {
        const fromAddress = '0x' + log.topics[1].slice(26);
        const toAddress = '0x' + log.topics[2].slice(26);
        
        if (fromAddress !== '0x0000000000000000000000000000000000000000') {
          addresses.add(fromAddress);
        }
        
        if (toAddress !== '0x0000000000000000000000000000000000000000') {
          addresses.add(toAddress);
        }
      }
    });
    
    const holdersCount = addresses.size;
    

    
    const excludedAddresses = [
      '0x0000000000000000000000000000000000000000', 
      normalizedAddress.toLowerCase(),

    ];
    

    let lockedSupply = BigInt(0);
    const balanceOfSignature = '0x70a08231';
    
    try {

      const balancePromises = excludedAddresses.map(async (address) => {

        if (address === '0x0000000000000000000000000000000000000000') {

        }
        
        const normalizedExcludedAddress = address.startsWith('0x') ? address.toLowerCase() : `0x${address}`.toLowerCase();
        
        const paddedAddress = '000000000000000000000000' + normalizedExcludedAddress.slice(2);
        
        try {
          const balanceHex = await callRpc<string>('eth_call', [{
            to: normalizedAddress,
            data: balanceOfSignature + paddedAddress
          }, 'latest']);
          
          return BigInt(balanceHex);
        } catch (e) {
          console.warn(`Error getting balance for excluded address ${normalizedExcludedAddress}:`, e);
          return BigInt(0);
        }
      });
      
      const excludedBalances = await Promise.all(balancePromises);
      lockedSupply = excludedBalances.reduce((sum, balance) => sum + balance, BigInt(0));
      
      logger.debug(`Calculated locked supply: ${lockedSupply.toString()}`);
    } catch (e) {
      console.warn('Error calculating locked supply:', e);
      logger.debug('Falling back to estimated circulating supply');
    }
    
    let totalSupplyBigInt: bigint;
    try {
      if (totalSupply.includes('e+')) {
        const parts = totalSupply.split('e+');
        const base = parts[0];
        const exponent = parseInt(parts[1]);
        
        let fullNumberStr = base.replace('.', '');
        const decimalIndex = base.indexOf('.');
        const zerosToAdd = exponent - (base.length - decimalIndex - 1);
        fullNumberStr += '0'.repeat(Math.max(0, zerosToAdd));
        
        totalSupplyBigInt = BigInt(fullNumberStr);
      } else {
        totalSupplyBigInt = BigInt(totalSupply);
      }
    } catch (error) {
      console.error('Error converting totalSupply to BigInt:', error);
      totalSupplyBigInt = BigInt(totalSupplyResult);
    }
    
    let circulatingSupply: number;
    let circulatingSupplyPct: number;
    
    if (lockedSupply > BigInt(0) && lockedSupply < totalSupplyBigInt) {
      const circulatingSupplyBigInt = totalSupplyBigInt - lockedSupply;
      circulatingSupply = Number(circulatingSupplyBigInt);
      circulatingSupplyPct = Number(circulatingSupplyBigInt * BigInt(100) / totalSupplyBigInt);
      logger.debug(`Calculated circulating supply: ${circulatingSupplyBigInt.toString()} (${circulatingSupplyPct.toFixed(2)}% of total)`);
    } else {
      circulatingSupply = Math.round(Number(totalSupply) * 0.7);
      circulatingSupplyPct = 70;
      logger.debug(`Using estimated circulating supply: ${circulatingSupply} (70% of total)`);
    }
    
    const formattedCirculatingSupply = formatTokenAmount(circulatingSupply, decimals, '');
    
    return {
      address: normalizedAddress,
      name,
      symbol,
      decimals,
      totalSupply,
      formattedTotalSupply: `${formattedTotalSupply} ${symbol}`,
      circulatingSupply,
      formattedCirculatingSupply: `${formattedCirculatingSupply} ${symbol}`,
      contractType: 'ERC-20',
      creator,
      creationTx,
      creationTimestamp,
      price: null,
      marketCap: null,
      volume24h: null,
      holdersCount,
      transfersCount
    };
  } catch (error) {
    console.error('Error fetching token info:', error);
    throw error;
  }
}

/**
 * 
 * @param {string} tokenAddress 
 * @param {number} limit 
 * @param {number} [page] 
 * @returns {Promise<TokenTransfer[]>} 
 * 
 * @deprecated 
 */
