import { Contract } from '@ethersproject/contracts'
import type { TransactionResponse } from '@ethersproject/providers'
import { LiquidityEventName, LiquiditySource } from '@uniswap/analytics-events'
import { CurrencyAmount, Fraction, Percent, Price, Token, V2_FACTORY_ADDRESSES } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position, TickMath, priceToClosestTick } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import { ButtonConfirmed } from 'components/Button'
import { BlueCard, DarkGrayCard, LightCard, YellowCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import { DoubleCurrencyLogo } from 'components/DoubleLogo'
import FeeSelector from 'components/FeeSelector'
import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle'
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
import SettingsTab from 'components/Settings'
import { V2Unsupported } from 'components/V2Unsupported'
import { Dots } from 'components/swap/styled'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { useToken } from 'hooks/Tokens'
import { useAccount } from 'hooks/useAccount'
import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
import { usePairContract, useV2MigratorContract } from 'hooks/useContract'
import useIsArgentWallet from 'hooks/useIsArgentWallet'
import { useNetworkSupportsV2 } from 'hooks/useNetworkSupportsV2'
import { PoolState, usePool } from 'hooks/usePools'
import { useTotalSupply } from 'hooks/useTotalSupply'
import { useGetTransactionDeadline } from 'hooks/useTransactionDeadline'
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
import { Trans, t } from 'i18n'
import JSBI from 'jsbi'
import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import { BodyWrapper } from 'pages/App/AppBody'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { AlertCircle, AlertTriangle, ArrowDown } from 'react-feather'
import { Navigate, useParams } from 'react-router-dom'
import { useTokenBalance } from 'state/connection/hooks'
import { useAppDispatch } from 'state/hooks'
import { Bound, resetMintState } from 'state/mint/v3/actions'
import { useRangeHopCallbacks, useV3DerivedMintInfo, useV3MintActionHandlers } from 'state/mint/v3/hooks'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import { TransactionType } from 'state/transactions/types'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { useTheme } from 'styled-components'
import { BackArrowLink, ExternalLink, ThemedText } from 'theme/components'
import { Text } from 'ui/src'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { isAddress } from 'utilities/src/addresses'
import { logger } from 'utilities/src/logger/logger'
import { useTrace } from 'utilities/src/telemetry/trace/TraceContext'
import { calculateGasMargin } from 'utils/calculateGasMargin'
import { currencyId } from 'utils/currencyId'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
import { unwrappedToken } from 'utils/unwrappedToken'
import { MigrateHeader } from '.'

const ZERO = JSBI.BigInt(0)

const DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE = new Percent(75, 10_000)

function EmptyState({ message }: { message: ReactNode }) {
  return (
    <AutoColumn style={{ minHeight: 200, justifyContent: 'center', alignItems: 'center' }}>
      <ThemedText.DeprecatedBody>{message}</ThemedText.DeprecatedBody>
    </AutoColumn>
  )
}

function LiquidityInfo({
  token0Amount,
  token1Amount,
}: {
  token0Amount: CurrencyAmount<Token>
  token1Amount: CurrencyAmount<Token>
}) {
  const currency0 = unwrappedToken(token0Amount.currency)
  const currency1 = unwrappedToken(token1Amount.currency)

  return (
    <AutoColumn gap="sm">
      <RowBetween>
        <RowFixed>
          <CurrencyLogo size={20} style={{ marginRight: '8px' }} currency={currency0} />
          <Text fontSize={16} fontWeight="$medium">
            {currency0.symbol}
          </Text>
        </RowFixed>
        <Text fontSize={16} fontWeight="$medium">
          <FormattedCurrencyAmount currencyAmount={token0Amount} />
        </Text>
      </RowBetween>
      <RowBetween>
        <RowFixed>
          <CurrencyLogo size={20} style={{ marginRight: '8px' }} currency={currency1} />
          <Text fontSize={16} fontWeight="$medium">
            {currency1.symbol}
          </Text>
        </RowFixed>

        <Text fontSize={16} fontWeight="$medium">
          <FormattedCurrencyAmount currencyAmount={token1Amount} />
        </Text>
      </RowBetween>
    </AutoColumn>
  )
}

// hard-code this for now
const percentageToMigrate = 100

function V2PairMigration({
  pair,
  pairBalance,
  totalSupply,
  reserve0,
  reserve1,
  token0,
  token1,
}: {
  pair: Contract
  pairBalance: CurrencyAmount<Token>
  totalSupply: CurrencyAmount<Token>
  reserve0: CurrencyAmount<Token>
  reserve1: CurrencyAmount<Token>
  token0: Token
  token1: Token
}) {
  const account = useAccount()
  const theme = useTheme()
  const v2FactoryAddress = account.chainId ? V2_FACTORY_ADDRESSES[account.chainId] : undefined
  const trace = useTrace()

  const pairFactory = useSingleCallResult(pair, 'factory')
  const isNotUniswap = pairFactory.result?.[0] && pairFactory.result[0] !== v2FactoryAddress

  const getDeadline = useGetTransactionDeadline() // custom from users settings
  const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE) // custom from users

  const currency0 = unwrappedToken(token0)
  const currency1 = unwrappedToken(token1)

  // this is just getLiquidityValue with the fee off, but for the passed pair
  const token0Value = useMemo(
    () =>
      CurrencyAmount.fromRawAmount(
        token0,
        JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve0.quotient), totalSupply.quotient),
      ),
    [token0, pairBalance, reserve0, totalSupply],
  )
  const token1Value = useMemo(
    () =>
      CurrencyAmount.fromRawAmount(
        token1,
        JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve1.quotient), totalSupply.quotient),
      ),
    [token1, pairBalance, reserve1, totalSupply],
  )

  // set up v3 pool
  const [feeAmount, setFeeAmount] = useState(FeeAmount.MEDIUM)
  const [poolState, pool] = usePool(token0, token1, feeAmount)
  const noLiquidity = poolState === PoolState.NOT_EXISTS

  // get spot prices + price difference
  const v2SpotPrice = useMemo(
    () => new Price(token0, token1, reserve0.quotient, reserve1.quotient),
    [token0, token1, reserve0, reserve1],
  )
  const v3SpotPrice = poolState === PoolState.EXISTS ? pool?.token0Price : undefined

  let priceDifferenceFraction: Fraction | undefined =
    v2SpotPrice && v3SpotPrice ? v3SpotPrice.divide(v2SpotPrice).subtract(1).multiply(100) : undefined
  if (priceDifferenceFraction?.lessThan(ZERO)) {
    priceDifferenceFraction = priceDifferenceFraction.multiply(-1)
  }

  const largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(2))

  // the following is a small hack to get access to price range data/input handlers
  const [baseToken, setBaseToken] = useState(token0)
  const { ticks, pricesAtTicks, invertPrice, invalidRange, outOfRange, ticksAtLimit } = useV3DerivedMintInfo(
    token0,
    token1,
    feeAmount,
    baseToken,
  )

  // get value and prices at ticks
  const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
  const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks

  const { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper } = useRangeHopCallbacks(
    baseToken,
    baseToken.equals(token0) ? token1 : token0,
    feeAmount,
    tickLower,
    tickUpper,
  )

  const { onLeftRangeInput, onRightRangeInput } = useV3MintActionHandlers(noLiquidity)

  // the v3 tick is either the pool's tickCurrent, or the tick closest to the v2 spot price
  const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice)
  // the price is either the current v3 price, or the price at the tick
  const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick)
  const position =
    typeof tickLower === 'number' && typeof tickUpper === 'number' && !invalidRange
      ? Position.fromAmounts({
          pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []),
          tickLower,
          tickUpper,
          amount0: token0Value.quotient,
          amount1: token1Value.quotient,
          useFullPrecision: true, // we want full precision for the theoretical position
        })
      : undefined

  const { amount0: v3Amount0Min, amount1: v3Amount1Min } = useMemo(
    () => (position ? position.mintAmountsWithSlippage(allowedSlippage) : { amount0: undefined, amount1: undefined }),
    [position, allowedSlippage],
  )

  const refund0 = useMemo(
    () =>
      position && CurrencyAmount.fromRawAmount(token0, JSBI.subtract(token0Value.quotient, position.amount0.quotient)),
    [token0Value, position, token0],
  )
  const refund1 = useMemo(
    () =>
      position && CurrencyAmount.fromRawAmount(token1, JSBI.subtract(token1Value.quotient, position.amount1.quotient)),
    [token1Value, position, token1],
  )

  const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
  const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)

  const migrator = useV2MigratorContract()

  // approvals
  const [approval, approveManually] = useApproveCallback(pairBalance, migrator?.address)
  const { signatureData, gatherPermitSignature } = useV2LiquidityTokenPermit(pairBalance, migrator?.address)

  const isArgentWallet = useIsArgentWallet()

  const approve = useCallback(async () => {
    if (isNotUniswap || isArgentWallet) {
      // sushi has to be manually approved
      await approveManually()
    } else if (gatherPermitSignature) {
      try {
        await gatherPermitSignature()
      } catch (error) {
        // try to approve if gatherPermitSignature failed for any reason other than the user rejecting it
        if (error?.code !== 4001) {
          await approveManually()
        }
      }
    } else {
      await approveManually()
    }
  }, [isNotUniswap, isArgentWallet, gatherPermitSignature, approveManually])

  const addTransaction = useTransactionAdder()
  const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)

  const networkSupportsV2 = useNetworkSupportsV2()

  const migrate = useCallback(async () => {
    if (
      !migrator ||
      !account.address ||
      typeof tickLower !== 'number' ||
      typeof tickUpper !== 'number' ||
      !v3Amount0Min ||
      !v3Amount1Min ||
      !account.chainId ||
      !networkSupportsV2
    ) {
      return
    }

    const deadline = signatureData?.deadline ?? (await getDeadline())
    if (!deadline) {
      throw new Error('could not get deadline')
    }

    const data: string[] = []

    // permit if necessary
    if (signatureData) {
      data.push(
        migrator.interface.encodeFunctionData('selfPermit', [
          pair.address,
          `0x${pairBalance.quotient.toString(16)}`,
          deadline,
          signatureData.v,
          signatureData.r,
          signatureData.s,
        ]),
      )
    }

    // create/initialize pool if necessary
    if (noLiquidity) {
      data.push(
        migrator.interface.encodeFunctionData('createAndInitializePoolIfNecessary', [
          token0.address,
          token1.address,
          feeAmount,
          `0x${sqrtPrice.toString(16)}`,
        ]),
      )
    }

    // TODO could save gas by not doing this in multicall
    data.push(
      migrator.interface.encodeFunctionData('migrate', [
        {
          pair: pair.address,
          liquidityToMigrate: `0x${pairBalance.quotient.toString(16)}`,
          percentageToMigrate,
          token0: token0.address,
          token1: token1.address,
          fee: feeAmount,
          tickLower,
          tickUpper,
          amount0Min: `0x${v3Amount0Min.toString(16)}`,
          amount1Min: `0x${v3Amount1Min.toString(16)}`,
          recipient: account.address,
          deadline,
          refundAsETH: true, // hard-code this for now
        },
      ]),
    )

    setConfirmingMigration(true)

    migrator.estimateGas
      .multicall(data)
      .then((gasEstimate) => {
        return migrator
          .multicall(data, { gasLimit: calculateGasMargin(gasEstimate) })
          .then((response: TransactionResponse) => {
            sendAnalyticsEvent(LiquidityEventName.MIGRATE_LIQUIDITY_SUBMITTED, {
              action: `${isNotUniswap ? LiquiditySource.SUSHISWAP : LiquiditySource.V2}->${LiquiditySource.V3}`,
              label: `${currency0.symbol}/${currency1.symbol}`,
              ...trace,
            })

            addTransaction(response, {
              type: TransactionType.MIGRATE_LIQUIDITY_V3,
              baseCurrencyId: currencyId(currency0),
              quoteCurrencyId: currencyId(currency1),
              isFork: isNotUniswap,
            })
            setPendingMigrationHash(response.hash)
          })
      })
      .catch(() => {
        setConfirmingMigration(false)
      })
  }, [
    migrator,
    tickLower,
    tickUpper,
    v3Amount0Min,
    v3Amount1Min,
    account.address,
    account.chainId,
    networkSupportsV2,
    signatureData,
    getDeadline,
    noLiquidity,
    pair.address,
    pairBalance.quotient,
    token0.address,
    token1.address,
    feeAmount,
    sqrtPrice,
    isNotUniswap,
    currency0,
    currency1,
    trace,
    addTransaction,
  ])

  const isSuccessfullyMigrated = !!pendingMigrationHash && JSBI.equal(pairBalance.quotient, ZERO)

  if (!networkSupportsV2) {
    return <V2Unsupported />
  }

  return (
    <AutoColumn gap="20px">
      <ThemedText.DeprecatedBody my={9} style={{ fontWeight: 485, textAlign: 'center' }}>
        <Trans
          i18nKey="migrate.v2Description"
          values={{
            source: isNotUniswap ? 'SushiSwap' : 'V2',
          }}
        >
          <ExternalLink
            key="migration-contract"
            href={getExplorerLink(
              account.chainId ?? UniverseChainId.Mainnet,
              migrator?.address ?? '',
              ExplorerDataType.ADDRESS,
            )}
          >
            <Text color="$accent1" display="inline">
              <Trans i18nKey="migrate.contract" /> ↗
            </Text>
          </ExternalLink>
        </Trans>
        .
      </ThemedText.DeprecatedBody>

      <LightCard>
        <AutoColumn gap="lg">
          <RowBetween>
            <RowFixed style={{ marginLeft: '8px' }}>
              <DoubleCurrencyLogo currencies={[currency0, currency1]} size={20} />
              <ThemedText.DeprecatedMediumHeader style={{ marginLeft: '8px' }}>
                <Trans
                  i18nKey="migrate.lpTokens"
                  values={{
                    symA: currency0.symbol,
                    symB: currency1.symbol,
                  }}
                />
              </ThemedText.DeprecatedMediumHeader>
            </RowFixed>
            <Badge>{isNotUniswap ? 'Sushi' : 'V2'}</Badge>
          </RowBetween>
          <LiquidityInfo token0Amount={token0Value} token1Amount={token1Value} />
        </AutoColumn>
      </LightCard>

      <div style={{ display: 'flex', justifyContent: 'center' }}>
        <ArrowDown size={24} />
      </div>

      <LightCard>
        <AutoColumn gap="lg">
          <RowBetween>
            <RowFixed style={{ marginLeft: '8px' }}>
              <DoubleCurrencyLogo currencies={[currency0, currency1]} size={20} />
              <ThemedText.DeprecatedMediumHeader style={{ marginLeft: '8px' }}>
                <Trans i18nKey="migrate.lpNFT" values={{ symA: currency0.symbol, symB: currency1.symbol }} />
              </ThemedText.DeprecatedMediumHeader>
            </RowFixed>
            <Badge>V3</Badge>
          </RowBetween>

          <FeeSelector feeAmount={feeAmount} handleFeePoolSelect={setFeeAmount} />
          {noLiquidity && (
            <BlueCard style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
              <AlertCircle color={theme.neutral1} style={{ marginBottom: '12px', opacity: 0.8 }} />
              <ThemedText.DeprecatedBody
                fontSize={14}
                style={{ marginBottom: 8, fontWeight: 535, opacity: 0.8 }}
                textAlign="center"
              >
                <Trans
                  i18nKey="migrate.firstLP"
                  values={{
                    source: isNotUniswap ? 'SushiSwap' : 'V2',
                  }}
                />
              </ThemedText.DeprecatedBody>

              <ThemedText.DeprecatedBody
                fontWeight="$medium"
                textAlign="center"
                fontSize={14}
                style={{ marginTop: '8px', opacity: 0.8 }}
              >
                <Trans i18nKey="migrate.highGasCost" />
              </ThemedText.DeprecatedBody>

              {v2SpotPrice && (
                <AutoColumn gap="sm" style={{ marginTop: '12px' }}>
                  <RowBetween>
                    <ThemedText.DeprecatedBody fontWeight={535} fontSize={14}>
                      <Trans
                        i18nKey="migrate.symbolPrice"
                        values={{
                          name: isNotUniswap ? 'SushiSwap' : 'V2',
                          sym: invertPrice ? currency1.symbol : currency0.symbol,
                        }}
                      />{' '}
                      {invertPrice
                        ? `${v2SpotPrice?.invert()?.toSignificant(6)} ${currency0.symbol}`
                        : `${v2SpotPrice?.toSignificant(6)} ${currency1.symbol}`}
                    </ThemedText.DeprecatedBody>
                  </RowBetween>
                </AutoColumn>
              )}
            </BlueCard>
          )}

          {largePriceDifference ? (
            <YellowCard>
              <AutoColumn gap="sm">
                <RowBetween>
                  <ThemedText.DeprecatedBody fontSize={14}>
                    <Trans
                      i18nKey="migrate.symbolPrice"
                      values={{
                        name: isNotUniswap ? 'SushiSwap' : 'V2',
                        sym: invertPrice ? currency1.symbol : currency0.symbol,
                      }}
                    />
                  </ThemedText.DeprecatedBody>
                  <ThemedText.DeprecatedBlack fontSize={14}>
                    {invertPrice
                      ? `${v2SpotPrice?.invert()?.toSignificant(6)} ${currency0.symbol}`
                      : `${v2SpotPrice?.toSignificant(6)} ${currency1.symbol}`}
                  </ThemedText.DeprecatedBlack>
                </RowBetween>

                <RowBetween>
                  <ThemedText.DeprecatedBody fontSize={14}>
                    V3 {invertPrice ? currency1.symbol : currency0.symbol} {t('common.price')}:
                  </ThemedText.DeprecatedBody>
                  <ThemedText.DeprecatedBlack fontSize={14}>
                    {invertPrice
                      ? `${v3SpotPrice?.invert()?.toSignificant(6)} ${currency0.symbol}`
                      : `${v3SpotPrice?.toSignificant(6)} ${currency1.symbol}`}
                  </ThemedText.DeprecatedBlack>
                </RowBetween>

                <RowBetween>
                  <ThemedText.DeprecatedBody fontSize={14} color="inherit">
                    <Trans i18nKey="migrate.priceDifference" />
                  </ThemedText.DeprecatedBody>
                  <ThemedText.DeprecatedBlack fontSize={14} color="inherit">
                    {priceDifferenceFraction?.toSignificant(4)}%
                  </ThemedText.DeprecatedBlack>
                </RowBetween>
              </AutoColumn>
              <ThemedText.DeprecatedBody fontSize={14} style={{ marginTop: 8, fontWeight: 485 }}>
                <Trans i18nKey="migrate.priceWarning" />
              </ThemedText.DeprecatedBody>
            </YellowCard>
          ) : !noLiquidity && v3SpotPrice ? (
            <RowBetween>
              <ThemedText.DeprecatedBody fontSize={14}>
                <Trans i18nKey="migrate.v3Price" values={{ sym: invertPrice ? currency1.symbol : currency0.symbol }} />
              </ThemedText.DeprecatedBody>
              <ThemedText.DeprecatedBlack fontSize={14}>
                {invertPrice
                  ? `${v3SpotPrice?.invert()?.toSignificant(6)} ${currency0.symbol}`
                  : `${v3SpotPrice?.toSignificant(6)} ${currency1.symbol}`}
              </ThemedText.DeprecatedBlack>
            </RowBetween>
          ) : null}

          <RowBetween>
            <ThemedText.DeprecatedLabel>
              <Trans i18nKey="migrate.setRange" />
            </ThemedText.DeprecatedLabel>
            <RateToggle
              currencyA={invertPrice ? currency1 : currency0}
              currencyB={invertPrice ? currency0 : currency1}
              handleRateToggle={() => {
                onLeftRangeInput('')
                onRightRangeInput('')
                setBaseToken((base) => (base.equals(token0) ? token1 : token0))
              }}
            />
          </RowBetween>

          <RangeSelector
            priceLower={priceLower}
            priceUpper={priceUpper}
            getDecrementLower={getDecrementLower}
            getIncrementLower={getIncrementLower}
            getDecrementUpper={getDecrementUpper}
            getIncrementUpper={getIncrementUpper}
            onLeftRangeInput={onLeftRangeInput}
            onRightRangeInput={onRightRangeInput}
            currencyA={invertPrice ? currency1 : currency0}
            currencyB={invertPrice ? currency0 : currency1}
            feeAmount={feeAmount}
            ticksAtLimit={ticksAtLimit}
          />

          {outOfRange ? (
            <YellowCard padding="8px 12px" $borderRadius="12px">
              <RowBetween>
                <AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
                <Text color="$yellow600" ml={12} fontSize={12}>
                  <Trans i18nKey="migrate.positionNoFees" />
                </Text>
              </RowBetween>
            </YellowCard>
          ) : null}

          {invalidRange ? (
            <YellowCard padding="8px 12px" $borderRadius="12px">
              <RowBetween>
                <AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
                <Text color="$" ml={12} fontSize={12}>
                  <Trans i18nKey="migrate.invalidRange" />
                </Text>
              </RowBetween>
            </YellowCard>
          ) : null}

          {position ? (
            <DarkGrayCard>
              <AutoColumn gap="md">
                <LiquidityInfo token0Amount={position.amount0} token1Amount={position.amount1} />
                {account.chainId && refund0 && refund1 ? (
                  <ThemedText.DeprecatedBlack fontSize={12}>
                    <Trans
                      i18nKey="migrate.refund"
                      values={{
                        amtA: formatCurrencyAmount(refund0, 4),
                        symA:
                          account.chainId && WRAPPED_NATIVE_CURRENCY[account.chainId]?.equals(token0)
                            ? 'ETH'
                            : token0.symbol,
                        amtB: formatCurrencyAmount(refund1, 4),
                        symB:
                          account.chainId && WRAPPED_NATIVE_CURRENCY[account.chainId]?.equals(token1)
                            ? 'ETH'
                            : token1.symbol,
                      }}
                    />
                  </ThemedText.DeprecatedBlack>
                ) : null}
              </AutoColumn>
            </DarkGrayCard>
          ) : null}

          <AutoColumn gap="md">
            {!isSuccessfullyMigrated && !isMigrationPending ? (
              <AutoColumn gap="md" style={{ flex: '1' }}>
                <ButtonConfirmed
                  disabled={
                    approval !== ApprovalState.NOT_APPROVED ||
                    signatureData !== null ||
                    !v3Amount0Min ||
                    !v3Amount1Min ||
                    invalidRange ||
                    confirmingMigration
                  }
                  onClick={approve}
                >
                  {approval === ApprovalState.PENDING ? (
                    <Dots>
                      <Trans i18nKey="common.approving" />
                    </Dots>
                  ) : approval === ApprovalState.APPROVED || signatureData !== null ? (
                    <Trans i18nKey="migrate.allowed" />
                  ) : (
                    <Trans i18nKey="migrate.allowLpMigration" />
                  )}
                </ButtonConfirmed>
              </AutoColumn>
            ) : null}
            <AutoColumn gap="md" style={{ flex: '1' }}>
              <ButtonConfirmed
                confirmed={isSuccessfullyMigrated}
                disabled={
                  !v3Amount0Min ||
                  !v3Amount1Min ||
                  invalidRange ||
                  (approval !== ApprovalState.APPROVED && signatureData === null) ||
                  confirmingMigration ||
                  isMigrationPending ||
                  isSuccessfullyMigrated
                }
                onClick={migrate}
              >
                {isSuccessfullyMigrated ? (
                  'Success!'
                ) : isMigrationPending ? (
                  <Dots>
                    <Trans i18nKey="migrate.migrating" />
                  </Dots>
                ) : (
                  <Trans i18nKey="common.migrate" />
                )}
              </ButtonConfirmed>
            </AutoColumn>
          </AutoColumn>
        </AutoColumn>
      </LightCard>
    </AutoColumn>
  )
}

