import BigNumber from 'bignumber.js'
import React, { useCallback, useMemo } from 'react'

import Button from 'components/common/Button'
import Divider from 'components/common/Divider'
import SummaryItems from 'components/common/SummaryItems'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useDepositActions from 'hooks/HLS/useDepositActions'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices'
import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountLeverage } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { getCoinAmount, getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import {
  getDepositCapMessage,
  getHealthFactorMessage,
  getLiquidityMessage,
  getNoBalanceInWalletMessage,
} from 'utils/messages'

interface Props {
  account: HLSAccountWithStrategy
  action: HlsStakingManageAction
  borrowAsset: BorrowAsset
  collateralAsset: Asset
}

export default function Deposit(props: Props) {
  const { addedDeposits, addedDebts, updatedAccount, addedTrades, simulateHlsStakingDeposit } =
    useUpdatedAccount(props.account)
  const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)

  const assets = useAllAssets()
  const { data: prices } = usePrices()
  const [keepLeverage, toggleKeepLeverage] = useToggle(true)
  const collateralAssetAmountInWallet = BN(
    useCurrentWalletBalance(props.collateralAsset.denom)?.amount || '0',
  )
  const addToStakingStrategy = useStore((s) => s.addToStakingStrategy)
  const borrowRate = useBorrowAsset(props.borrowAsset.denom)?.borrowRate || 0

  const currentLeverage = useMemo(
    () => calculateAccountLeverage(props.account, prices, assets).toNumber(),
    [prices, props.account, assets],
  )

  const depositCoin = useMemo(
    () =>
      BNCoin.fromDenomAndBigNumber(
        props.collateralAsset.denom,
        addedDeposits.find(byDenom(props.collateralAsset.denom))?.amount || BN_ZERO,
      ),
    [addedDeposits, props.collateralAsset.denom],
  )

  const addedDepositFromSwap = useMemo(
    () => addedTrades.find(byDenom(props.collateralAsset.denom))?.amount || BN_ZERO,
    [addedTrades, props.collateralAsset.denom],
  )

  const borrowCoin = useMemo(
    () =>
      BNCoin.fromDenomAndBigNumber(
        props.borrowAsset.denom,
        addedDebts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
      ),
    [addedDebts, props.borrowAsset.denom],
  )

  const warningMessages = useMemo(() => {
    let messages: string[] = []
    const capLeft = props.account.strategy.depositCap.max.minus(
      props.account.strategy.depositCap.used,
    )

    if (capLeft.isLessThan(depositCoin.amount.plus(addedDepositFromSwap))) {
      messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit', assets))
    }

    if (collateralAssetAmountInWallet.isZero()) {
      messages.push(getNoBalanceInWalletMessage(props.collateralAsset.symbol))
    }

    return messages
  }, [
    addedDepositFromSwap,
    collateralAssetAmountInWallet,
    depositCoin.amount,
    props.account.strategy.depositCap.max,
    props.account.strategy.depositCap.used,
    props.collateralAsset.denom,
    props.collateralAsset.symbol,
    assets,
  ])

  const maxBorrowAmount = useMemo(
    () => computeMaxBorrowAmount(props.borrowAsset.denom, 'deposit'),
    [computeMaxBorrowAmount, props.borrowAsset.denom],
  )

  const borrowWarningMessages = useMemo(() => {
    let messages: string[] = []
    if (borrowCoin.amount.isGreaterThan(maxBorrowAmount)) {
      messages.push(
        getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow', assets),
      )
    }

    const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO

    if (borrowCoin.amount.isGreaterThan(borrowLiquidity)) {
      messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
    }

    return messages
  }, [
    assets,
    borrowCoin.amount,
    maxBorrowAmount,
    props.borrowAsset.denom,
    props.borrowAsset.liquidity?.amount,
  ])

  const actions = useDepositActions({ depositCoin, borrowCoin })

  const currentDebt: BigNumber = useMemo(
    () => props.account.debts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
    [props.account.debts, props.borrowAsset.denom],
  )

  const handleDeposit = useCallback(() => {
    useStore.setState({ hlsManageModal: null })
    addToStakingStrategy({
      accountId: props.account.id,
      actions,
      depositCoin,
      borrowCoin,
    })
  }, [actions, addToStakingStrategy, borrowCoin, depositCoin, props.account.id])

  const handleOnChange = useCallback(
    (amount: BigNumber) => {
      let additionalDebt = BN_ZERO

      if (currentLeverage > 1 && keepLeverage) {
        const depositValue = getCoinValue(
          BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
          prices,
          assets,
        )
        const borrowValue = BN(currentLeverage - 1).times(depositValue)
        additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices, assets)
      }

      simulateHlsStakingDeposit(
        BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
        BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, additionalDebt),
      )
    },
    [
      currentLeverage,
      keepLeverage,
      prices,
      props.borrowAsset.denom,
      props.collateralAsset.denom,
      simulateHlsStakingDeposit,
      assets,
    ],
  )

  const items: SummaryItem[] = useMemo(
    () => [
      ...(keepLeverage
        ? [
            {
              title: 'Borrow rate',
              amount: borrowRate,
              options: {
                suffix: `%`,
                minDecimals: 2,
                maxDecimals: 2,
              },
            },
            {
              title: 'Additional Borrow Amount',
              amount: borrowCoin.amount.toNumber(),
              warningMessages: borrowWarningMessages,
              options: {
                suffix: ` ${props.borrowAsset.symbol}`,
                abbreviated: true,
                decimals: props.borrowAsset.decimals,
              },
            },
            {
              title: 'New Debt Amount',
              amount: currentDebt.plus(borrowCoin.amount).toNumber(),
              options: {
                suffix: ` ${props.borrowAsset.symbol}`,
                abbreviated: true,
                decimals: props.borrowAsset.decimals,
              },
            },
          ]
        : []),
    ],
    [
      borrowCoin.amount,
      borrowRate,
      borrowWarningMessages,
      currentDebt,
      keepLeverage,
      props.borrowAsset.decimals,
      props.borrowAsset.symbol,
    ],
  )

  return (
    <>
      <div>
        <TokenInputWithSlider
          amount={depositCoin.amount}
          asset={props.collateralAsset}
          max={collateralAssetAmountInWallet}
          onChange={handleOnChange}
          maxText='In Wallet'
          warningMessages={warningMessages}
        />
        <Divider className='my-6' />
        <div className='flex flex-wrap items-center justify-between flex-1'>
          <div>
            <Text className='w-full mb-1'>Keep leverage</Text>
            <Text size='xs' className='text-white/50'>
              Automatically borrow more funds to keep leverage
            </Text>
          </div>
          <Switch name='keep-leverage' checked={keepLeverage} onChange={toggleKeepLeverage} />
        </div>
      </div>
      <div className='flex flex-col gap-4'>
        <SummaryItems items={items} />
        <Button
          onClick={handleDeposit}
          text='Deposit'
          disabled={
            depositCoin.amount.isZero() ||
            !!warningMessages.length ||
            !!borrowWarningMessages.length
          }
        />
      </div>
    </>
  )
}
