/**
 * LoginForm — credential login form.
 *
 * Composes Field + Input + Button + Alert primitives.  Calls the auth API
 * via the manager exposed by useAuth (manager.fetchWithAuth for the login
 * endpoint), then stores tokens with manager.setTokens and triggers a
 * snapshot refresh.
 *
 * No global Provider required — each instance is self-contained.
 *
 * ARIA: <form> element, labels associated to inputs via Field, aria-invalid
 * set on inputs when an error is present.
 */
import * as React from 'react'
import { useState, useId } from 'react'

import { Button } from '../primitives/button'
import { Input } from '../primitives/input'
import { Field } from '../primitives/field'
import { Alert, AlertTitle, AlertDescription } from '../primitives/alert'
import { useAuth } from '../hooks/useAuth'
import { usePasskey } from '../hooks/usePasskey'
import type { AuthConfig, UserInfo } from '../hooks/useAuth'

export interface LoginFormProps {
  /** Optional AuthConfig — when omitted the default shared manager is used. */
  authConfig?: AuthConfig
  /** Label for the email input */
  emailLabel?: string
  /** Label for the password input */
  passwordLabel?: string
  /** Label for the submit button */
  submitLabel?: string
  /** Label for the passkey button */
  passkeyLabel?: string
  /** Label for the forgot password link */
  forgotPasswordLabel?: string
  /** href for the "Forgot password?" link — renders as a Button asChild link */
  forgotPasswordHref?: string
  /** Called after a successful login with the decoded user info */
  onSuccess?: (user: UserInfo) => void
}

export function LoginForm({
  authConfig,
  emailLabel = 'Email',
  passwordLabel = 'Password',
  submitLabel = 'Sign in',
  passkeyLabel = 'Sign in with passkey',
  forgotPasswordLabel = 'Forgot password?',
  forgotPasswordHref,
  onSuccess,
}: LoginFormProps) {
  const { manager, refresh, user } = useAuth(authConfig)
  const { isSupported: passkeySupported } = usePasskey()

  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [pending, setPending] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const emailId = useId()
  const passwordId = useId()
  const errorId = useId()

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault()
    setError(null)
    setPending(true)

    try {
      // POST to the configured auth base path — same pattern as manager.logout().
      const basePath = manager.config.apiBasePath
      const response = await fetch(`${basePath}/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      })

      if (!response.ok) {
        let message = 'Invalid email or password.'
        try {
          const body = (await response.json()) as { detail?: string; message?: string }
          message = body.detail ?? body.message ?? message
        } catch {
          // ignore JSON parse errors
        }
        throw new Error(message)
      }

      const data = (await response.json()) as {
        access_token?: string
        refresh_token?: string
      }

      if (!data.access_token) {
        throw new Error('No access token returned from server.')
      }

      manager.setTokens(data.access_token, data.refresh_token)
      refresh()

      // Re-read user from manager after token is stored.
      const token = manager.getAccessToken()
      const payload = token ? manager.parseToken(token) : null
      const loggedInUser: UserInfo = payload
        ? {
            id: payload.sub,
            username: payload.sub,
            email,
            roles: payload.roles ?? [],
            groups: payload.groups ?? [],
            is_active: true,
            is_verified: true,
          }
        : (user ?? {
            id: '',
            username: '',
            email,
            roles: [],
            groups: [],
            is_active: true,
            is_verified: true,
          })

      onSuccess?.(loggedInUser)
    } catch (err: unknown) {
      const message = err instanceof Error ? err.message : 'An unexpected error occurred.'
      setError(message)
    } finally {
      setPending(false)
    }
  }

  const hasError = error !== null

  return (
    <form
      onSubmit={e => void handleSubmit(e)}
      noValidate
      aria-label="Sign in"
      className="flex w-full flex-col gap-5"
    >
      {hasError && (
        <Alert variant="error" id={errorId} role="alert" aria-live="polite">
          <AlertTitle>Sign in failed</AlertTitle>
          <AlertDescription>{error}</AlertDescription>
        </Alert>
      )}

      <Field label={emailLabel} htmlFor={emailId} required {...(hasError ? { error: ' ' } : {})}>
        <Input
          id={emailId}
          type="email"
          autoComplete="email"
          value={email}
          onChange={e => {
            setEmail(e.currentTarget.value)
          }}
          disabled={pending}
          error={hasError}
          {...(hasError ? { errorMessageId: errorId } : {})}
          required
          placeholder="you@example.com"
        />
      </Field>

      <Field
        label={passwordLabel}
        htmlFor={passwordId}
        required
        {...(hasError ? { error: ' ' } : {})}
      >
        <Input
          id={passwordId}
          type="password"
          autoComplete="current-password"
          value={password}
          onChange={e => {
            setPassword(e.currentTarget.value)
          }}
          disabled={pending}
          error={hasError}
          {...(hasError ? { errorMessageId: errorId } : {})}
          required
        />
      </Field>

      <div className="flex flex-col gap-2">
        <Button variant="default" type="submit" loading={pending} className="w-full">
          {submitLabel}
        </Button>

        {passkeySupported && (
          <Button variant="secondary" type="button" disabled={pending} className="w-full">
            {passkeyLabel}
          </Button>
        )}

        {forgotPasswordHref && (
          <Button variant="link" size="sm" type="button" asChild className="self-end">
            <a href={forgotPasswordHref}>{forgotPasswordLabel}</a>
          </Button>
        )}
      </div>
    </form>
  )
}
