"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useState,
  ReactNode,
} from "react";
import {
  apiFetch,
  authLogin,
  authLogout,
  authRegister,
  clearTokens,
  fetchMe,
  getAccessToken,
  getRefreshToken,
  setTokens,
  ACCESS_TOKEN_KEY,
  REFRESH_TOKEN_KEY,
  SESSION_ID_KEY,
} from "@/lib/api";

export interface AuthUser {
  user_id: string;
  email: string;
  username?: string | null;
  full_name: string | null;
  [key: string]: any;
}

interface AuthState {
  user: AuthUser | null;
  isAuthenticated: boolean;
  isLoading: boolean;
}

interface AuthContextValue extends AuthState {
  login: (
    email: string,
    password: string,
    mfaCode?: string,
  ) => Promise<{ mfaRequired?: boolean }>;
  register: (payload: {
    email: string;
    password: string;
    password_confirm: string;
    full_name?: string;
    username?: string;
  }) => Promise<void>;
  logout: () => Promise<void>;
  refreshUser: () => Promise<void>;
  apiFetch: typeof apiFetch;
}

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [state, setState] = useState<AuthState>({
    user: null,
    isAuthenticated: false,
    isLoading: true,
  });

  const refreshUser = useCallback(async () => {
    const user = await fetchMe();
    if (user) {
      setState((prev) => ({
        ...prev,
        user,
        isAuthenticated: true,
        isLoading: false,
      }));
    } else {
      clearTokens();
      setState({ user: null, isAuthenticated: false, isLoading: false });
    }
  }, []);

  // Bootstrap: if a token exists in storage, load the current user.
  useEffect(() => {
    let active = true;
    if (typeof window === "undefined") {
      setState((prev) => ({ ...prev, isLoading: false }));
      return;
    }
    if (getAccessToken()) {
      fetchMe().then((user) => {
        if (!active) return;
        if (user) {
          setState({ user, isAuthenticated: true, isLoading: false });
        } else {
          clearTokens();
          setState({ user: null, isAuthenticated: false, isLoading: false });
        }
      });
    } else {
      setState((prev) => ({ ...prev, isLoading: false }));
    }
    return () => {
      active = false;
    };
  }, []);

  const login = useCallback(
    async (email: string, password: string, mfaCode?: string) => {
      const { ok, data } = await authLogin(email, password, mfaCode);
      if (!ok) {
        if (data?.mfa_required) {
          return { mfaRequired: true };
        }
        const err = new Error(data?.detail || "Login failed") as any;
        err.status = data?.status;
        throw err;
      }
      setTokens(data.access, data.refresh, data.session_id);
      const user = await fetchMe();
      setState({
        user: user ?? null,
        isAuthenticated: true,
        isLoading: false,
      });
      return {};
    },
    [],
  );

  const register = useCallback(
    async (payload: {
      email: string;
      password: string;
      password_confirm: string;
      full_name?: string;
      username?: string;
    }) => {
      const { ok, data } = await authRegister(payload);
      if (!ok) {
        const err = new Error(data?.detail || "Registration failed") as any;
        err.errors = data;
        err.status = data?.status;
        throw err;
      }
      setTokens(data.access, data.refresh, data.session_id);
      setState({
        user: data.user ?? null,
        isAuthenticated: true,
        isLoading: false,
      });
    },
    [],
  );

  const logout = useCallback(async () => {
    await authLogout(getRefreshToken());
    clearTokens();
    setState({ user: null, isAuthenticated: false, isLoading: false });
  }, []);

  const value: AuthContextValue = {
    ...state,
    login,
    register,
    logout,
    refreshUser,
    apiFetch,
  };

  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 an AuthProvider");
  }
  return ctx;
}

export { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY, SESSION_ID_KEY };
