/**
 * LoginPage - email/password login with phone OTP as alternate option.
 *
 * Default view: phone OTP (FDS §2.1 primary path) via Firebase phone auth.
 * Alternate: email + password (login or register).
 *
 * Endpoints:
 *   POST /api/auth/firebase-verify - verify Firebase ID token + issue session
 *   POST /api/auth/login-email     - email + password login
 *   POST /api/auth/register        - email + password registration
 *   POST /api/auth/magic-link/send - magic-link request
 */

'use client';

import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useT } from '@/lib/i18n/react';
import type { Locale } from '@/lib/i18n';
import { HydratedIsland } from '@/components/HydratedIsland';
import { AppShell } from '@/components/ui/layout/AppShell';
import { Container } from '@/components/ui/layout/Container';
import { SiteNav } from '@/components/ui/layout/SiteNav';
import { BottomNav, useCustomerNavItems } from '@/components/ui/layout/BottomNav';
import { Button } from '@/components/ui/primitives/Button';
import { FormField } from '@/components/ui/primitives/FormField';
import { Input } from '@/components/ui/primitives/Input';
import { Turnstile, type TurnstileHandle } from '@/components/ui/primitives/Turnstile';
import { InlineNotice } from '@/components/ui/feedback/InlineNotice';
import { Icon } from '@/components/ui/icons/Icon';
import {
  ToastProvider,
  ToastViewport,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastClose,
  type ToastTone,
} from '@/components/ui/overlays/Toast';
import { useToast } from '@/components/ui/overlays/Toast/useToast';
import { useCartMerge } from '@/features/cart/useCartMerge';
import { useWishlistMerge } from '@/features/wishlist/useWishlistMerge';
import { WishlistMergeDialog } from '@/features/wishlist/WishlistMergeDialog';
import { captureCaught } from '@/lib/observability';
import { broadcastMhEvent } from '@/lib/cross-tab-sync';
import { OtpLoginShell } from '@/features/auth-flow/OtpLoginShell';

// ─── Types ───────────────────────────────────────────────────────────────────

type ToastFn = (p: {
  title: string;
  tone?: ToastTone;
  duration?: number;
  description?: string;
}) => void;

// ─── Schemas ─────────────────────────────────────────────────────────────────

function emailLoginSchemaFor(invalidEmailMsg: string) {
  return z.object({
    email: z
      .string()
      .trim()
      .pipe(z.email({ error: invalidEmailMsg })),
    password: z.string().min(1, 'Password required'),
  });
}

type EmailLoginValues = { email: string; password: string };

// ─── Props ────────────────────────────────────────────────────────────────────

export interface LoginPageProps {
  redirectTo?: string;
  /** Server-resolved locale — threads to HydratedIsland so SSR + first hydration render use correct locale. */
  initialLocale?: Locale;
  /** Server-resolved initial method — skips hydration race on tab toggle. */
  initialMethod?: 'phone' | 'email';
}

// ─── Email/Password form ──────────────────────────────────────────────────────

