/**
 * OtpLoginShell — phone OTP login orchestrator (FDS §2.1).
 * Routes PhoneStep → CodeStep → SuccessStep; owns Firebase + API + E2E bypass.
 * Supports embedded (no AppShell) + custom onSuccess.
 */

'use client';

import { useCallback, useState, useRef } from 'react';
import { getFirebaseAuth } from '@/lib/firebase.js';
import type { ConfirmationResult } from 'firebase/auth';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useT } from '@/lib/i18n/react';
import { HydratedIsland } from '@/components/HydratedIsland';
import { AppShell } from '@/components/ui/layout/AppShell';
import { SiteNav } from '@/components/ui/layout/SiteNav';
import { InlineNotice } from '@/components/ui/feedback/InlineNotice';
import {
  ToastProvider,
  ToastViewport,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastClose,
} 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 { buildE2eProofHeaders } from '@/lib/security/e2e-proof';
import { broadcastMhEvent } from '@/lib/cross-tab-sync';
import { normalizeToE164 } from '@/lib/phone';
import { PhoneStep } from './steps/PhoneStep';
import { CodeStep } from './steps/CodeStep';
import { SuccessStep } from './steps/SuccessStep';

const phoneSchema = z.object({ phone: z.string().min(9, 'Phone number required') });

// Code schema factory accepts i18n error message so it can use translated text.
function codeSchemaFor(invalidCodeMsg: string) {
  return z.object({ code: z.string().regex(/^\d{6}$/, invalidCodeMsg) });
}

type PhoneFormValues = z.infer<typeof phoneSchema>;
type CodeFormValues = { code: string };

export interface OtpLoginShellProps {
  /** URL to redirect to after successful login. @default '/' */
  redirectTo?: string;
  /** Render without AppShell/TopBar wrapper (for in-dialog embedding). */
  embedded?: boolean;
  /** Override default post-verify navigation. */
  onSuccess?: () => void | Promise<void>;
}

// ─── Shell ───────────────────────────────────────────────────────────────────

