/**
 * Contract Event Listeners - Utilities for listening to contract events
 */

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

export interface ContractEvent {
  address: Address
  eventName: string
  args: any
  blockNumber: bigint
  transactionHash: `0x${string}`
  logIndex: number
  timestamp: number
}

export type EventCallback = (event: ContractEvent) => void

/**
 * Subscribe to contract events
 */
export async function subscribeToContractEvents(
  publicClient: any,
  contractAddress: Address,
  abi: Abi,
  eventName: string,
  callback: EventCallback,
  fromBlock?: bigint
): Promise<() => void> {
  let isSubscribed = true
  let lastBlock = fromBlock || (await publicClient.getBlockNumber()) - 100n

  const pollEvents = async () => {
    if (!isSubscribed) return

    try {
      const currentBlock = await publicClient.getBlockNumber()
      
      // Get events from last block to current
      const events = await publicClient.getLogs({
        address: contractAddress,
        event: {
          type: 'event',
          name: eventName,
        } as any,
        fromBlock: lastBlock,
        toBlock: currentBlock,
      })

      for (const log of events) {
        try {
          const { decodeEventLog } = await import('viem')
          const decoded = decodeEventLog({
            abi,
            data: log.data,
            topics: log.topics,
          })

          callback({
            address: contractAddress,
            eventName,
            args: decoded.args,
            blockNumber: log.blockNumber,
            transactionHash: log.transactionHash,
            logIndex: log.logIndex,
            timestamp: Date.now(),
          })
        } catch (error) {
          console.error('Error decoding event:', error)
        }
      }

      lastBlock = currentBlock
    } catch (error) {
      console.error('Error polling events:', error)
    }
  }

  // Poll every 5 seconds
  const interval = setInterval(pollEvents, 5000)
  await pollEvents() // Initial poll

  return () => {
    isSubscribed = false
    clearInterval(interval)
  }
}

/**
 * Listen to specific event once
 */
export async function waitForEvent(
  publicClient: any,
  contractAddress: Address,
  abi: Abi,
  eventName: string,
  timeout = 60000
): Promise<ContractEvent> {
  return new Promise((resolve, reject) => {
    let unsubscribe: (() => void) | null = null
    const timeoutId = setTimeout(() => {
      if (unsubscribe) unsubscribe()
      reject(new Error('Event timeout'))
    }, timeout)

    subscribeToContractEvents(publicClient, contractAddress, abi, eventName, (event) => {
      if (unsubscribe) unsubscribe()
      clearTimeout(timeoutId)
      resolve(event)
    }).then((unsub) => {
      unsubscribe = unsub
    })
  })
}

/**
 * Get recent events for a contract
 */
export async function getRecentEvents(
  publicClient: any,
  contractAddress: Address,
  abi: Abi,
  eventName: string,
  blockRange = 1000
): Promise<ContractEvent[]> {
  try {
    const currentBlock = await publicClient.getBlockNumber()
    const fromBlock = currentBlock - BigInt(blockRange)

    const logs = await publicClient.getLogs({
      address: contractAddress,
      event: {
        type: 'event',
        name: eventName,
      } as any,
      fromBlock,
      toBlock: currentBlock,
    })

    const events: ContractEvent[] = []

    for (const log of logs) {
      try {
        const { decodeEventLog } = await import('viem')
        const decoded = decodeEventLog({
          abi,
          data: log.data,
          topics: log.topics,
        })

        events.push({
          address: contractAddress,
          eventName,
          args: decoded.args,
          blockNumber: log.blockNumber,
          transactionHash: log.transactionHash,
          logIndex: log.logIndex,
          timestamp: Date.now(),
        })
      } catch (error) {
        console.error('Error decoding event:', error)
      }
    }

    return events
  } catch (error) {
    console.error('Error getting recent events:', error)
    return []
  }
}