function EmailPasswordForm({
  redirectTo,
  onUsePhone,
  toast,
}: {
  redirectTo: string;
  onUsePhone: () => void;
  toast: ToastFn;
}) {
  const t = useT('auth_flow');
  const tCart = useT('cart');
  const tCommon = useT('common');
  const { mergeFromLocalStorage } = useCartMerge();
  const wishlistMerge = useWishlistMerge();
  const [apiError, setApiError] = useState<string | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [success, setSuccess] = useState(false);

  const loginForm = useForm<EmailLoginValues>({
    resolver: zodResolver(emailLoginSchemaFor(t('invalid_email'))),
    mode: 'onBlur',
  });
  const [magicLinkSent, setMagicLinkSent] = useState(false);
  const [magicLinkSending, setMagicLinkSending] = useState(false);

  // Cloudflare Turnstile token — single-use, gates both login + magic-link.
  // Submit is blocked until a token is present; consumed tokens are cleared and
  // the widget reset (TTL ~300s, server verifies exactly once).
  const turnstileRef = useRef<TurnstileHandle>(null);
  const [turnstileToken, setTurnstileToken] = useState<string | null>(null);

  // Reset the widget + clear the held token after any submit that consumes it
  // (success or a 403 TURNSTILE_FAILED). The token is never reused.
  function resetTurnstile() {
    turnstileRef.current?.reset();
    setTurnstileToken(null);
  }

  // Programmatic mount focus (jsx-a11y/no-autofocus) — `EmailPasswordForm`
  // mounts when the user picks the email/password method, so a mount-time
  // effect matches the prior `autoFocus` observable behavior.
  const emailInputRef = useRef<HTMLInputElement | null>(null);
  useEffect(() => {
    emailInputRef.current?.focus();
  }, []);
  const emailRegister = loginForm.register('email');

  // Shared single-use token: while either action is in flight, disable BOTH so
  // the same token can't be sent to two endpoints (second siteverify = duplicate).
  const isBusy = submitting || magicLinkSending;

  async function onMagicLinkRequest() {
    const email = loginForm.getValues('email');
    if (!email) {
      loginForm.setError('email', { message: t('invalid_email') });
      return;
    }
    if (!turnstileToken) return; // gated: button is disabled until a token exists
    setMagicLinkSending(true);
    setApiError(null);
    try {
      const res = await fetch('/api/auth/magic-link/send', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, redirect: redirectTo, turnstileToken }),
      });
      // Anti-enumeration: send always returns 200 { ok: true } — the only
      // failure surfaced here is the Turnstile gate (403 TURNSTILE_FAILED).
      if (res.status === 403) {
        const data = (await res.json().catch((err: unknown) => {
          captureCaught(err, { scope: 'features.auth-flow.LoginPage', severity: 'info' });
          return null;
        })) as { code?: string } | null;
        if (data?.code === 'TURNSTILE_FAILED') {
          resetTurnstile();
          setApiError(t('turnstile_failed'));
          return;
        }
      }
      setMagicLinkSent(true);
    } catch (err) {
      captureCaught(err, { scope: 'features.auth-flow.LoginPage', severity: 'warning' });
    } finally {
      // Single-use token: clear + reset after every attempt so a retry gets a
      // fresh token (success resets too, in case the user re-opens the form).
      resetTurnstile();
      setMagicLinkSending(false);
    }
  }

  async function onLogin(v: EmailLoginValues) {
    if (!turnstileToken) return; // gated: button is disabled until a token exists
    setSubmitting(true);
    setApiError(null);
    try {
      const res = await fetch('/api/auth/login-email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: v.email, password: v.password, turnstileToken }),
      });
      const data = (await res.json()) as { ok: boolean; error?: string; code?: string };
      if (!data.ok) {
        // Turnstile gate rejection — reset the single-use widget so the user can
        // retry with a fresh token; surface a localized message.
        if (res.status === 403 && data.code === 'TURNSTILE_FAILED') {
          resetTurnstile();
          setApiError(t('turnstile_failed'));
          return;
        }
        // Any other failure also consumes the token server-side intent-wise;
        // reset so the next attempt carries a fresh, unused token.
        resetTurnstile();
        setApiError(data.error ?? tCommon('error'));
        return;
      }

      // Merge localStorage cart BEFORE redirect — no-op when local cart is empty
      try {
        const mergeResult = await mergeFromLocalStorage();
        await wishlistMerge.run();
        if (mergeResult.merged.length > 0 || mergeResult.removed.length > 0) {
          toast({ title: tCart('mergeToast'), tone: 'success' });
        }
        if (mergeResult.removed.length > 0) {
          toast({
            title: tCart('mergeToast'),
            description: mergeResult.removed.map((r) => r.dealId).join(', '),
            tone: 'warning',
            duration: 7000,
          });
        }
      } catch (err) {
        captureCaught(err, { scope: 'features.auth-flow.LoginPage', severity: 'warning' });
        // Merge failure must not block login redirect
      }

      broadcastMhEvent({ kind: 'login' });
      setSuccess(true);
      setTimeout(() => {
        window.location.assign(redirectTo);
      }, 600);
    } catch (err) {
      captureCaught(err, { scope: 'features.auth-flow.LoginPage', severity: 'warning' });
      resetTurnstile();
      setApiError(tCommon('error'));
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <>
      {success ? (
        <div role="status" aria-live="polite" className="flex flex-col items-center gap-4 py-8">
          <Icon name="Check" size="xl" color="success" />
          <p className="text-success-700 text-center font-semibold">{t('login_success')}</p>
        </div>
      ) : (
        <div className="flex flex-col gap-4">
          {apiError && <InlineNotice tone="danger" description={apiError} />}

          <form
            onSubmit={(event) => void loginForm.handleSubmit((v) => void onLogin(v))(event)}
            className="flex flex-col gap-4"
            noValidate
          >
            <FormField
              label={t('email_label')}
              required
              error={loginForm.formState.errors.email?.message}
              htmlFor="email-login-email"
            >
              <Input
                id="email-login-email"
                type="email"
                dir="ltr"
                autoComplete="email"
                inputMode="email"
                aria-label={t('email_label')}
                placeholder={t('email_placeholder')}
                {...emailRegister}
                ref={(el) => {
                  emailRegister.ref(el);
                  emailInputRef.current = el;
                }}
              />
            </FormField>

            <FormField
              label={t('password_label')}
              required
              error={loginForm.formState.errors.password?.message}
              htmlFor="email-login-password"
            >
              <Input
                id="email-login-password"
                type="password"
                dir="ltr"
                autoComplete="current-password"
                placeholder={t('password_placeholder')}
                {...loginForm.register('password')}
              />
            </FormField>

            {/* Turnstile gate — blocks login + magic-link until solved. */}
            <Turnstile
              ref={turnstileRef}
              action="login"
              onToken={setTurnstileToken}
              onExpire={() => setTurnstileToken(null)}
              onError={() => setTurnstileToken(null)}
            />

            <Button
              type="submit"
              variant="primary"
              size="lg"
              loading={submitting}
              disabled={!turnstileToken || isBusy}
              iconStart={<Icon name="Mail" size="sm" />}
              className="w-full"
            >
              {t('login_cta')}
            </Button>
          </form>

          {/* Magic-link CTA */}
          {magicLinkSent ? (
            <InlineNotice tone="success" description={t('magic_link_check_email')} />
          ) : (
            <Button
              type="button"
              variant="ghost"
              size="md"
              loading={magicLinkSending}
              disabled={!turnstileToken || isBusy}
              onClick={() => void onMagicLinkRequest()}
              className="w-full"
            >
              {t('magic_link_cta')}
            </Button>
          )}

          {/* Phone OTP as alternate */}
          <div className="border-border-default border-t pt-3 text-center">
            <Button type="button" variant="ghost" size="sm" onClick={onUsePhone}>
              {t('use_phone_instead')}
            </Button>
          </div>
        </div>
      )}
      <WishlistMergeDialog {...wishlistMerge.dialogProps} />
    </>
  );
}

