import { useState, useEffect } from 'react'
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { parseUnits } from 'viem'
import { abis, getContractAddress } from '@/lib/contracts'
import { handleContractCatchError, isValidAmount, parseContractError } from '@/lib/errors'
import { SWAP_GAS_LIMIT } from '@/lib/constants'

type SwapStatus = 'idle' | 'approving' | 'approved' | 'swapping' | 'success' | 'error'

/**
 * Hook for swapping tokens via YTRewardRouter
 * Accepts contract addresses and decimals directly — no hardcoded token symbols.
 */
export function useSwap() {
  const { address, chainId } = useAccount()
  const [status, setStatus] = useState<SwapStatus>('idle')
  const [error, setError] = useState<string | null>(null)
  const [approveHash, setApproveHash] = useState<`0x${string}` | undefined>()
  const [swapHash, setSwapHash] = useState<`0x${string}` | undefined>()

  const { writeContractAsync: approveWrite } = useWriteContract()
  const { writeContractAsync: swapWrite } = useWriteContract()

  const { isLoading: isApproving, isSuccess: isApproveSuccess, isError: isApproveError, error: approveReceiptError } = useWaitForTransactionReceipt({
    hash: approveHash,
  })

  const { isLoading: isSwapping, isSuccess: isSwapSuccess, isError: isSwapError, error: swapReceiptError } = useWaitForTransactionReceipt({
    hash: swapHash,
  })

  const executeApprove = async (tokenInAddress: string, tokenInDecimals: number, amount: string) => {
    if (status !== 'idle') return false
    if (!chainId || !address) {
      setError('Please connect your wallet')
      return false
    }
    if (!isValidAmount(amount)) {
      setError('Invalid amount')
      return false
    }

    try {
      setStatus('approving')
      setError(null)

      const rewardRouterAddress = getContractAddress('YTRewardRouter', chainId)

      if (!tokenInAddress || !rewardRouterAddress) {
        throw new Error('Contract not deployed on this chain')
      }

      const amountInWei = parseUnits(amount, tokenInDecimals)

      const hash = await approveWrite({
        address: tokenInAddress as `0x${string}`,
        abi: abis.USDY,
        functionName: 'approve',
        args: [rewardRouterAddress, amountInWei],
      })

      setApproveHash(hash)
      return true
    } catch (err: unknown) {
      handleContractCatchError(err, 'Approval failed', setError, setStatus as (s: string) => void)
      return false
    }
  }

  const executeSwap = async (
    tokenInAddress: string,
    tokenInDecimals: number,
    tokenOutAddress: string,
    tokenOutDecimals: number,
    amount: string,
    minOut: string = '0'
  ) => {
    if (status === 'swapping' || status === 'success' || status === 'error') return false
    if (!chainId || !address) {
      setError('Please connect your wallet')
      return false
    }
    if (!isValidAmount(amount)) {
      setError('Invalid amount')
      return false
    }

    try {
      setStatus('swapping')
      setError(null)

      const rewardRouterAddress = getContractAddress('YTRewardRouter', chainId)

      if (!tokenInAddress || !tokenOutAddress || !rewardRouterAddress) {
        throw new Error('Contract not deployed on this chain')
      }

      const amountInWei = parseUnits(amount, tokenInDecimals)
      const minOutWei = parseUnits(minOut, tokenOutDecimals)

      const hash = await swapWrite({
        address: rewardRouterAddress,
        abi: abis.YTRewardRouter,
        functionName: 'swapYT',
        args: [tokenInAddress as `0x${string}`, tokenOutAddress as `0x${string}`, amountInWei, minOutWei, address],
        gas: SWAP_GAS_LIMIT,
        type: 'legacy',
      })

      setSwapHash(hash)
      return true
    } catch (err: unknown) {
      handleContractCatchError(err, 'Swap failed', setError, setStatus as (s: string) => void)
      return false
    }
  }

  const executeApproveAndSwap = async (
    tokenInAddress: string,
    tokenInDecimals: number,
    tokenOutAddress: string,
    tokenOutDecimals: number,
    amount: string,
    minOut: string = '0'
  ) => {
    if (status !== 'idle') return
    const approved = await executeApprove(tokenInAddress, tokenInDecimals, amount)
    if (!approved) return

    await new Promise(resolve => setTimeout(resolve, 2000))

    await executeSwap(tokenInAddress, tokenInDecimals, tokenOutAddress, tokenOutDecimals, amount, minOut)
  }

  const reset = () => {
    setStatus('idle')
    setError(null)
    setApproveHash(undefined)
    setSwapHash(undefined)
  }

  useEffect(() => {
    if (isApproveSuccess && status === 'approving') {
      setStatus('approved')
    }
  }, [isApproveSuccess, status])

  useEffect(() => {
    if (isApproveError && status === 'approving') {
      setError(parseContractError(approveReceiptError))
      setStatus('error')
    }
  }, [isApproveError, status, approveReceiptError])

  useEffect(() => {
    if (isSwapSuccess && status === 'swapping') {
      setStatus('success')
    }
  }, [isSwapSuccess, status])

  useEffect(() => {
    if (isSwapError && status === 'swapping') {
      setError(parseContractError(swapReceiptError))
      setStatus('error')
    }
  }, [isSwapError, status, swapReceiptError])

  return {
    status,
    error,
    isLoading: isApproving || isSwapping,
    approveHash,
    swapHash,
    executeApprove,
    executeSwap,
    executeApproveAndSwap,
    reset,
  }
}
