"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Loader2 } from "lucide-react"
import { AuthCard } from "@/components/auth/auth-card"
import { AuthConnectButton } from "@/components/auth/auth-connect-button"
import { FlowChallengeForm } from "@/components/auth/flow-challenge-form"
import {
  AUTH_FLOW_SLUGS,
  isAuthenticationFlow,
  type AuthFlowSlug,
} from "@/lib/auth/auth-flow-slugs"
import {
  fetchEmbeddedAuthContext,
  flowComponent,
  flowRedirectUrl,
  flowTitle,
  flowValidationErrors,
  isOAuthAuthorizeRedirect,
  isRecoveryEmailSent,
  respondAuthFlow,
  respondDirectFlow,
  startAuthFlow,
  startDirectFlow,
  type FlowChallenge,
} from "@/lib/auth/flow-api"

type AuthFlowPageProps = {
  slug: AuthFlowSlug
  flowQuery?: string
  defaultTitle: string
  defaultDescription: string
  successTitle: string
  successDescription: string
  successActionLabel: string
  successHref: string
  /** Full document navigation required (OIDC login routes). */
  successExternal?: boolean
  /** After embedded auth flow, bridge to OIDC session via BFF /flows/complete. */
  bridgeAuthentication?: boolean
  returnTo?: string
  initialError?: string | null
  footer: React.ReactNode
  onSuccess?: () => void
}

function decodeAuthError(value: string | null | undefined): string | null {
  if (!value) return null
  try {
    return decodeURIComponent(value)
  } catch {
    return value
  }
}

