import { useStripe } from '@stripe/react-stripe-js'
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe, Stripe } from '@stripe/stripe-js'
import { ActionIcon, Box, Menu, Modal, Skeleton, Typography } from '@tidbcloud/uikit'
import { Form } from '@tidbcloud/uikit/biz'
import { IconDotsHorizontal } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import React, { useEffect, useState } from 'react'
import { useQueryClient } from 'react-query'

import { log } from 'common/log'
import { AccessGuard } from 'dbaas/contexts/access-control'
import {
  stripeGetStripeInvoiceDetail,
  setupIntent,
  useListPaymentMethods,
  useGetCustomerInfo,
  getListStripeInvoicesQueryKey
} from 'dbaas/services/billing'
import { FormsStripeInvoice } from 'dbaas/services/billing/interface'
import useStores from 'dbaas/stores/useStores'

import { MoneyValue } from '../components/MoneyValue'

import { INVOICE_STATUS, PAYMENT_INTENT_STATUS } from './constants'

interface InvoiceActionsProps {
  row: FormsStripeInvoice
  orgId: string
  billingConfigId: number
}

export const PayNowForm = ({
  onClose,
  invoice,
  orgId,
  billingConfigId
}: {
  open: boolean
  onClose: () => void
  invoice: FormsStripeInvoice
  orgId: string
  billingConfigId: number
}) => {
  const stripe = useStripe()
  const queryClient = useQueryClient()
  const { data: paymentMethods, isLoading: isLoadingPaymentMethods } = useListPaymentMethods(orgId, billingConfigId, {
    query: {
      refetchInterval: false,
      refetchOnWindowFocus: false
    }
  })
  const { data: customerInfo, isLoading: isLoadingCustomerInfo } = useGetCustomerInfo(orgId, billingConfigId, {
    query: {
      refetchInterval: false,
      refetchOnWindowFocus: false
    }
  })
  const last4 = paymentMethods?.find((method) => method.id === customerInfo?.default_source)?.card?.last4 || ''

  const onSubmit = async () => {
    if (!stripe) {
      log.error('Stripe not available.')
      throw new Error('Service not available. Please try again later.')
    }
    const details = await stripeGetStripeInvoiceDetail(orgId, billingConfigId, invoice.id || '')

    // handle payment method confirmation or 3DS verification
    if (
      Array.of<string>(
        PAYMENT_INTENT_STATUS.REQUIRES_PAYMENT_METHOD,
        PAYMENT_INTENT_STATUS.REQUIRES_ACTION,
        PAYMENT_INTENT_STATUS.REQUIRES_CONFIRMATION
      ).includes(details?.payment_intent_status || '')
    ) {
      const result = await stripe.confirmCardPayment(details.client_secret || '', {
        // @TODO need to get payment method from the user
        payment_method: customerInfo?.default_source || '',
        return_url: `${window.location.origin}/org-settings/billing/invoices`
      })
      if (result.error) {
        log.error('Stripe confirm card payment error:', result.error.message)
        throw new Error(result.error.message)
      }
    } else {
      log.error('Unknown payment intent status.', details.payment_intent_status)
      throw new Error('Unknown payment intent status.')
    }
    // invalidate invoices list query
    queryClient.invalidateQueries({
      queryKey: getListStripeInvoicesQueryKey(orgId, billingConfigId)
    })
    onClose()
  }

  const loading = isLoadingPaymentMethods || isLoadingCustomerInfo
  return (
    <Form
      onSubmit={onSubmit}
      actionsProps={{
        confirmText: 'Pay Now',
        onCancel: onClose,
        confirmProps: {
          disabled: loading
        }
      }}
    >
      {loading && (
        <>
          <Skeleton visible={loading} height={16}></Skeleton>
          <Skeleton visible={loading} height={16} mt={1} width="45%"></Skeleton>
        </>
      )}
      {!loading && (
        <Typography variant="body-lg">
          You are about to pay{' '}
          <b>
            <MoneyValue amount={invoice.total || '0'} />
          </b>
          . Your credit card {last4 ? `(ending in ${last4})` : ''} will be charged immediately.
        </Typography>
      )}
    </Form>
  )
}

const PayNowModal = ({
  open,
  onClose,
  invoice,
  orgId,
  billingConfigId
}: {
  open: boolean
  onClose: () => void
  invoice: FormsStripeInvoice
  orgId: string
  billingConfigId: number
}) => {
  const [stripe, setStripe] = useState<Stripe | null>(null)

  const loadStripeWithKey = async (publicKwy: string) => {
    const stripe = await loadStripe(publicKwy, {
      locale: 'en'
    })
    setStripe(stripe)
  }

  const setup = async () => {
    const intent = await setupIntent(orgId, billingConfigId)
    if (!intent) {
      return
    }
    loadStripeWithKey(intent.public_key)
  }

  useEffect(() => {
    setup()
  }, [])

  return (
    <Modal opened={open} onClose={onClose} title="Confirm Payment">
      <Elements stripe={stripe}>
        <PayNowForm open={open} onClose={onClose} invoice={invoice} orgId={orgId} billingConfigId={billingConfigId} />
      </Elements>
    </Modal>
  )
}

export const InvoiceActions: React.FC<InvoiceActionsProps> = observer(({ row, orgId, billingConfigId }) => {
  const {
    store: { isThirdParty }
  } = useStores()
  const hasPdf = !!row.invoice_pdf
  const showPayNow = !isThirdParty && row.status === INVOICE_STATUS.OPEN
  const [showPayNowModal, setShowPayNowModal] = useState(false)
  if (!hasPdf && !showPayNow) {
    return '-'
  }

  return (
    <Box>
      <Menu withinPortal withArrow position="bottom">
        <Menu.Target>
          <ActionIcon w={32} h={32} variant="subtle" data-mp-event="Invoice Actions Clicked">
            <IconDotsHorizontal size={16} />
          </ActionIcon>
        </Menu.Target>
        <Menu.Dropdown maw={164}>
          {hasPdf && (
            <Menu.Item
              component="a"
              download={'invoice.pdf'}
              href={row.invoice_pdf}
              data-mp-event="Invoice Export Button Clicked"
            >
              Export
            </Menu.Item>
          )}
          {showPayNow && (
            <AccessGuard id="BillingPaymentAction">
              <Menu.Item
                onClick={() => {
                  setShowPayNowModal(true)
                }}
                data-mp-event="Invoice Pay Now Button Clicked"
              >
                Pay Now
              </Menu.Item>
            </AccessGuard>
          )}
        </Menu.Dropdown>
      </Menu>
      {showPayNowModal && (
        <PayNowModal
          open={showPayNowModal}
          onClose={() => setShowPayNowModal(false)}
          invoice={row}
          orgId={orgId}
          billingConfigId={billingConfigId}
        />
      )}
    </Box>
  )
})
