import HCaptcha from '@hcaptcha/react-hcaptcha'
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import { useTheme } from 'next-themes'
import { useCallback, useEffect, useState } from 'react'
import { toast } from 'sonner'
import { Modal } from 'ui'

import AddPaymentMethodForm from './AddPaymentMethodForm'
import { getStripeElementsAppearanceOptions } from './Payment.utils'
import { useOrganizationPaymentMethodSetupIntent } from '@/data/organizations/organization-payment-method-setup-intent-mutation'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { STRIPE_PUBLIC_KEY } from '@/lib/constants'

interface AddNewPaymentMethodModalProps {
  visible: boolean
  returnUrl: string
  onCancel: () => void
  onConfirm: () => void
}

const stripePromise = loadStripe(STRIPE_PUBLIC_KEY)

const AddNewPaymentMethodModal = ({
  visible,
  returnUrl,
  onCancel,
  onConfirm,
}: AddNewPaymentMethodModalProps) => {
  const { resolvedTheme } = useTheme()
  const [intent, setIntent] = useState<any>()
  const { data: selectedOrganization } = useSelectedOrganizationQuery()

  const [captchaToken, setCaptchaToken] = useState<string | null>(null)
  const [captchaRef, setCaptchaRef] = useState<HCaptcha | null>(null)

  const { mutate: setupIntent } = useOrganizationPaymentMethodSetupIntent({
    onSuccess: (intent) => {
      setIntent(intent)
    },
    onError: (error) => {
      toast.error(`Failed to setup intent: ${error.message}`)
    },
  })

  const captchaRefCallback = useCallback((node: any) => {
    setCaptchaRef(node)
  }, [])

  useEffect(() => {
    const initSetupIntent = async (hcaptchaToken: string | undefined) => {
      const slug = selectedOrganization?.slug
      if (!slug) return console.error('Slug is required')
      if (!hcaptchaToken) return console.error('HCaptcha token required')

      setIntent(undefined)
      setupIntent({ slug, hcaptchaToken })
    }

    const loadPaymentForm = async () => {
      if (visible && captchaRef) {
        let token = captchaToken

        try {
          if (!token) {
            const captchaResponse = await captchaRef.execute({ async: true })
            token = captchaResponse?.response ?? null
          }
        } catch (error) {
          return
        }

        await initSetupIntent(token ?? undefined)
        resetCaptcha()
      }
    }

    loadPaymentForm()
  }, [visible, captchaRef])

  const resetCaptcha = () => {
    setCaptchaToken(null)
    captchaRef?.resetCaptcha()
  }

  const options = {
    clientSecret: intent ? intent.client_secret : '',
    appearance: getStripeElementsAppearanceOptions(resolvedTheme),
  } as any

  const onLocalCancel = () => {
    setIntent(undefined)
    return onCancel()
  }

  const onLocalConfirm = () => {
    setIntent(undefined)
    return onConfirm()
  }

  return (
    // We cant display the hCaptcha in the modal, as the modal auto-closes when clicking the captcha
    // So we only show the modal if the captcha has been executed successfully (intent loaded)
    <>
      <HCaptcha
        ref={captchaRefCallback}
        sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
        size="invisible"
        onOpen={() => {
          // [Joshen] This is to ensure that hCaptcha popup remains clickable
          if (document !== undefined) document.body.classList.add('pointer-events-auto!')
        }}
        onClose={() => {
          onLocalCancel()
          if (document !== undefined) document.body.classList.remove('pointer-events-auto!')
        }}
        onVerify={(token) => {
          setCaptchaToken(token)
          if (document !== undefined) document.body.classList.remove('pointer-events-auto!')
        }}
        onExpire={() => {
          setCaptchaToken(null)
        }}
      />

      <Modal
        hideFooter
        size="medium"
        visible={visible && intent !== undefined}
        header="Add new payment method"
        onCancel={onLocalCancel}
        className="PAYMENT"
      >
        <Elements stripe={stripePromise} options={options}>
          <AddPaymentMethodForm
            returnUrl={returnUrl}
            onCancel={onLocalCancel}
            onConfirm={onLocalConfirm}
          />
        </Elements>
      </Modal>
    </>
  )
}

export default AddNewPaymentMethodModal
