import { useAccount, useReadContract, useReadContracts } from 'wagmi'
import { formatUnits } from 'viem'
import { abis, getContractAddress } from '@/lib/contracts'
import { Token } from '@/lib/api/tokens'
import { useTokenList } from './useTokenList'

/**
 * Hook to query user's collateral balance for a specific YT token
 */
export function useCollateralBalance(token: Token | undefined) {
  const { address, chainId } = useAccount()

  const lendingProxyAddress = chainId ? getContractAddress('lendingProxy', chainId) : undefined

  const { data: collateralBalance, refetch, isLoading } = useReadContract({
    address: lendingProxyAddress,
    abi: abis.lendingProxy,
    functionName: 'getCollateral',
    args: address && token?.contractAddress ? [address, token.contractAddress as `0x${string}`] : undefined,
    query: {
      enabled: !!address && !!token?.contractAddress && !!lendingProxyAddress,
    },
  })

  const decimals = token?.onChainDecimals ?? token?.decimals ?? 18
  const formattedBalance = collateralBalance
    ? formatUnits(collateralBalance as bigint, decimals)
    : '0'

  return {
    balance: collateralBalance as bigint | undefined,
    formattedBalance,
    isLoading,
    refetch,
  }
}

/**
 * Hook to query user's borrow balance (in USDC)
 */
export function useBorrowBalance() {
  const { address, chainId } = useAccount()
  const { bySymbol } = useTokenList()
  const usdcToken = bySymbol['USDC']

  const lendingProxyAddress = chainId ? getContractAddress('lendingProxy', chainId) : undefined

  const { data: borrowBalance, refetch, isLoading } = useReadContract({
    address: lendingProxyAddress,
    abi: abis.lendingProxy,
    functionName: 'borrowBalanceOf',
    args: address ? [address] : undefined,
    query: {
      enabled: !!address && !!chainId && !!lendingProxyAddress,
    },
  })

  const usdcDecimals = usdcToken?.onChainDecimals ?? usdcToken?.decimals ?? 18
  const formattedBalance = borrowBalance
    ? formatUnits(borrowBalance as bigint, usdcDecimals)
    : '0.00'

  return {
    balance: borrowBalance as bigint | undefined,
    formattedBalance,
    isLoading,
    refetch,
  }
}

/**
 * Hook to query user's balance (positive = supply, negative = borrow)
 */
export function useAccountBalance() {
  const { address, chainId } = useAccount()

  const lendingProxyAddress = chainId ? getContractAddress('lendingProxy', chainId) : undefined

  const { data: supplyBalance, refetch: refetchSupply, isLoading: isLoadingSupply } = useReadContract({
    address: lendingProxyAddress,
    abi: abis.lendingProxy,
    functionName: 'supplyBalanceOf',
    args: address ? [address] : undefined,
    query: {
      enabled: !!address && !!chainId && !!lendingProxyAddress,
    },
  })

  const { data: borrowBalance, refetch: refetchBorrow, isLoading: isLoadingBorrow } = useReadContract({
    address: lendingProxyAddress,
    abi: abis.lendingProxy,
    functionName: 'borrowBalanceOf',
    args: address ? [address] : undefined,
    query: {
      enabled: !!address && !!chainId && !!lendingProxyAddress,
    },
  })

  const supplyValue = (supplyBalance as bigint) || 0n
  const borrowValue = (borrowBalance as bigint) || 0n
  const netBalance = supplyValue - borrowValue

  return {
    balance: netBalance,
    isSupply: supplyValue > 0n,
    isBorrow: borrowValue > 0n,
    isLoading: isLoadingSupply || isLoadingBorrow,
    refetch: () => {
      refetchSupply()
      refetchBorrow()
    },
  }
}

/**
 * Hook to calculate LTV (Loan-to-Value) ratio
 * LTV = (Borrow Balance / Collateral Value) * 100
 */
export function useLTV() {
  const { bySymbol } = useTokenList()
  const ytA = bySymbol['YT-A']
  const ytB = bySymbol['YT-B']
  const ytC = bySymbol['YT-C']

  const { formattedBalance: borrowBalance } = useBorrowBalance()
  const { value: valueA } = useCollateralValue(ytA)
  const { value: valueB } = useCollateralValue(ytB)
  const { value: valueC } = useCollateralValue(ytC)

  const totalCollateralValue = parseFloat(valueA) + parseFloat(valueB) + parseFloat(valueC)
  const borrowValue = parseFloat(borrowBalance)

  const ltv = totalCollateralValue > 0
    ? (borrowValue / totalCollateralValue) * 100
    : 0

  return {
    ltv: ltv.toFixed(2),
    ltvRaw: ltv,
    isLoading: false,
  }
}

