import { BigNumber } from '@ethersproject/bignumber'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Fraction, Token } from '@uniswap/sdk-core'
import ExecuteModal from 'components/vote/ExecuteModal'
import QueueModal from 'components/vote/QueueModal'
import { useActiveLocale } from 'hooks/useActiveLocale'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import JSBI from 'jsbi'
import useBlockNumber from 'lib/hooks/useBlockNumber'
import ms from 'ms.macro'
import { useState } from 'react'
import { ArrowLeft } from 'react-feather'
import ReactMarkdown from 'react-markdown'
import { RouteComponentProps } from 'react-router-dom'
import styled from 'styled-components/macro'

import { ButtonPrimary } from '../../components/Button'
import { GreyCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import { CardSection, DataCard } from '../../components/earn/styled'
import { RowBetween, RowFixed } from '../../components/Row'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import DelegateModal from '../../components/vote/DelegateModal'
import VoteModal from '../../components/vote/VoteModal'
import {
  AVERAGE_BLOCK_TIME_IN_SECS,
  COMMON_CONTRACT_NAMES,
  DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
} from '../../constants/governance'
import { ZERO_ADDRESS } from '../../constants/misc'
import { UNI } from '../../constants/tokens'
import {
  useModalOpen,
  useToggleDelegateModal,
  useToggleExecuteModal,
  useToggleQueueModal,
  useToggleVoteModal,
} from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import {
  ProposalData,
  ProposalState,
  useProposalData,
  useQuorum,
  useUserDelegatee,
  useUserVotesAsOfBlock,
} from '../../state/governance/hooks'
import { VoteOption } from '../../state/governance/types'
import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, ThemedText } from '../../theme'
import { isAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ProposalStatus } from './styled'

const PageWrapper = styled(AutoColumn)`
  width: 100%;
`

const ProposalInfo = styled(AutoColumn)`
  background: ${({ theme }) => theme.bg0};
  border-radius: 12px;
  padding: 1.5rem;
  position: relative;
  max-width: 640px;
  width: 100%;
`

const ArrowWrapper = styled(StyledInternalLink)`
  display: flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  color: ${({ theme }) => theme.text1};

  a {
    color: ${({ theme }) => theme.text1};
    text-decoration: none;
  }
  :hover {
    text-decoration: none;
  }
`
const CardWrapper = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  width: 100%;
`

const StyledDataCard = styled(DataCard)`
  width: 100%;
  background: none;
  background-color: ${({ theme }) => theme.bg1};
  height: fit-content;
  z-index: 2;
`

const ProgressWrapper = styled.div`
  width: 100%;
  margin-top: 1rem;
  height: 4px;
  border-radius: 4px;
  background-color: ${({ theme }) => theme.bg3};
  position: relative;
`

const Progress = styled.div<{ status: 'for' | 'against'; percentageString?: string }>`
  height: 4px;
  border-radius: 4px;
  background-color: ${({ theme, status }) => (status === 'for' ? theme.green1 : theme.red1)};
  width: ${({ percentageString }) => percentageString ?? '0%'};
`

const MarkDownWrapper = styled.div`
  max-width: 640px;
  overflow: hidden;
`

const WrapSmall = styled(RowBetween)`
  ${({ theme }) => theme.mediaWidth.upToSmall`
    align-items: flex-start;
    flex-direction: column;
  `};
`

const DetailText = styled.div`
  word-break: break-all;
`

const ProposerAddressLink = styled(ExternalLink)`
  word-break: break-all;