export default function MigrateV2Pair() {
  const { address } = useParams<{ address: string }>()
  // reset mint state on component mount, and as a cleanup (on unmount)
  const dispatch = useAppDispatch()
  useEffect(() => {
    dispatch(resetMintState())
    return () => {
      dispatch(resetMintState())
    }
  }, [dispatch])

  const account = useAccount()

  // get pair contract
  const validatedAddress = isAddress(address)
  const pair = usePairContract(validatedAddress ? validatedAddress : undefined)

  // get token addresses from pair contract
  const token0AddressCallState = useSingleCallResult(pair, 'token0', undefined, NEVER_RELOAD)
  const token0Address = token0AddressCallState?.result?.[0]
  const token1Address = useSingleCallResult(pair, 'token1', undefined, NEVER_RELOAD)?.result?.[0]

  // get tokens
  const token0 = useToken(token0Address)
  const token1 = useToken(token1Address)

  // get liquidity token balance
  const liquidityToken: Token | undefined = useMemo(
    () => (account.chainId && validatedAddress ? new Token(account.chainId, validatedAddress, 18) : undefined),
    [account.chainId, validatedAddress],
  )

  // get data required for V2 pair migration
  const pairBalance = useTokenBalance(account.address, liquidityToken)
  const totalSupply = useTotalSupply(liquidityToken)
  const [reserve0Raw, reserve1Raw] = useSingleCallResult(pair, 'getReserves')?.result ?? []
  const reserve0 = useMemo(
    () => (token0 && reserve0Raw ? CurrencyAmount.fromRawAmount(token0, reserve0Raw) : undefined),
    [token0, reserve0Raw],
  )
  const reserve1 = useMemo(
    () => (token1 && reserve1Raw ? CurrencyAmount.fromRawAmount(token1, reserve1Raw) : undefined),
    [token1, reserve1Raw],
  )

  // redirect for invalid url params
  if (
    !validatedAddress ||
    !pair ||
    (pair &&
      token0AddressCallState?.valid &&
      !token0AddressCallState?.loading &&
      !token0AddressCallState?.error &&
      !token0Address)
  ) {
    logger.warn('MigrateV2Pair', 'MigrateV2Pair', 'Invalid pair address', {
      token0Address,
      token1Address,
      chainId: account.chainId,
    })
    return <Navigate to="/migrate/v2" replace />
  }

  return (
    <BodyWrapper style={{ padding: 24 }}>
      <AutoColumn gap="16px">
        <AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
          <BackArrowLink to="/migrate/v2" />
          <MigrateHeader>
            <Trans i18nKey="migrate.v2Title" />
          </MigrateHeader>
          <SettingsTab
            autoSlippage={DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE}
            chainId={account.chainId}
            hideRoutingSettings
          />
        </AutoRow>

        {!account.isConnected ? (
          <ThemedText.DeprecatedLargeHeader>
            <Trans i18nKey="migrate.connectAccount" />
          </ThemedText.DeprecatedLargeHeader>
        ) : pairBalance && totalSupply && reserve0 && reserve1 && token0 && token1 ? (
          <V2PairMigration
            pair={pair}
            pairBalance={pairBalance}
            totalSupply={totalSupply}
            reserve0={reserve0}
            reserve1={reserve1}
            token0={token0}
            token1={token1}
          />
        ) : (
          <EmptyState message={<Trans i18nKey="common.loading" />} />
        )}
      </AutoColumn>
    </BodyWrapper>
  )
}
