import {
  ReactNode,
  createContext,
  useContext,
  useEffect,
  useState,
} from "react";
import * as WebBrowser from "expo-web-browser";

import { getApiUrl, baseApi } from "@/store/api/baseApi";
import { getToken, setToken, loadPersistedToken } from "@/auth/token";
import { store } from "@/store/index";
import { User } from "@/store/models/User.model";

WebBrowser.maybeCompleteAuthSession();

type AuthContextType = {
  user: User | null;
  token: string | null;
  loading: boolean;
  bootstrapping: boolean;
  isAuthenticated: boolean;
  login: (email: string, password: string) => Promise<void>;
  register: (p: {
    firstName: string;
    lastName: string;
    email: string;
    password: string;
  }) => Promise<void>;
  logout: () => Promise<void>;
};

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

export const useAuthContext = () => {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuthContext must be used within AuthProvider");
  return ctx;
};

export type AuthProviderProps = {
  children: ReactNode;
};

export const AuthProvider = ({ children }: AuthProviderProps) => {
  const [user, setUser] = useState<User | null>(null);
  const [tokenState, setTokenState] = useState<string | null>(getToken());
  const [bootstrapping, setBootstrapping] = useState(true);
  const [loading, setLoading] = useState(false);

  const isAuthenticated = !!tokenState && !!user;

  const syncUser = async (jwt?: string | null) => {
    const useToken = jwt ?? tokenState;
    if (!useToken) return;
    try {
      const res = await fetch(`${getApiUrl()?.replace(/\/$/, "")}/auth/me`, {
        headers: { Authorization: `Bearer ${useToken}` },
      });
      if (!res.ok) throw new Error("Failed to fetch current user");
      const u: User = await res.json();
      setUser(u);
    } catch (e) {
      console.warn("/auth/me failed:", e);
    }
  };

  useEffect(() => {
    (async () => {
      try {
        const t = await loadPersistedToken();
        setTokenState(t);
        if (t) await syncUser(t);
      } finally {
        setBootstrapping(false);
      }
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const login: AuthContextType["login"] = async (email, password) => {
    setLoading(true);
    try {
      const res = await fetch(`${getApiUrl()?.replace(/\/$/, "")}/auth/login`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });
      if (!res.ok) {
        const status = res.status;
        let msg = "Invalid credentials";
        try {
          const errData = await res.json();
          msg = errData?.message || errData?.error || msg;
        } catch {
          // non-JSON response, keep default msg
        }
        // Only replace the message for explicit 'not activated' cases
        if (status === 403 && /not activated/i.test(msg)) {
          msg =
            "Account is not activated. Please use your invitation link to set a password.";
        }
        throw new Error(msg);
      }
      const data = await res.json();
      await setToken(data.token);
      setTokenState(data.token);
      setUser(data.user);
      // Clear/refresh caches after login for correct user context
      store.dispatch(baseApi.util.resetApiState());
      store.dispatch(baseApi.util.invalidateTags(["Users", "Todos"]));
    } finally {
      setLoading(false);
    }
  };

  const register: AuthContextType["register"] = async ({
    firstName,
    lastName,
    email,
    password,
  }) => {
    setLoading(true);
    try {
      const res = await fetch(
        `${getApiUrl()?.replace(/\/$/, "")}/auth/register`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ firstName, lastName, email, password }),
        }
      );
      if (!res.ok) throw new Error("Registration failed");
      const data = await res.json();
      await setToken(data.token);
      setTokenState(data.token);
      setUser(data.user);
      // Invalidate users cache to refresh activation statuses after register
      store.dispatch(baseApi.util.invalidateTags(["Users"]));
    } finally {
      setLoading(false);
    }
  };

  const logout: AuthContextType["logout"] = async () => {
    await setToken(null);
    setTokenState(null);
    setUser(null);
    // Clear RTK Query caches on logout to prevent stale cross-user data
    store.dispatch(baseApi.util.resetApiState());
  };

  return (
    <AuthContext.Provider
      value={{
        user,
        token: tokenState,
        loading,
        bootstrapping,
        isAuthenticated,
        login,
        register,
        logout,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};