function OtpLoginShellInner({ redirectTo = '/', embedded = false, onSuccess }: OtpLoginShellProps) {
  const t = useT('auth_flow');
  const tCart = useT('cart');
  const tCommon = useT('common');
  const { toasts, toast, dismiss } = useToast();
  const { mergeFromLocalStorage } = useCartMerge();
  const wishlistMerge = useWishlistMerge();
  const [step, setStep] = useState<'phone' | 'code' | 'success'>('phone');
  const [apiError, setApiError] = useState<string | null>(null);

  const phoneForm = useForm<PhoneFormValues>({ resolver: zodResolver(phoneSchema) });
  const codeForm = useForm<CodeFormValues>({
    resolver: zodResolver(codeSchemaFor(t('invalid_code'))),
  });

  const [countryIso, setCountryIso] = useState('IL');
  const [sendingCode, setSendingCode] = useState(false);
  const [verifyingCode, setVerifyingCode] = useState(false);
  const [confirmationResult, setConfirmationResult] = useState<ConfirmationResult | null>(null);
  const recaptchaContainerRef = useRef<HTMLDivElement>(null);
  const navigateAfterLogin = useCallback(() => {
    window.location.href = redirectTo;
  }, [redirectTo]);

  // E2E bypass (Playwright sets window.__E2E_PHONE_AUTH__ via addInitScript).
  const e2ePhoneAuth =
    typeof window !== 'undefined' &&
    (window as { __E2E_PHONE_AUTH__?: boolean }).__E2E_PHONE_AUTH__ === true;
  const storedPhoneRef = useRef<string>('');

  async function onPhoneSubmit(values: PhoneFormValues) {
    setSendingCode(true);
    setApiError(null);
    try {
      const e164Phone = normalizeToE164(values.phone, countryIso);
      if (e2ePhoneAuth) {
        storedPhoneRef.current = e164Phone;
        setStep('code');
        return;
      }
      const { RecaptchaVerifier, signInWithPhoneNumber } = await import('firebase/auth');
      const auth = await getFirebaseAuth();
      const recaptchaVerifier = new RecaptchaVerifier(auth, recaptchaContainerRef.current!, {
        size: 'invisible',
      });
      const result = await signInWithPhoneNumber(auth, e164Phone, recaptchaVerifier);
      setConfirmationResult(result);
      setStep('code');
    } catch (err) {
      const code = (err as { code?: string }).code ?? '';
      if (code === 'auth/invalid-phone-number') {
        setApiError(t('invalid_phone'));
      } else if (code === 'auth/too-many-requests') {
        setApiError(t('rate_limited'));
      } else {
        setApiError(tCommon('error'));
      }
    } finally {
      setSendingCode(false);
    }
  }

  async function onCodeSubmit(values: CodeFormValues) {
    setVerifyingCode(true);
    setApiError(null);

    let idToken: string;

    if (e2ePhoneAuth) {
      idToken = `E2E:${storedPhoneRef.current}`;
      const url = '/api/auth/firebase-verify';
      const bodyStr = JSON.stringify({ idToken });
      const headers: Record<string, string> = { 'Content-Type': 'application/json' };
      const secret = (window as { __E2E_SECRET__?: string }).__E2E_SECRET__;
      if (secret) {
        Object.assign(headers, await buildE2eProofHeaders(secret, 'POST', url, bodyStr));
      }
      try {
        const res = await fetch(url, {
          method: 'POST',
          headers,
          body: bodyStr,
        });
        const data = (await res.json()) as { ok: boolean; error?: string };
        if (!data.ok) {
          setApiError(data.error ?? tCommon('error'));
          return;
        }
        broadcastMhEvent({ kind: 'login' });
        setStep('success');
        if (onSuccess) {
          await onSuccess();
        } else {
          sessionStorage.setItem('mh:freshLogin', '1');
          navigateAfterLogin();
        }
      } catch (err) {
        captureCaught(err, { scope: 'features.auth-flow.OtpLogin.e2e', severity: 'warning' });
        setApiError(tCommon('error'));
      } finally {
        setVerifyingCode(false);
      }
      return;
    }

    if (!confirmationResult) {
      setApiError(tCommon('error'));
      setVerifyingCode(false);
      return;
    }
    try {
      const userCredential = await confirmationResult.confirm(values.code);
      idToken = await userCredential.user.getIdToken();

      const res = await fetch('/api/auth/firebase-verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ idToken }),
      });
      const data = (await res.json()) as { ok: boolean; error?: string };
      if (!data.ok) {
        setApiError(data.error ?? tCommon('error'));
        return;
      }
      try {
        const mergeResult = await mergeFromLocalStorage();
        if (mergeResult.merged.length > 0 || mergeResult.removed.length > 0)
          toast({ title: tCart('mergeToast'), tone: 'success' });
        if (mergeResult.removed.length > 0)
          toast({ title: tCart('mergeRemovedToast'), tone: 'warning' });
      } catch (err) {
        captureCaught(err, { scope: 'features.auth-flow.OtpLogin', severity: 'warning' });
      }
      await wishlistMerge.run();
      broadcastMhEvent({ kind: 'login' });
      setStep('success');
      if (onSuccess) {
        await onSuccess();
      } else {
        sessionStorage.setItem('mh:freshLogin', '1');
        navigateAfterLogin();
      }
    } catch (err) {
      const code = (err as { code?: string }).code ?? '';
      if (code === 'auth/code-expired') {
        setApiError(t('otp_expired'));
      } else if (code === 'auth/invalid-verification-code') {
        setApiError(t('invalid_code'));
      } else {
        setApiError(tCommon('error'));
      }
    } finally {
      setVerifyingCode(false);
    }
  }

  function onCodeBack() {
    setStep('phone');
    setApiError(null);
    codeForm.reset();
  }

  const body = (
    <div className="flex flex-col gap-6">
      {!embedded && (
        <h1 className="text-text-primary text-center text-[length:var(--font-size-display)] leading-tight font-[var(--font-weight-extrabold)]">
          {t('login_title')}
        </h1>
      )}

      {apiError && <InlineNotice tone="danger" description={apiError} />}

      {step === 'success' && <SuccessStep />}

      {step === 'phone' && (
        <PhoneStep
          form={phoneForm}
          onSubmit={onPhoneSubmit}
          sending={sendingCode}
          recaptchaContainerRef={recaptchaContainerRef}
          countryCode={countryIso}
          onCountryCodeChange={setCountryIso}
        />
      )}

      {step === 'code' && (
        <CodeStep
          form={codeForm}
          onSubmit={onCodeSubmit}
          verifying={verifyingCode}
          onBack={onCodeBack}
        />
      )}
    </div>
  );

  const toastNodes = (
    <>
      {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 />
    </>
  );

  if (embedded) {
    return (
      <>
        {body}
        <WishlistMergeDialog {...wishlistMerge.dialogProps} />
        {toastNodes}
      </>
    );
  }

  return (
    <>
      <AppShell
        mode="customer"
        topBar={
          <SiteNav variant="mobile" title={t('login_title')} currentPath="/login" isGuest={true} />
        }
      >
        <div className="mx-auto max-w-sm px-4 py-10">{body}</div>
      </AppShell>
      <WishlistMergeDialog {...wishlistMerge.dialogProps} />
      {toastNodes}
    </>
  );
}

export function OtpLoginShell(props: OtpLoginShellProps) {
  return (
    <HydratedIsland>
      <ToastProvider>
        <OtpLoginShellInner {...props} />
      </ToastProvider>
    </HydratedIsland>
  );
}
