/**
 * Transaction Simulator - Enhanced transaction simulation utilities
 */

import type { Address } from 'viem'
import type { Abi } from 'viem'

export interface SimulationResult {
  success: boolean
  gasEstimate: bigint
  returnValue?: any
  error?: string
  traces?: any[]
  logs?: any[]
}

export interface SimulatedCall {
  to: Address
  value: bigint
  data: `0x${string}`
  gas?: bigint
  gasPrice?: bigint
}

/**
 * Simulate a contract call
 */
export async function simulateCall(
  publicClient: any,
  call: SimulatedCall
): Promise<SimulationResult> {
  try {
    // Use publicClient.call for simulation
    const result = await publicClient.call({
      ...call,
      account: call.to, // Use target as account for simulation
    })

    return {
      success: true,
      gasEstimate: result.gasUsed || 21000n,
      returnValue: result.data,
      logs: result.logs,
    }
  } catch (error: any) {
    return {
      success: false,
      gasEstimate: 0n,
      error: error.message || 'Simulation failed',
    }
  }
}

/**
 * Simulate contract function call
 */
export async function simulateFunctionCall(
  publicClient: any,
  contractAddress: Address,
  abi: Abi,
  functionName: string,
  args: unknown[],
  from?: Address
): Promise<SimulationResult> {
  try {
    // Encode function call
    const { encodeFunctionData } = await import('viem')
    const data = encodeFunctionData({
      abi,
      functionName: functionName as any,
      args,
    })

    // Estimate gas
    const gasEstimate = await publicClient.estimateGas({
      to: contractAddress,
      data,
      account: from || contractAddress,
    })

    // Simulate call
    const result = await publicClient.call({
      to: contractAddress,
      data,
      account: from || contractAddress,
    })

    // Decode return value if possible
    let returnValue: any = null
    try {
      const { decodeFunctionResult } = await import('viem')
      const functionAbi = abi.find(
        (item: any) => item.type === 'function' && item.name === functionName
      ) as any
      if (functionAbi && functionAbi.outputs && result.data && result.data !== '0x') {
        returnValue = decodeFunctionResult({
          abi,
          functionName: functionName as any,
          data: result.data,
        })
      }
    } catch {
      // Decoding failed, keep raw data
      returnValue = result.data
    }

    return {
      success: true,
      gasEstimate,
      returnValue,
      logs: result.logs,
    }
  } catch (error: any) {
    // Try to extract revert reason
    let errorMessage = error.message || 'Simulation failed'
    if (error.data || error.cause?.data) {
      try {
        const { decodeErrorResult } = await import('viem')
        const errorData = error.data || error.cause?.data
        const decoded = decodeErrorResult({
          abi,
          data: errorData,
        })
        errorMessage = decoded.errorName || errorMessage
      } catch {
        // Decoding failed
      }
    }

    return {
      success: false,
      gasEstimate: 0n,
      error: errorMessage,
    }
  }
}

/**
 * Simulate batch of calls
 */
export async function simulateBatch(
  publicClient: any,
  calls: SimulatedCall[]
): Promise<SimulationResult[]> {
  const results: SimulationResult[] = []

  for (const call of calls) {
    const result = await simulateCall(publicClient, call)
    results.push(result)
    
    // If one fails, we might want to stop or continue
    // For now, continue with all calls
  }

  return results
}

/**
 * Estimate gas cost in ETH
 */
export function estimateGasCost(
  gasEstimate: bigint,
  gasPrice: bigint
): string {
  const cost = gasEstimate * gasPrice
  const { formatEther } = require('viem')
  return formatEther(cost)
}

/**
 * Get simulation status emoji
 */
export function getSimulationStatusEmoji(result: SimulationResult): string {
  if (result.success) return '✅'
  if (result.error?.includes('revert')) return '❌'
  if (result.error?.includes('gas')) return '⛽'
  return '⚠️'
}
