/**
 * contexts/AuthContext.tsx
 * Single source of truth for Firebase auth state.
 * Wraps the entire app — only ONE onAuthStateChanged listener exists.
 */

"use client";

import {
  createContext,
  useContext,
  useState,
  useEffect,
  useCallback,
  type ReactNode,
} from "react";
import {
  onAuthStateChanged,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  sendPasswordResetEmail,
  signOut,
  type User as FirebaseUser,
} from "firebase/auth";
import { auth } from "@/lib/firebase";
import axios from "axios";
import type { IUser, LoginPayload, RegisterPayload } from "@/types";

/** Maps Firebase / API error codes to user-friendly toast messages. */
function friendlyAuthError(err: unknown): string {
  // 1. Check for Axios error response first (contains our custom API messages)
  if (axios.isAxiosError(err) && err.response?.data?.error) {
    return err.response.data.error;
  }

  if (err instanceof Error) {
    const code = (err as any).code as string | undefined;

    // Firebase client-side error codes
    if (code === "auth/email-already-in-use")
      return "An account with this email already exists. Please sign in instead.";
    if (code === "auth/user-not-found")
      return "No account found with this email address.";
    if (code === "auth/invalid-email")
      return "Please enter a valid email address.";
    if (code === "auth/missing-email")
      return "Please enter your email address.";
    if (code === "auth/wrong-password" || code === "auth/invalid-credential")
      return "Incorrect password. Please try again.";
    if (code === "auth/too-many-requests")
      return "Too many attempts. Server is busy — please try again in a few minutes.";
    if (code === "auth/network-request-failed")
      return "Network error. Please check your connection and try again.";

    // API / DB errors forwarded as plain messages (fallback if not Axios)
    const msg = err.message;
    if (msg.includes("Email or User ID already exists") || msg.includes("already exists"))
      return "An account with this email already exists. Please sign in instead.";
    if (msg.includes("User profile not found") || msg.includes("not found"))
      return "No account found with this email. Please register first.";
    if (msg.includes("Database connection error") || msg.includes("503") || msg.includes("busy") || msg.includes("timeout"))
      return "Server is busy. Please try again in a few moments.";

    return msg || "Something went wrong. Please try again.";
  }
  return "Something went wrong. Please try again.";
}

interface AuthState {
  firebaseUser: FirebaseUser | null;
  user: IUser | null;
  loading: boolean;
  error: string | null;
}

interface AuthContextValue extends AuthState {
  isAuthenticated: boolean;
  login: (payload: LoginPayload) => Promise<IUser>;
  register: (payload: RegisterPayload) => Promise<IUser>;
  forgotPassword: (email: string) => Promise<void>;
  logout: () => Promise<void>;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [state, setState] = useState<AuthState>({
    firebaseUser: null,
    user: null,
    loading: true,
    error: null,
  });

  // Single global listener — only runs once on mount
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
      if (firebaseUser) {
        try {
          const idToken = await firebaseUser.getIdToken();
          const { data } = await axios.post("/api/auth/login", { idToken });
          setState({
            firebaseUser,
            user: data.data,
            loading: false,
            error: null,
          });
        } catch {
          // Firebase user exists but no MongoDB profile (or DB error)
          setState({
            firebaseUser,
            user: null,
            loading: false,
            error: null,
          });
        }
      } else {
        setState({
          firebaseUser: null,
          user: null,
          loading: false,
          error: null,
        });
      }
    });

    return () => unsubscribe();
  }, []);

  const login = useCallback(
    async ({ email, password }: LoginPayload): Promise<IUser> => {
      setState((s) => ({ ...s, loading: true, error: null }));
      try {
        const credential = await signInWithEmailAndPassword(
          auth,
          email,
          password,
        );
        const idToken = await credential.user.getIdToken();
        const { data } = await axios.post("/api/auth/login", { idToken });
        setState({
          firebaseUser: credential.user,
          user: data.data,
          loading: false,
          error: null,
        });
        return data.data as IUser;
      } catch (err) {
        const error = friendlyAuthError(err);
        setState((s) => ({ ...s, loading: false, error }));
        throw new Error(error);
      }
    },
    [],
  );

  const register = useCallback(
    async (payload: RegisterPayload): Promise<IUser> => {
      setState((s) => ({ ...s, loading: true, error: null }));
      try {
        const credential = await createUserWithEmailAndPassword(
          auth,
          payload.email,
          payload.password,
        );
        const idToken = await credential.user.getIdToken();
        const { data } = await axios.post("/api/auth/register", {
          idToken,
          name: payload.name,
          email: payload.email,
          phone: payload.phone,
          referralCode: payload.referralCode,
        });
        setState({
          firebaseUser: credential.user,
          user: data.data,
          loading: false,
          error: null,
        });
        return data.data as IUser;
      } catch (err) {
        const error = friendlyAuthError(err);
        setState((s) => ({ ...s, loading: false, error }));
        throw new Error(error);
      }
    },
    [],
  );

  const forgotPassword = useCallback(async (email: string): Promise<void> => {
    try {
      await sendPasswordResetEmail(auth, email.trim());
    } catch (err) {
      const error = friendlyAuthError(err);
      throw new Error(error);
    }
  }, []);

  const logout = useCallback(async () => {
    await signOut(auth);
    try {
      await axios.delete("/api/auth/me");
    } catch {
      // ignore — cookie cleanup best-effort
    }
    setState({ firebaseUser: null, user: null, loading: false, error: null });
  }, []);

  const value: AuthContextValue = {
    ...state,
    isAuthenticated: !!state.firebaseUser,
    login,
    register,
    forgotPassword,
    logout,
  };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) {
    throw new Error("useAuth must be used within <AuthProvider>");
  }
  return ctx;
}
