import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import * as authApi from "@/features/auth/api";
import { getAccessToken, setAccessToken, setRefreshHandler, setUnauthorizedHandler } from "@/shared/api/request";

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [permissions, setPermissions] = useState([]);
  const [loading, setLoading] = useState(true);

  const applySession = useCallback((session) => {
    if (session?.accessToken) {
      setAccessToken(session.accessToken);
    }
    setUser(session?.user || null);
    setPermissions(session?.permissions || []);
  }, []);

  const clearSession = useCallback(() => {
    setAccessToken("");
    setUser(null);
    setPermissions([]);
  }, []);

  const refresh = useCallback(async () => {
    try {
      const session = await authApi.refreshSession();
      applySession(session);
      return true;
    } catch {
      clearSession();
      return false;
    }
  }, [applySession, clearSession]);

  useEffect(() => {
    setRefreshHandler(refresh);
    setUnauthorizedHandler(clearSession);
    return () => {
      setRefreshHandler(null);
      setUnauthorizedHandler(null);
    };
  }, [clearSession, refresh]);

  useEffect(() => {
    let mounted = true;

    async function bootstrap() {
      try {
        if (getAccessToken()) {
          try {
            const session = await authApi.refreshSession();
            if (mounted) {
              applySession(session);
            }
            return;
          } catch {
            // Keep the existing access token as a fallback when the refresh cookie is unavailable.
          }
          try {
            const session = await authApi.getMe();
            if (mounted) {
              applySession(session);
            }
          } catch {
            clearSession();
          }
          return;
        }
        await refresh();
      } finally {
        if (mounted) {
          setLoading(false);
        }
      }
    }

    bootstrap();
    return () => {
      mounted = false;
    };
  }, [applySession, clearSession, refresh]);

  const login = useCallback(
    async (payload) => {
      const session = await authApi.login(payload);
      applySession(session);
      return session;
    },
    [applySession]
  );

  const logout = useCallback(async () => {
    try {
      await authApi.logout();
    } finally {
      clearSession();
    }
  }, [clearSession]);

  const can = useCallback((code) => permissions.includes(code), [permissions]);

  const value = useMemo(
    () => ({ user, permissions, loading, login, logout, refresh, can }),
    [can, loading, login, logout, permissions, refresh, user]
  );

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used inside AuthProvider");
  }
  return context;
}
