import { MailCheck } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { Link, useNavigate, useSearchParams } from 'react-router-dom'

import { AuthLayout } from './AuthLayout'
import { Alert, Button, Spinner } from '@/components/ui'
import { useAuth } from '@/features/auth/AuthProvider'
import { ApiError, api } from '@/lib/api'

/**
 * Completes email verification from the link in the user's inbox.
 *
 * The emailed link points here, carrying the signed API path and its signature
 * as query parameters. This page relays them to the API, which re-checks the
 * signature -- the SPA cannot forge a verification, it only carries the signed
 * material across.
 */
export function VerifyEmailPage() {
  const [searchParams] = useSearchParams()
  const { refresh, isAuthenticated } = useAuth()
  const navigate = useNavigate()

  /*
   * Whether the link carries what it needs is derived during render, not
   * discovered inside the effect. An incomplete link needs no request at all,
   * so there is nothing to synchronise and no state to set.
   */
  const path = searchParams.get('path')
  const signature = searchParams.get('signature')
  const expires = searchParams.get('expires')
  const isLinkComplete = path !== null && signature !== null

  const [status, setStatus] = useState<'verifying' | 'done' | 'failed'>('verifying')
  const [message, setMessage] = useState('')

  // Strict mode mounts effects twice in development; verifying once is enough.
  const hasRun = useRef(false)

  useEffect(() => {
    if (hasRun.current || !isLinkComplete) {
      return
    }
    hasRun.current = true

    const verify = async () => {
      try {
        /*
         * `path` is the signed API route from the email. It is sent as a query
         * parameter and used verbatim, because altering any part of it would
         * invalidate the signature the API checks.
         */
        const response = await api.get<{ message: string }>(
          path.replace(/^\/api\/v1/, ''),
          { query: { signature, expires } },
        )

        setStatus('done')
        setMessage(response.message)

        if (isAuthenticated) {
          await refresh()
        }
      } catch (caught) {
        setStatus('failed')
        setMessage(
          caught instanceof ApiError
            ? caught.message
            : 'This confirmation link could not be used.',
        )
      }
    }

    void verify()
  }, [path, signature, expires, isLinkComplete, refresh, isAuthenticated])

  if (!isLinkComplete) {
    return (
      <AuthLayout title="Confirmation link incomplete">
        <Alert tone="error" title="This link is not usable">
          Open the most recent confirmation link from your email, or request a new one.
        </Alert>
        <div className="mt-5">
          <Link to="/verify-email/pending">
            <Button variant="secondary" size="lg" fullWidth>
              Request a new link
            </Button>
          </Link>
        </div>
      </AuthLayout>
    )
  }

  return (
    <AuthLayout title="Confirming your email">
      {status === 'verifying' && (
        <div className="flex items-center gap-3 text-sm text-text-secondary">
          <Spinner label="Confirming your email address" />
          Confirming your email address…
        </div>
      )}

      {status === 'done' && (
        <>
          <Alert tone="success" title="Email confirmed">
            {message}
          </Alert>
          <div className="mt-5">
            <Button
              size="lg"
              fullWidth
              onClick={() => navigate(isAuthenticated ? '/dashboard' : '/login')}
            >
              {isAuthenticated ? 'Go to dashboard' : 'Sign in'}
            </Button>
          </div>
        </>
      )}

      {status === 'failed' && (
        <>
          <Alert tone="error" title="Could not confirm">
            {message}
          </Alert>
          <div className="mt-5">
            <Link to="/verify-email/pending">
              <Button variant="secondary" size="lg" fullWidth>
                Request a new link
              </Button>
            </Link>
          </div>
        </>
      )}
    </AuthLayout>
  )
}

/** Shown right after registration, and whenever a new link is wanted. */
export function VerifyEmailPendingPage() {
  const { user, isAuthenticated } = useAuth()
  const [notice, setNotice] = useState<string | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [isSending, setIsSending] = useState(false)

  const resend = async () => {
    setNotice(null)
    setError(null)
    setIsSending(true)

    try {
      const response = await api.post<{ message: string }>('/auth/email/resend')
      setNotice(response.message)
    } catch (caught) {
      setError(
        caught instanceof ApiError
          ? caught.message
          : 'Could not send a new link. Try again shortly.',
      )
    } finally {
      setIsSending(false)
    }
  }

  return (
    <AuthLayout
      title="Confirm your email address"
      description={
        user?.email !== undefined
          ? `We sent a confirmation link to ${user.email}.`
          : 'We sent you a confirmation link.'
      }
    >
      <div className="flex flex-col gap-5">
        <div className="flex items-start gap-3 rounded-lg border border-border-subtle bg-surface-sunken px-4 py-3.5">
          <MailCheck aria-hidden className="mt-0.5 size-4 shrink-0 text-text-accent" />
          <p className="text-sm text-text-secondary">
            You can browse the marketplace straight away. Confirming your address is required
            before making an offer, buying, or listing a domain.
          </p>
        </div>

        {notice !== null && <Alert tone="success">{notice}</Alert>}
        {error !== null && <Alert tone="error">{error}</Alert>}

        {isAuthenticated && (
          <Button variant="secondary" size="lg" fullWidth isLoading={isSending} onClick={resend}>
            Send a new link
          </Button>
        )}

        <Link to="/domains">
          <Button variant="ghost" size="lg" fullWidth>
            Browse domains
          </Button>
        </Link>
      </div>
    </AuthLayout>
  )
}