`

function getDateFromBlock(
  targetBlock: number | undefined,
  currentBlock: number | undefined,
  averageBlockTimeInSeconds: number | undefined,
  currentTimestamp: BigNumber | undefined
): Date | undefined {
  if (targetBlock && currentBlock && averageBlockTimeInSeconds && currentTimestamp) {
    const date = new Date()
    date.setTime(
      currentTimestamp
        .add(BigNumber.from(averageBlockTimeInSeconds).mul(BigNumber.from(targetBlock - currentBlock)))
        .toNumber() * ms`1 second`
    )
    return date
  }
  return undefined
}

export default function VotePage({
  match: {
    params: { governorIndex, id },
  },
}: RouteComponentProps<{ governorIndex: string; id: string }>) {
  const parsedGovernorIndex = Number.parseInt(governorIndex)

  const { chainId, account } = useActiveWeb3React()

  const quorumAmount = useQuorum(parsedGovernorIndex)

  // get data for this specific proposal
  const proposalData: ProposalData | undefined = useProposalData(parsedGovernorIndex, id)

  // update vote option based on button interactions
  const [voteOption, setVoteOption] = useState<VoteOption | undefined>(undefined)

  // modal for casting votes
  const showVoteModal = useModalOpen(ApplicationModal.VOTE)
  const toggleVoteModal = useToggleVoteModal()

  // toggle for showing delegation modal
  const showDelegateModal = useModalOpen(ApplicationModal.DELEGATE)
  const toggleDelegateModal = useToggleDelegateModal()

  // toggle for showing queue modal
  const showQueueModal = useModalOpen(ApplicationModal.QUEUE)
  const toggleQueueModal = useToggleQueueModal()

  // toggle for showing execute modal
  const showExecuteModal = useModalOpen(ApplicationModal.EXECUTE)
  const toggleExecuteModal = useToggleExecuteModal()

  // get and format date from data
  const currentTimestamp = useCurrentBlockTimestamp()
  const currentBlock = useBlockNumber()
  const startDate = getDateFromBlock(
    proposalData?.startBlock,
    currentBlock,
    (chainId && AVERAGE_BLOCK_TIME_IN_SECS[chainId]) ?? DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
    currentTimestamp
  )
  const endDate = getDateFromBlock(
    proposalData?.endBlock,
    currentBlock,
    (chainId && AVERAGE_BLOCK_TIME_IN_SECS[chainId]) ?? DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
    currentTimestamp
  )
  const now = new Date()
  const locale = useActiveLocale()
  const dateFormat: Intl.DateTimeFormatOptions = {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
    timeZoneName: 'short',
  }
  // convert the eta to milliseconds before it's a date
  const eta = proposalData?.eta ? new Date(proposalData.eta.mul(ms`1 second`).toNumber()) : undefined

  // get total votes and format percentages for UI
  const totalVotes = proposalData?.forCount?.add(proposalData.againstCount)
  const forPercentage = totalVotes
    ? proposalData?.forCount?.asFraction?.divide(totalVotes.asFraction)?.multiply(100)
    : undefined
  const againstPercentage = forPercentage ? new Fraction(100).subtract(forPercentage) : undefined

  // only count available votes as of the proposal start block
  const availableVotes: CurrencyAmount<Token> | undefined = useUserVotesAsOfBlock(proposalData?.startBlock ?? undefined)

  // only show voting if user has > 0 votes at proposal start block and proposal is active,
  const showVotingButtons =
    availableVotes &&
    JSBI.greaterThan(availableVotes.quotient, JSBI.BigInt(0)) &&
    proposalData &&
    proposalData.status === ProposalState.ACTIVE

  // we only show the button if there's an account connected and the proposal state is correct
  const showQueueButton = account && proposalData?.status === ProposalState.SUCCEEDED

  // we only show the button if there's an account connected and the proposal state is correct
  const showExecuteButton = account && proposalData?.status === ProposalState.QUEUED

  const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(
    account ?? undefined,
    chainId ? UNI[chainId] : undefined
  )
  const userDelegatee: string | undefined = useUserDelegatee()

  // in blurb link to home page if they are able to unlock
  const showLinkForUnlock = Boolean(
    uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS
  )

  // show links in propsoal details if content is an address
  // if content is contract with common name, replace address with common name
  const linkIfAddress = (content: string) => {
    if (isAddress(content) && chainId) {
      const commonName = COMMON_CONTRACT_NAMES[chainId]?.[content] ?? content
      return (
        <ExternalLink href={getExplorerLink(chainId, content, ExplorerDataType.ADDRESS)}>{commonName}</ExternalLink>
      )
    }
    return <span>{content}</span>
  }

  return (
    <>
      <PageWrapper gap="lg" justify="center">
        <VoteModal
          isOpen={showVoteModal}
          onDismiss={toggleVoteModal}
          proposalId={proposalData?.id}
          voteOption={voteOption}
        />
        <DelegateModal isOpen={showDelegateModal} onDismiss={toggleDelegateModal} title={<Trans>Unlock Votes</Trans>} />
        <QueueModal isOpen={showQueueModal} onDismiss={toggleQueueModal} proposalId={proposalData?.id} />
        <ExecuteModal isOpen={showExecuteModal} onDismiss={toggleExecuteModal} proposalId={proposalData?.id} />
        <ProposalInfo gap="lg" justify="start">
          <RowBetween style={{ width: '100%' }}>
            <ArrowWrapper to="/vote">
              <Trans>
                <ArrowLeft size={20} /> All Proposals
              </Trans>
            </ArrowWrapper>
            {proposalData && <ProposalStatus status={proposalData.status} />}
          </RowBetween>
          <AutoColumn gap="10px" style={{ width: '100%' }}>
            <ThemedText.LargeHeader style={{ marginBottom: '.5rem' }}>{proposalData?.title}</ThemedText.LargeHeader>
            <RowBetween>
              <ThemedText.Main>
                {startDate && startDate > now ? (
                  <Trans>Voting starts approximately {startDate.toLocaleString(locale, dateFormat)}</Trans>
                ) : null}
              </ThemedText.Main>
            </RowBetween>
            <RowBetween>
              <ThemedText.Main>
                {endDate &&
                  (endDate < now ? (
                    <Trans>Voting ended {endDate.toLocaleString(locale, dateFormat)}</Trans>
                  ) : (
                    <Trans>Voting ends approximately {endDate.toLocaleString(locale, dateFormat)}</Trans>
                  ))}
              </ThemedText.Main>
            </RowBetween>
            {proposalData && proposalData.status === ProposalState.ACTIVE && !showVotingButtons && (
              <GreyCard>
                <ThemedText.Black>
                  <Trans>
                    Only UNI votes that were self delegated or delegated to another address before block{' '}
                    {proposalData.startBlock} are eligible for voting.{' '}
                  </Trans>
                  {showLinkForUnlock && (
                    <span>
                      <Trans>
                        <StyledInternalLink to="/vote">Unlock voting</StyledInternalLink> to prepare for the next
                        proposal.
                      </Trans>
                    </span>
                  )}
                </ThemedText.Black>
              </GreyCard>
            )}
          </AutoColumn>
          {showVotingButtons && (
            <RowFixed style={{ width: '100%', gap: '12px' }}>
              <ButtonPrimary
                padding="8px"
                $borderRadius="8px"
                onClick={() => {
                  setVoteOption(VoteOption.For)
                  toggleVoteModal()
                }}
              >
                <Trans>Vote For</Trans>
              </ButtonPrimary>
              <ButtonPrimary
                padding="8px"
                $borderRadius="8px"
                onClick={() => {
                  setVoteOption(VoteOption.Against)
                  toggleVoteModal()
                }}
              >
                <Trans>Vote Against</Trans>
              </ButtonPrimary>
            </RowFixed>
          )}
          {showQueueButton && (
            <RowFixed style={{ width: '100%', gap: '12px' }}>
              <ButtonPrimary
                padding="8px"
                $borderRadius="8px"
                onClick={() => {
                  toggleQueueModal()
                }}
              >
                <Trans>Queue</Trans>
              </ButtonPrimary>
            </RowFixed>
          )}
          {showExecuteButton && (
            <>
              {eta && (
                <RowBetween>
                  <ThemedText.Black>
                    <Trans>This proposal may be executed after {eta.toLocaleString(locale, dateFormat)}.</Trans>
                  </ThemedText.Black>
                </RowBetween>
              )}
              <RowFixed style={{ width: '100%', gap: '12px' }}>
                <ButtonPrimary
                  padding="8px"
                  $borderRadius="8px"
                  onClick={() => {
                    toggleExecuteModal()
                  }}
                  // can't execute until the eta has arrived
                  disabled={!currentTimestamp || !proposalData?.eta || currentTimestamp.lt(proposalData.eta)}
                >
                  <Trans>Execute</Trans>
                </ButtonPrimary>
              </RowFixed>
            </>
          )}
          <CardWrapper>
            <StyledDataCard>
              <CardSection>
                <AutoColumn gap="md">
                  <WrapSmall>
                    <ThemedText.Black fontWeight={600}>
                      <Trans>For</Trans>
                    </ThemedText.Black>
                    {proposalData && (
                      <ThemedText.Black fontWeight={600}>
                        {proposalData.forCount.toFixed(0, { groupSeparator: ',' })}
                        {quorumAmount && (
                          <span style={{ fontWeight: 400 }}>{` / ${quorumAmount.toExact({
                            groupSeparator: ',',
                          })}`}</span>
                        )}
                      </ThemedText.Black>
                    )}
                  </WrapSmall>
                </AutoColumn>
                <ProgressWrapper>
                  <Progress
                    status={'for'}
                    percentageString={
                      proposalData?.forCount.greaterThan(0) ? `${forPercentage?.toFixed(0) ?? 0}%` : '0%'
                    }
                  />
                </ProgressWrapper>
              </CardSection>
            </StyledDataCard>
            <StyledDataCard>
              <CardSection>
                <AutoColumn gap="md">
                  <WrapSmall>
                    <ThemedText.Black fontWeight={600}>
                      <Trans>Against</Trans>
                    </ThemedText.Black>
                    {proposalData && (
                      <ThemedText.Black fontWeight={600}>
                        {proposalData.againstCount.toFixed(0, { groupSeparator: ',' })}
                      </ThemedText.Black>
                    )}
                  </WrapSmall>
                </AutoColumn>
                <ProgressWrapper>
                  <Progress
                    status={'against'}
                    percentageString={
                      proposalData?.againstCount?.greaterThan(0) ? `${againstPercentage?.toFixed(0) ?? 0}%` : '0%'
                    }
                  />
                </ProgressWrapper>
              </CardSection>
            </StyledDataCard>
          </CardWrapper>
          <AutoColumn gap="md">
            <ThemedText.MediumHeader fontWeight={600}>
              <Trans>Details</Trans>
            </ThemedText.MediumHeader>
            {proposalData?.details?.map((d, i) => {
              return (
                <DetailText key={i}>
                  {i + 1}: {linkIfAddress(d.target)}.{d.functionSig}(
                  {d.callData.split(',').map((content, i) => {
                    return (
                      <span key={i}>
                        {linkIfAddress(content)}
                        {d.callData.split(',').length - 1 === i ? '' : ','}
                      </span>
                    )
                  })}
                  )
                </DetailText>
              )
            })}
          </AutoColumn>
          <AutoColumn gap="md">
            <ThemedText.MediumHeader fontWeight={600}>
              <Trans>Description</Trans>
            </ThemedText.MediumHeader>
            <MarkDownWrapper>
              <ReactMarkdown source={proposalData?.description} />
            </MarkDownWrapper>
          </AutoColumn>
          <AutoColumn gap="md">
            <ThemedText.MediumHeader fontWeight={600}>
              <Trans>Proposer</Trans>
            </ThemedText.MediumHeader>
            <ProposerAddressLink
              href={
                proposalData?.proposer && chainId
                  ? getExplorerLink(chainId, proposalData?.proposer, ExplorerDataType.ADDRESS)
                  : ''
              }
            >
              <ReactMarkdown source={proposalData?.proposer} />
            </ProposerAddressLink>
          </AutoColumn>
        </ProposalInfo>
      </PageWrapper>
      <SwitchLocaleLink />
    </>
  )
}
