"use client"

import { useCallback, useEffect, useRef } from 'react';

import { AUTH_CONSTANTS } from '../constants';
import { useAuth } from '../context';
import { authLogger } from '../utils/logger';
import { useAuthFormState } from './useAuthFormState';
import { useAuthValidation } from './useAuthValidation';
import { useAutoAuth } from './useAutoAuth';
import { useTwoFactorVerify } from './useTwoFactorVerify';

import type { AuthFormReturn, UseAuthFormOptions } from '../types';
/**
 * Complete auth form hook.
 * Composes smaller hooks for state, validation, and submission.
 */
export const useAuthForm = (options: UseAuthFormOptions): AuthFormReturn => {
  const {
    onIdentifierSuccess,
    onOTPSuccess,
    onError,
    sourceUrl,
    redirectUrl,
    requireTermsAcceptance = false,
    authPath = '/auth',
  } = options;

  // Compose smaller hooks
  const formState = useAuthFormState();
  const validation = useAuthValidation();

  // Ref to track auto-submit from URL
  const isAutoSubmitFromUrlRef = useRef(false);
  // Remembers the exact `identifier:otp` we already attempted from a magic-link
  // URL. We verify a given code at most ONCE — even if it turns out invalid or
  // expired — so re-renders never re-fire the request. Without this, the auto
  // submit loops and hammers /verify/ until the backend returns 429.
  const processedOtpKeyRef = useRef<string | null>(null);

  // Auth context for API calls and storage
  const {
    requestOTP,
    verifyOTP,
    getSavedEmail,
    saveEmail,
  } = useAuth();

  // Destructure for convenience
  const {
    identifier,
    otp,
    isLoading,
    acceptedTerms,
    twoFactorSessionId,
    twoFactorCode,
    useBackupCode,
    setIdentifier,
    setOtp,
    setStep,
    setError,
    setIsLoading,
    clearError,
    setTwoFactorSessionId,
    setShouldPrompt2FA,
    setTwoFactorCode,
    setUseBackupCode,
    startRateLimitCountdown,
    setWebmail,
  } = formState;

  // 2FA verification hook - skip redirect so we can show success screen
  const twoFactor = useTwoFactorVerify({
    onSuccess: () => {
      authLogger.info('2FA verification successful, showing success screen');
      setStep('success');
      onOTPSuccess?.();
    },
    onError: (error) => {
      setError(error);
      onError?.(error);
    },
    redirectUrl,
    skipRedirect: true, // We handle navigation via success step
  });

  const { validateIdentifier } = validation;

  // ─────────────────────────────────────────────────────────────────────────
  // Storage Helper
  // ─────────────────────────────────────────────────────────────────────────

  const saveIdentifierToStorage = useCallback((id: string) => {
    saveEmail(id);
  }, [saveEmail]);

  // ─────────────────────────────────────────────────────────────────────────
  // Effects
  // ─────────────────────────────────────────────────────────────────────────

  // Load saved identifier on mount
  useEffect(() => {
    const savedEmail = getSavedEmail();
    if (savedEmail) {
      setIdentifier(savedEmail);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // ─────────────────────────────────────────────────────────────────────────
  // Submit Handlers
  // ─────────────────────────────────────────────────────────────────────────

  const handleIdentifierSubmit = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();

    if (!identifier) {
      const msg = 'Please enter your email address';
      setError(msg);
      onError?.(msg);
      return;
    }

    if (!validateIdentifier(identifier)) {
      const msg = 'Please enter a valid email address';
      setError(msg);
      onError?.(msg);
      return;
    }

    if (requireTermsAcceptance && !acceptedTerms) {
      const msg = 'Please accept the Terms of Service and Privacy Policy';
      setError(msg);
      onError?.(msg);
      return;
    }

    setIsLoading(true);
    clearError();

    try {
      const result = await requestOTP(identifier, sourceUrl);

      if (result.success) {
        saveIdentifierToStorage(identifier);
        setWebmail(result.webmail ?? null);
        setStep('otp');
        onIdentifierSuccess?.(identifier);
      } else {
        if (result.retryAfter) {
          startRateLimitCountdown(result.retryAfter);
          clearError();
        } else {
          setError(result.message);
          onError?.(result.message);
        }
      }
    } catch {
      const msg = 'An unexpected error occurred';
      setError(msg);
      onError?.(msg);
    } finally {
      setIsLoading(false);
    }
  }, [
    identifier, acceptedTerms, requireTermsAcceptance,
    validateIdentifier, requestOTP, saveIdentifierToStorage,
    setError, setIsLoading, setStep, clearError, setWebmail,
    startRateLimitCountdown, onIdentifierSuccess, onError, sourceUrl,
  ]);

  // Core OTP submit - accepts explicit values to avoid stale closures
  const submitOTP = useCallback(async (
    submitIdentifier: string,
    submitOtp: string,
  ): Promise<boolean> => {
    if (!submitOtp || submitOtp.length < AUTH_CONSTANTS.EMAIL_OTP_LENGTH) {
      const msg = `Please enter the ${AUTH_CONSTANTS.EMAIL_OTP_LENGTH}-digit verification code`;
      setError(msg);
      onError?.(msg);
      return false;
    }

    setIsLoading(true);
    clearError();

    try {
      // Skip redirect - we'll show success screen first
      const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true);

      // Check if 2FA is required (user has TOTP device)
      if (result.requires_2fa && result.session_id) {
        authLogger.info('2FA required after OTP verification');
        setTwoFactorSessionId(result.session_id);
        setShouldPrompt2FA(result.should_prompt_2fa || false);
        setStep('2fa');
        saveIdentifierToStorage(submitIdentifier);
        return true; // OTP was successful, now need 2FA
      }

      if (result.success) {
        saveIdentifierToStorage(submitIdentifier);

        // Sign-in only verifies identity. Setting up 2FA is owned by
        // ProfileLayout, so we never branch into a setup prompt here — the
        // backend's `should_prompt_2fa` hint is intentionally ignored.
        authLogger.info('OTP verification successful, showing success screen');
        setStep('success');
        onOTPSuccess?.();
        return true;
      } else {
        setError(result.message);
        onError?.(result.message);
        return false;
      }
    } catch {
      const msg = 'An unexpected error occurred';
      setError(msg);
      onError?.(msg);
      return false;
    } finally {
      setIsLoading(false);
    }
  }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);

  const handleOTPSubmit = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    await submitOTP(identifier, otp);
  }, [identifier, otp, submitOTP]);

  const handleResendOTP = useCallback(async () => {
    setIsLoading(true);
    clearError();

    try {
      const result = await requestOTP(identifier, sourceUrl);

      if (result.success) {
        saveIdentifierToStorage(identifier);
        setWebmail(result.webmail ?? null);
        setOtp('');
      } else {
        if (result.retryAfter) {
          startRateLimitCountdown(result.retryAfter);
          clearError(); // countdown in rateLimitLabel is enough feedback
        } else {
          setError(result.message);
          onError?.(result.message);
        }
      }
    } catch {
      const msg = 'Failed to resend verification code';
      setError(msg);
      onError?.(msg);
    } finally {
      setIsLoading(false);
    }
  }, [identifier, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, startRateLimitCountdown, setWebmail, onError, sourceUrl]);

  const handleBackToIdentifier = useCallback(() => {
    setStep('identifier');
    clearError();
  }, [setStep, clearError]);

  const forceOTPStep = useCallback(() => {
    setStep('otp');
    clearError();
  }, [setStep, clearError]);

  // ─────────────────────────────────────────────────────────────────────────
  // 2FA Handlers
  // ─────────────────────────────────────────────────────────────────────────

  const handle2FASubmit = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();

    if (!twoFactorSessionId) {
      const msg = 'Missing 2FA session';
      setError(msg);
      onError?.(msg);
      return;
    }

    if (useBackupCode) {
      await twoFactor.verifyBackupCode(twoFactorSessionId, twoFactorCode);
    } else {
      await twoFactor.verifyTOTP(twoFactorSessionId, twoFactorCode);
    }
  }, [twoFactorSessionId, twoFactorCode, useBackupCode, twoFactor, setError, onError]);

  const handleUseBackupCode = useCallback(() => {
    setUseBackupCode(true);
    setTwoFactorCode('');
    clearError();
  }, [setUseBackupCode, setTwoFactorCode, clearError]);

  const handleUseTOTP = useCallback(() => {
    setUseBackupCode(false);
    setTwoFactorCode('');
    clearError();
  }, [setUseBackupCode, setTwoFactorCode, clearError]);

  // ─────────────────────────────────────────────────────────────────────────
  // Auto-auth from URL
  // ─────────────────────────────────────────────────────────────────────────

  const handleOTPFromUrl = useCallback((detectedOtp: string, detectedEmail?: string) => {
    // Prefer the email from the magic-link URL (works on a fresh browser/device),
    // fall back to the one saved from a previous request on this browser.
    const autoIdentifier = detectedEmail || getSavedEmail() || '';

    if (!autoIdentifier) {
      authLogger.warn('No identifier found for auto-submit (no email in URL or storage)');
      return;
    }

    // Verify each magic-link code at most once. A re-render must never re-fire
    // the request — a failed/expired code that retried in a loop is exactly what
    // drove the /verify/ spam and the backend's 429 lockout.
    const otpKey = `${autoIdentifier}:${detectedOtp}`;
    if (processedOtpKeyRef.current === otpKey) return;
    if (isAutoSubmitFromUrlRef.current) return;

    processedOtpKeyRef.current = otpKey;
    isAutoSubmitFromUrlRef.current = true;

    authLogger.info('OTP detected from URL, auto-submitting');

    // Update UI state
    setIdentifier(autoIdentifier);
    setOtp(detectedOtp);
    setStep('otp');

    // Submit with explicit values
    setTimeout(async () => {
      try {
        await submitOTP(autoIdentifier, detectedOtp);
      } finally {
        isAutoSubmitFromUrlRef.current = false;
      }
    }, 100);
  }, [getSavedEmail, setIdentifier, setOtp, setStep, submitOTP]);

  useAutoAuth({
    allowedPaths: [authPath],
    onOTPDetected: handleOTPFromUrl,
    cleanupUrl: true,
  });

  // ─────────────────────────────────────────────────────────────────────────
  // Return
  // ─────────────────────────────────────────────────────────────────────────

  return {
    // State
    ...formState,

    // Submit handlers
    handleIdentifierSubmit,
    handleOTPSubmit,
    handleResendOTP,
    handleBackToIdentifier,
    forceOTPStep,

    // 2FA handlers
    handle2FASubmit,
    handleUseBackupCode,
    handleUseTOTP,

    // Validation
    ...validation,

    // Auto-submit ref
    isAutoSubmittingFromUrl: isAutoSubmitFromUrlRef,

    // 2FA state from hook (for loading indicator)
    is2FALoading: twoFactor.isLoading,
    twoFactorWarning: twoFactor.warning,
    twoFactorAttemptsRemaining: twoFactor.attemptsRemaining,
  };
};

// Re-export types for convenience
export type { AuthFormReturn, UseAuthFormOptions } from '../types';
