/**
 * Contract Operations
 */

import { callRpc } from '../../core/rpc';
import { callExplorerApi } from '../../core/explorerApi';
import { getTransactionReceipt } from '../transactions/transactions';
import { getBlockByNumber, getLatestBlockNumber } from '../blocks/blocks';
import {  ContractInfo } from '../../types'; 
import { 
  parseHexToDecimal,
} from '../../utils/formatters';
import { 
  registerSystemContract,
} from '../../utils/registry';

import {
  isSystemContract,
  checkIsContract,
  getContractBytecode,
  isTransactionObject
} from './contract-utils';

import { logger } from '../../utils/logger';

const initializeSystemContracts = () => {
  const systemContracts = [
    { address: '0x00000000000000000000000000000000000a4b05', name: 'Core System Contract' }
  ];
  
  systemContracts.forEach(contract => {
    registerSystemContract(contract.address, contract.name);
  });
};


initializeSystemContracts();

/**

 * @param {string} address 
 * @returns {Promise<ContractInfo>} 
 */
export async function getContractInfo(address: string): Promise<ContractInfo> {
  try {
    const isContractAddress = await checkIsContract(address);
    
    if (!isContractAddress) {
      throw new Error(`Address ${address} is not a contract`);
    }
    
    const bytecode = await getContractBytecode(address);
    
    const balance = await callRpc<string>('eth_getBalance', [address, 'latest']);
    
    if (isSystemContract(address.toLowerCase())) {
      return {
        address,
        bytecode,
        creator: "System Contract",
        creationTx: null,
        creationTimestamp: null,
        balance,
        isVerified: false
      };
    }
    
    let creator = null;
    let creationTx = null;
    let creationTimestamp = null;
    let isVerified = false;
    
    try {
      const contractFromExplorer = await callExplorerApi<any>(`contracts/${address}`);
      
      if (contractFromExplorer && contractFromExplorer.data) {
        const contractData = contractFromExplorer.data;
        
        creator = contractData.creator_address || null;
        creationTx = contractData.creation_transaction || null;
        creationTimestamp = contractData.creation_timestamp || null;
        isVerified = contractData.is_verified || false;
        
        logger.debug(`Got contract info from Explorer DB for ${address}`);
      }
    } catch (e: any) {
      logger.warn(`Could not fetch contract info from Explorer DB: ${e.message}`);
    }
    
    return {
      address,
      bytecode,
      creator,
      creationTx,
      creationTimestamp,
      balance,
      isVerified
    };
  } catch (error) {
    logger.error('Error getting contract info:', error);
    throw error;
  }
}

/**
 * 
 * @param {string} address 
 * @returns {Promise<boolean>} 
 */
export async function isContractVerified(address: string): Promise<boolean> {
  try {
    const isContractAddress = await checkIsContract(address);
    
    if (!isContractAddress) {
      return false; 
    }
    
    try {
      const contractFromExplorer = await callExplorerApi<any>(`contracts/${address}`);
      
      if (contractFromExplorer && contractFromExplorer.data) {
        return contractFromExplorer.data.is_verified || false;
      }
    } catch (e) {
      logger.warn(`Could not fetch verification status from Explorer DB: ${e}`);
    }
    
    return false;
  } catch (error) {
    logger.error('Error checking contract verification:', error);
    return false;
  }
}

/**
 * 
 * @param {string} address 
 * @returns {Promise<object | null>} 
 */
export async function getContractAbi(address: string): Promise<object | null> {
  try {
    const isVerified = await isContractVerified(address);
    
    if (!isVerified) {
      return null; 
    }
    
    try {
      const abiFromExplorer = await callExplorerApi<any>(`contracts/${address}/abi`);
      
      if (abiFromExplorer && abiFromExplorer.data && abiFromExplorer.data.abi) {
        return JSON.parse(abiFromExplorer.data.abi);
      }
    } catch (e) {
      logger.warn(`Could not fetch ABI from Explorer DB: ${e}`);
    }
    
    return null;
  } catch (error) {
    logger.error('Error getting contract ABI:', error);
    return null;
  }
}

/**
 * 
 * @param {number} maxResults 
 * @param {number} maxBlocks 
 * @returns {Promise<string[]>} 
 */
export async function findValidContracts(maxResults: number = 5, maxBlocks: number = 10): Promise<string[]> {
  try {
    const latestBlockHex = await getLatestBlockNumber();
    const latestBlock = parseHexToDecimal(latestBlockHex);
    
    const contractAddresses: string[] = [];
    const processedAddresses = new Set<string>();
    
    for (let i = 0; i < maxBlocks; i++) {
      if (contractAddresses.length >= maxResults) {
        break;
      }
      
      const blockNumber = latestBlock - i;
      if (blockNumber < 0) break;
      
      try {
        const block = await getBlockByNumber(blockNumber, true);
        
        if (block && block.transactions && Array.isArray(block.transactions)) {
          const contractCreations = block.transactions
            .filter(tx => isTransactionObject(tx) && !tx.to);
          
          for (const tx of contractCreations) {
            if (isTransactionObject(tx)) {
              try {
                const receipt = await getTransactionReceipt(tx.hash);
                
                if (receipt && receipt.contractAddress && !processedAddresses.has(receipt.contractAddress)) {
                  const isContract = await checkIsContract(receipt.contractAddress);
                  
                  if (isContract) {
                    contractAddresses.push(receipt.contractAddress);
                    processedAddresses.add(receipt.contractAddress);
                    
                    if (contractAddresses.length >= maxResults) {
                      break;
                    }
                  }
                }
              } catch (e) {
                logger.warn(`Error processing transaction ${tx.hash}:`, e);
              }
            }
          }
          
          if (contractAddresses.length < maxResults) {
            const contractInteractions = block.transactions
              .filter(tx => isTransactionObject(tx) && tx.to);
            
            for (const tx of contractInteractions) {
              if (isTransactionObject(tx) && tx.to) {
                if (processedAddresses.has(tx.to)) {
                  continue;
                }
                
                try {
                  const isContract = await checkIsContract(tx.to);
                  
                  if (isContract) {
                    contractAddresses.push(tx.to);
                    processedAddresses.add(tx.to);
                    
                    if (contractAddresses.length >= maxResults) {
                      break;
                    }
                  }
                } catch (e) {
                  logger.warn(`Error checking if address is contract ${tx.to}:`, e);
                }
              }
            }
          }
        }
      } catch (e) {
        logger.warn(`Error processing block ${blockNumber}:`, e);
        continue; 
      }
    }
    
    return contractAddresses;
  } catch (error) {
    logger.error('Error finding valid contracts:', error);
    return [];
  }
} 