export function AuthFlowPage({
  slug,
  flowQuery,
  defaultTitle,
  defaultDescription,
  successTitle,
  successDescription,
  successActionLabel,
  successHref,
  successExternal = false,
  bridgeAuthentication = false,
  returnTo = "/mail/inbox",
  initialError = null,
  footer,
  onSuccess,
}: AuthFlowPageProps) {
  const [challenge, setChallenge] = useState<FlowChallenge | null>(null)
  const [loading, setLoading] = useState(true)
  const [submitting, setSubmitting] = useState(false)
  const [bridging, setBridging] = useState(false)
  const [flowError, setFlowError] = useState<string | null>(null)
  const [done, setDone] = useState(false)
  const [denied, setDenied] = useState(false)
  const flowQueryRef = useRef<string | undefined>(flowQuery)
  const bootstrappedRef = useRef(false)
  const bridgedRef = useRef(false)
  // Direct mode: drive Authentik's flow executor in the browser so the browser holds the session,
  // then navigate to the authorize URL to obtain the OIDC code.
  const directMode = bridgeAuthentication && isAuthenticationFlow(slug)
  const executorBaseRef = useRef<string | null>(null)
  const authorizeUrlRef = useRef<string | null>(null)
  const startedRef = useRef(false)
  const redirectError = decodeAuthError(initialError)

  const finishAuthentication = useCallback(
    (finalChallenge?: FlowChallenge | null) => {
      if (!directMode || bridgedRef.current) {
        onSuccess?.()
        return
      }
      bridgedRef.current = true
      setBridging(true)
      const target = flowRedirectUrl(finalChallenge ?? challenge)
      if (target && isOAuthAuthorizeRedirect(target)) {
        window.location.href = target
        return
      }
      if (authorizeUrlRef.current) {
        window.location.href = authorizeUrlRef.current
        return
      }
      // No authorize continuation available — surface an error rather than silently failing.
      setBridging(false)
      bridgedRef.current = false
      setFlowError("Impossible de finaliser la connexion (authorize introuvable)")
    },
    [challenge, directMode, onSuccess]
  )

  useEffect(() => {
    if (bootstrappedRef.current) return
    bootstrappedRef.current = true

    void (async () => {
      setLoading(true)
      try {
        let step: Awaited<ReturnType<typeof startDirectFlow>>
        if (directMode) {
          const ctx = await fetchEmbeddedAuthContext(returnTo)
          executorBaseRef.current = ctx.executorBase
          authorizeUrlRef.current = ctx.authorizeUrl
          flowQueryRef.current = ctx.flowQuery
          step = await startDirectFlow(ctx.executorBase, slug, ctx.flowQuery)
        } else {
          step = await startAuthFlow(slug, flowQueryRef.current ?? flowQuery)
        }
        startedRef.current = true
        setChallenge(step.challenge)
        setDone(step.done)
        setDenied(step.denied)
        if (step.done && !step.denied) {
          if (directMode) {
            finishAuthentication(step.challenge)
            return
          }
          onSuccess?.()
        }
      } catch (err) {
        setFlowError(
          err instanceof Error ? err.message : "Impossible de démarrer le parcours"
        )
      } finally {
        setLoading(false)
      }
    })()
    // Intentionally once per mount — avoid re-start loop from challenge deps.
    // eslint-disable-next-line react-hooks/exhaustive-deps -- bootstrap runs once
  }, [])

  const handleSubmit = useCallback(
    async (payload: Record<string, unknown>) => {
      if (!startedRef.current) return
      setSubmitting(true)
      setFlowError(null)
      try {
        const step = directMode
          ? await respondDirectFlow(
              executorBaseRef.current ?? "/auth/api/v3/flows/executor",
              slug,
              payload,
              flowQueryRef.current
            )
          : await respondAuthFlow(slug, payload, flowQueryRef.current ?? flowQuery)
        setChallenge(step.challenge)
        setDone(step.done)
        setDenied(step.denied)
        if (step.done && !step.denied) {
          finishAuthentication(step.challenge)
        }
      } catch (err) {
        setFlowError(err instanceof Error ? err.message : "Échec de l'étape")
      } finally {
        setSubmitting(false)
      }
    },
    [directMode, finishAuthentication, flowQuery, slug]
  )

  const validationErrors = useMemo(() => flowValidationErrors(challenge), [challenge])
  const formError = flowError ?? redirectError ?? validationErrors._form ?? null

  const title = useMemo(() => {
    if ((done && !denied) || isRecoveryEmailSent(slug, challenge)) return successTitle
    if (denied) return "Accès refusé"
    return flowTitle(challenge) || defaultTitle
  }, [challenge, defaultTitle, denied, done, slug, successTitle])

  const description = useMemo(() => {
    if ((done && !denied) || isRecoveryEmailSent(slug, challenge)) return successDescription
    if (denied) {
      return "Ce parcours a été refusé. Vérifiez vos informations ou contactez le support."
    }
    return defaultDescription
  }, [challenge, defaultDescription, denied, done, slug, successDescription])

  const component = flowComponent(challenge)
  const redirectUrl = flowRedirectUrl(challenge)
  const recoveryEmailSent = isRecoveryEmailSent(slug, challenge)
  const showSuccess =
    (done && !denied && !bridgeAuthentication) || recoveryEmailSent

  return (
    <AuthCard title={title} description={description} error={formError} footer={footer}>
      {loading || bridging ? (
        <div className="flex justify-center py-8">
          <Loader2 className="size-6 animate-spin text-muted-foreground" aria-hidden />
          <span className="sr-only">{bridging ? "Finalisation…" : "Chargement…"}</span>
        </div>
      ) : showSuccess ? (
        <div className="flex flex-col items-center gap-4">
          {redirectUrl ? (
            <p className="text-center text-sm text-muted-foreground">
              Redirection en cours…
            </p>
          ) : null}
          {successExternal ? (
            <AuthConnectButton href={successHref}>
              {successActionLabel}
            </AuthConnectButton>
          ) : (
            <AuthConnectButton as="link" href={successHref}>
              {successActionLabel}
            </AuthConnectButton>
          )}
        </div>
      ) : (
        <FlowChallengeForm
          challenge={challenge}
          component={component}
          submitting={submitting}
          fieldErrors={validationErrors}
          flowSlug={slug}
          onSubmit={handleSubmit}
        />
      )}
    </AuthCard>
  )
}

export { AUTH_FLOW_SLUGS }
