import Button from 'react-bootstrap/Button'
import { fixedDecimal, numWithUnits } from '@/lib/format'
import { timeLeft } from '@/lib/time'
import { useMe } from './me'
import styles from './poll.module.css'
import { signIn } from 'next-auth/react'
import ActionTooltip from './action-tooltip'
import useQrPayment from './use-qr-payment'
import { useToast } from './toast'
import { usePaidMutation } from './use-paid-mutation'
import { POLL_VOTE, RETRY_PAID_ACTION } from '@/fragments/paidAction'

export default function Poll ({ item }) {
  const { me } = useMe()
  const pollVote = usePollVote({ query: POLL_VOTE, itemId: item.id })
  const toaster = useToast()

  const PollButton = ({ v }) => {
    return (
      <ActionTooltip placement='left' notForm overlayText='1 sat'>
        <Button
          variant='outline-info' className={styles.pollButton}
          onClick={me
            ? async () => {
              const variables = { id: v.id }
              const optimisticResponse = { pollVote: { __typename: 'PollVotePaidAction', result: { id: v.id } } }
              try {
                const { error } = await pollVote({
                  variables,
                  optimisticResponse
                })
                if (error) throw error
              } catch (error) {
                const reason = error?.message || error?.toString?.()
                toaster.danger(reason)
              }
            }
            : signIn}
        >
          {v.option}
        </Button>
      </ActionTooltip>
    )
  }

  const RetryVote = () => {
    const retryVote = usePollVote({ query: RETRY_PAID_ACTION, itemId: item.id })
    const waitForQrPayment = useQrPayment()

    if (item.poll.meInvoiceActionState === 'PENDING') {
      return (
        <span
          className='ms-2 fw-bold text-info pointer'
          onClick={() => waitForQrPayment(
            { id: parseInt(item.poll.meInvoiceId) }, null, { cancelOnClose: false }).catch(console.error)}
        >vote pending
        </span>
      )
    }
    return (
      <span
        className='ms-2 fw-bold text-warning pointer'
        onClick={() => retryVote({ variables: { invoiceId: parseInt(item.poll.meInvoiceId) } })}
      >
        retry vote
      </span>
    )
  }

  const hasExpiration = !!item.pollExpiresAt
  const timeRemaining = timeLeft(new Date(item.pollExpiresAt))
  const mine = item.user.id === me?.id
  const meVotePending = item.poll.meInvoiceActionState && item.poll.meInvoiceActionState !== 'PAID'
  const showPollButton = me && (!hasExpiration || timeRemaining) && !item.poll.meVoted && !meVotePending && !mine
  const pollCount = item.poll.count
  return (
    <div className={styles.pollBox}>
      {item.poll.options.map(v =>
        showPollButton
          ? <PollButton key={v.id} v={v} />
          : <PollResult
              key={v.id} v={v}
              progress={pollCount
                ? fixedDecimal((v.count) * 100 / pollCount, 1)
                : 0}
            />)}
      <div className='text-muted mt-1'>
        {numWithUnits(item.poll.count, { unitSingular: 'vote', unitPlural: 'votes' })}
        {hasExpiration && ` \\ ${timeRemaining ? `${timeRemaining} left` : 'poll ended'}`}
        {!showPollButton && meVotePending && <RetryVote />}
      </div>
    </div>
  )
}

function PollResult ({ v, progress }) {
  return (
    <div className={styles.pollResult}>
      <span className={styles.pollOption}>{v.option}</span>
      <span className='ms-auto me-2 align-self-center'>{progress}%</span>
      <div className={styles.pollProgress} style={{ width: `${progress}%` }} />
    </div>
  )
}

export function usePollVote ({ query = POLL_VOTE, itemId }) {
  const update = (cache, { data }) => {
    // the mutation name varies for optimistic retries
    const response = Object.values(data)[0]
    if (!response) return
    const { result, invoice } = response
    const { id } = result
    cache.modify({
      id: `Item:${itemId}`,
      fields: {
        poll (existingPoll) {
          const poll = { ...existingPoll }
          poll.meVoted = true
          if (invoice) {
            poll.meInvoiceActionState = 'PENDING'
            poll.meInvoiceId = invoice.id
          }
          poll.count += 1
          return poll
        }
      },
      optimistic: true
    })
    cache.modify({
      id: `PollOption:${id}`,
      fields: {
        count (existingCount) {
          return existingCount + 1
        }
      },
      optimistic: true
    })
  }

  const onPayError = (e, cache, { data }) => {
    // the mutation name varies for optimistic retries
    const response = Object.values(data)[0]
    if (!response) return
    const { result, invoice } = response
    const { id } = result
    cache.modify({
      id: `Item:${itemId}`,
      fields: {
        poll (existingPoll) {
          const poll = { ...existingPoll }
          poll.meVoted = false
          if (invoice) {
            poll.meInvoiceActionState = 'FAILED'
            poll.meInvoiceId = invoice?.id
          }
          poll.count -= 1
          return poll
        }
      },
      optimistic: true
    })
    cache.modify({
      id: `PollOption:${id}`,
      fields: {
        count (existingCount) {
          return existingCount - 1
        }
      },
      optimistic: true
    })
  }

  const onPaid = (cache, { data }) => {
    // the mutation name varies for optimistic retries
    const response = Object.values(data)[0]
    if (!response?.invoice) return
    const { invoice } = response
    cache.modify({
      id: `Item:${itemId}`,
      fields: {
        poll (existingPoll) {
          const poll = { ...existingPoll }
          poll.meVoted = true
          poll.meInvoiceActionState = 'PAID'
          poll.meInvoiceId = invoice.id
          return poll
        }
      }
    })
  }

  const [pollVote] = usePaidMutation(query, { update, onPayError, onPaid })
  return pollVote
}