/**
 * Hook to get YT token price from its contract
 */
export function useYTPrice(token: Token | undefined) {
  const { data: ytPrice, isLoading } = useReadContract({
    address: token?.contractAddress as `0x${string}` | undefined,
    abi: abis.YTToken,
    functionName: 'ytPrice',
    query: {
      enabled: !!token?.contractAddress,
    },
  })

  // ytPrice 返回 30 位精度的价格（PRICE_PRECISION = 1e30）
  const formattedPrice = ytPrice
    ? formatUnits(ytPrice as bigint, 30)
    : '0'

  return {
    price: ytPrice as bigint | undefined,
    formattedPrice,
    isLoading,
  }
}

/**
 * Hook to calculate collateral value in USD
 */
export function useCollateralValue(token: Token | undefined) {
  const { formattedBalance } = useCollateralBalance(token)
  const { formattedPrice } = useYTPrice(token)

  const value = parseFloat(formattedBalance) * parseFloat(formattedPrice)

  return {
    value: value.toFixed(2),
    valueRaw: value,
  }
}

type AssetConfig = {
  asset: `0x${string}`
  decimals: number
  borrowCollateralFactor: bigint
  liquidateCollateralFactor: bigint
  liquidationFactor: bigint
  supplyCap: bigint
}

/**
 * Hook to calculate total borrow capacity and available borrowable amount
 */
export function useMaxBorrowable() {
  const { chainId } = useAccount()
  const { bySymbol } = useTokenList()
  const ytA = bySymbol['YT-A']
  const ytB = bySymbol['YT-B']
  const ytC = bySymbol['YT-C']

  const lendingProxyAddress = chainId ? getContractAddress('lendingProxy', chainId) : undefined

  // Collateral balances
  const { formattedBalance: balA, refetch: refetchA } = useCollateralBalance(ytA)
  const { formattedBalance: balB, refetch: refetchB } = useCollateralBalance(ytB)
  const { formattedBalance: balC, refetch: refetchC } = useCollateralBalance(ytC)

  // YT token prices (ytPrice(), 30-decimal precision)
  const { formattedPrice: priceA } = useYTPrice(ytA)
  const { formattedPrice: priceB } = useYTPrice(ytB)
  const { formattedPrice: priceC } = useYTPrice(ytC)

  // Borrow collateral factors from lendingProxy.assetConfigs
  const ytAddresses = [ytA, ytB, ytC]
    .filter(t => !!t?.contractAddress)
    .map(t => t!.contractAddress as `0x${string}`)

  const configContracts = ytAddresses.map((addr) => ({
    address: lendingProxyAddress,
    abi: abis.lendingProxy,
    functionName: 'assetConfigs' as const,
    args: [addr],
  }))

  const { data: configData } = useReadContracts({
    contracts: configContracts as any,
    query: { enabled: !!lendingProxyAddress && configContracts.length > 0 },
  })

  type ConfigTuple = readonly [string, number, bigint, bigint, bigint, bigint]

  const getConfig = (i: number): ConfigTuple | undefined =>
    configData?.[i]?.result as ConfigTuple | undefined

  const calcCapacity = (bal: string, price: string, config: ConfigTuple | undefined) => {
    if (!config || !config[2]) return 0
    const factor = Number(config[2]) / 1e18
    return parseFloat(bal) * parseFloat(price) * factor
  }

  const capacityA = calcCapacity(balA, priceA, getConfig(0))
  const capacityB = calcCapacity(balB, priceB, getConfig(1))
  const capacityC = calcCapacity(balC, priceC, getConfig(2))

  const totalCapacity = capacityA + capacityB + capacityC

  const { formattedBalance: borrowedBalance, refetch: refetchBorrow } = useBorrowBalance()
  const currentBorrow = parseFloat(borrowedBalance) || 0
  const available = Math.max(0, totalCapacity - currentBorrow)

  const refetch = () => {
    refetchA()
    refetchB()
    refetchC()
    refetchBorrow()
  }

  return {
    totalCapacity,
    available,
    formattedCapacity: totalCapacity.toFixed(2),
    formattedAvailable: available.toFixed(2),
    refetch,
  }
}