// ─── LoginPageInner ───────────────────────────────────────────────────────────

function LoginPageInner({
  redirectTo = '/',
  initialMethod = 'phone',
  initialLocale,
}: LoginPageProps) {
  const t = useT('auth_flow');
  const tCommon = useT('common');
  const { toasts, toast, dismiss } = useToast();
  // Lazy initializer preserves SSR-resolved `initialMethod` across island remounts.
  // Render-time derived-state synchronization mirrors prop changes into state.
  const [method, setMethod] = useState<'phone' | 'email'>(() => initialMethod);
  const [previousInitialMethod, setPreviousInitialMethod] = useState(initialMethod);
  if (previousInitialMethod !== initialMethod) {
    setPreviousInitialMethod(initialMethod);
    setMethod(initialMethod);
  }
  const navItems = useCustomerNavItems('/login');

  return (
    <AppShell
      mode="customer"
      initialLocale={initialLocale}
      desktopTopBar={<SiteNav variant="desktop" currentPath="/login" isGuest={true} />}
      topBar={
        <SiteNav variant="mobile" title={t('login_title')} currentPath="/login" isGuest={true} />
      }
      bottomNav={<BottomNav mode="customer" items={navItems} />}
    >
      {/* Toast viewport for merge notifications */}
      {toasts.map((item) => (
        <Toast
          key={item.id}
          tone={item.tone}
          duration={item.duration}
          onOpenChange={(open) => {
            if (!open) dismiss(item.id);
          }}
        >
          <div className="flex flex-1 flex-col gap-1">
            <ToastTitle>{item.title}</ToastTitle>
            {item.description && <ToastDescription>{item.description}</ToastDescription>}
          </div>
          <ToastClose />
        </Toast>
      ))}
      <ToastViewport />

      <div>
        {/* Mobile: back link */}
        <div className="flex items-center gap-2 px-4 pt-4 pb-2 lg:hidden">
          <a
            href="/"
            className="text-brand-primary-600 flex items-center gap-1 text-sm hover:underline focus-visible:outline-2 focus-visible:outline-offset-2"
          >
            <Icon name="ChevronRight" size="sm" mirror />
            {tCommon('back')}
          </a>
        </div>

        {/* Card layout */}
        <Container maxWidth="4xl" px="4" className="py-6 lg:px-8 lg:py-10">
          <div className="mx-auto max-w-sm">
            {/* Glass card on desktop, flat on mobile */}
            <div className="bg-surface-base lg:border-glass-border-white lg:bg-surface-glass rounded-xl p-6 lg:border lg:shadow-[var(--shadow-glass)] lg:backdrop-blur-[var(--glass-blur-sm)]">
              {/* Card heading */}
              <h1 className="text-text-primary mb-6 text-center text-[length:var(--font-size-display)] leading-tight font-[var(--font-weight-extrabold)]">
                {t('login_title')}
              </h1>

              {method === 'email' ? (
                <EmailPasswordForm
                  redirectTo={redirectTo}
                  onUsePhone={() => setMethod('phone')}
                  toast={toast}
                />
              ) : (
                <div className="flex flex-col gap-4">
                  <OtpLoginShell embedded redirectTo={redirectTo} />
                  <div className="border-border-default border-t pt-3 text-center">
                    <Button
                      type="button"
                      variant="ghost"
                      size="sm"
                      onClick={() => setMethod('email')}
                    >
                      {t('use_email_instead')}
                    </Button>
                  </div>
                </div>
              )}
            </div>

            {/* Tab-agnostic footer — register link visible regardless of method */}
            <p className="text-text-secondary mt-4 text-center text-sm">
              {t('or_register')}{' '}
              <a
                href="/register"
                className="text-brand-primary-600 font-medium underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2"
              >
                {t('register_link_cta')}
              </a>
            </p>
          </div>
        </Container>
      </div>
    </AppShell>
  );
}

// ─── LoginPage ────────────────────────────────────────────────────────────────

export function LoginPage({ redirectTo = '/', initialLocale, initialMethod }: LoginPageProps) {
  return (
    <HydratedIsland locale={initialLocale}>
      <ToastProvider>
        <LoginPageInner
          redirectTo={redirectTo}
          initialMethod={initialMethod}
          initialLocale={initialLocale}
        />
      </ToastProvider>
    </HydratedIsland>
  );
}
