import { useState, useCallback, useEffect } from 'react'
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { parseUnits } from 'viem'
import { abis } from '@/lib/contracts'
import { Token } from '@/lib/api/tokens'
import { useTokenList } from './useTokenList'
import { handleContractCatchError, isValidAmount, parseContractError } from '@/lib/errors'
import { DEFAULT_GAS_LIMIT } from '@/lib/constants'

type DepositStatus = 'idle' | 'approving' | 'approved' | 'depositing' | 'success' | 'error'

export function useDeposit(ytToken: Token | undefined) {
  const { address, chainId } = useAccount()
  const { bySymbol } = useTokenList()
  const usdcToken = bySymbol['USDC']
  const [status, setStatus] = useState<DepositStatus>('idle')
  const [error, setError] = useState<string | null>(null)
  const [pendingAmount, setPendingAmount] = useState<string>('')

  const {
    writeContractAsync: approveWrite,
    data: approveHash,
    isPending: isApprovePending,
    reset: resetApprove,
  } = useWriteContract()

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

  const {
    writeContractAsync: depositWrite,
    data: depositHash,
    isPending: isDepositPending,
    reset: resetDeposit,
  } = useWriteContract()

  const {
    isLoading: isDepositConfirming,
    isSuccess: isDepositSuccess,
    isError: isDepositError,
    error: depositReceiptError,
  } = useWaitForTransactionReceipt({ hash: depositHash })

  const executeApprove = useCallback(async (amount: string) => {
    if (status !== 'idle') return false
    if (!address || !ytToken?.contractAddress || !usdcToken?.contractAddress || !amount) {
      setError('Missing required parameters')
      return false
    }
    if (!isValidAmount(amount)) {
      setError('Invalid amount')
      return false
    }

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

      const usdcDecimals = usdcToken.onChainDecimals ?? usdcToken.decimals
      const amountInWei = parseUnits(amount, usdcDecimals)

      await approveWrite({
        address: usdcToken.contractAddress as `0x${string}`,
        abi: abis.USDY,
        functionName: 'approve',
        args: [ytToken.contractAddress as `0x${string}`, amountInWei],
      })

      return true
    } catch (err: unknown) {
      handleContractCatchError(err, 'Approve failed', setError, setStatus as (s: string) => void)
      return false
    }
  }, [address, approveWrite, ytToken, usdcToken, status])

  const executeDeposit = useCallback(async (amount: string) => {
    if (status === 'depositing' || status === 'success' || status === 'error') return false
    if (!address || !ytToken?.contractAddress || !usdcToken || !amount) {
      setError('Missing required parameters')
      return false
    }
    if (!isValidAmount(amount)) {
      setError('Invalid amount')
      return false
    }

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

      const usdcDecimals = usdcToken.onChainDecimals ?? usdcToken.decimals
      const amountInWei = parseUnits(amount, usdcDecimals)

      await depositWrite({
        address: ytToken.contractAddress as `0x${string}`,
        abi: abis.YTToken,
        functionName: 'depositYT',
        args: [amountInWei],
        gas: DEFAULT_GAS_LIMIT,
      })

      return true
    } catch (err: unknown) {
      handleContractCatchError(err, 'Deposit failed', setError, setStatus as (s: string) => void)
      return false
    }
  }, [address, depositWrite, ytToken, usdcToken, status])

  const executeApproveAndDeposit = useCallback(async (amount: string) => {
    if (status !== 'idle') return
    setPendingAmount(amount)
    const approveSuccess = await executeApprove(amount)
    if (!approveSuccess) return
  }, [executeApprove, status])

  // Auto-execute deposit when approve is successful
  useEffect(() => {
    if (isApproveSuccess && status === 'approving' && pendingAmount) {
      setStatus('approved')
      executeDeposit(pendingAmount)
    }
  }, [isApproveSuccess, status, pendingAmount, executeDeposit])

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

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

  useEffect(() => {
    if (isDepositError && status === 'depositing') {
      setError(parseContractError(depositReceiptError))
      setStatus('error')
    }
  }, [isDepositError, status, depositReceiptError])

  const reset = useCallback(() => {
    setStatus('idle')
    setError(null)
    setPendingAmount('')
    resetApprove()
    resetDeposit()
  }, [resetApprove, resetDeposit])

  return {
    status,
    error,
    isLoading: isApprovePending || isApproveConfirming || isDepositPending || isDepositConfirming,
    approveHash,
    depositHash,
    executeApprove,
    executeDeposit,
    executeApproveAndDeposit,
    reset,
  }
}
