import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
  type ReactNode,
} from "react";
import { useQueryClient } from "@tanstack/react-query";
import { SIGN_IN_PATH } from "@/lib/authRedirect";
import {
  clearSiteAuthStorage,
  fetchSiteUser,
  setSiteAuthFlag,
  siteLogin,
  siteLogout,
  type SiteLoginResult,
  type SiteUser,
} from "@/lib/siteApi";

interface AuthContextType {
  isAuthenticated: boolean;
  loading: boolean;
  loggingOut: boolean;
  user: SiteUser | null;
  login: (username: string, password: string) => Promise<SiteLoginResult>;
  logout: () => Promise<void>;
  hasRole: (role: string) => boolean;
  verifySession: () => Promise<boolean>;
}

const AuthContext = createContext<AuthContextType>({} as AuthContextType);

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const queryClient = useQueryClient();
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [loading, setLoading] = useState(true);
  const [loggingOut, setLoggingOut] = useState(false);
  const [user, setUser] = useState<SiteUser | null>(null);
  const isAuthenticatedRef = useRef(isAuthenticated);
  isAuthenticatedRef.current = isAuthenticated;

  const applySession = useCallback((sessionUser: SiteUser | null) => {
    if (sessionUser) {
      setUser((previous) => {
        if (
          previous?.username === sessionUser.username &&
          JSON.stringify(previous.roles) === JSON.stringify(sessionUser.roles)
        ) {
          return previous;
        }
        return sessionUser;
      });
      setIsAuthenticated(true);
      setSiteAuthFlag(true);
      return true;
    }
    setUser(null);
    setIsAuthenticated(false);
    clearSiteAuthStorage();
    return false;
  }, []);

  const verifySession = useCallback(async (): Promise<boolean> => {
    try {
      const sessionUser = await fetchSiteUser();
      return applySession(sessionUser);
    } catch (error) {
      console.error("[AuthContext] Session verification failed:", error);
      return isAuthenticatedRef.current;
    }
  }, [applySession]);

  useEffect(() => {
    let cancelled = false;

    verifySession().finally(() => {
      if (!cancelled) setLoading(false);
    });

    return () => {
      cancelled = true;
    };
  }, [verifySession]);

  useEffect(() => {
    const revalidate = () => {
      verifySession();
    };

    const onPageShow = (event: PageTransitionEvent) => {
      if (event.persisted) {
        window.location.reload();
      }
    };

    const onFocus = () => {
      revalidate();
    };

    const onVisibilityChange = () => {
      if (document.visibilityState === "visible") {
        revalidate();
      }
    };

    window.addEventListener("pageshow", onPageShow);
    window.addEventListener("focus", onFocus);
    document.addEventListener("visibilitychange", onVisibilityChange);

    return () => {
      window.removeEventListener("pageshow", onPageShow);
      window.removeEventListener("focus", onFocus);
      document.removeEventListener("visibilitychange", onVisibilityChange);
    };
  }, [verifySession]);

  const login = async (username: string, password: string): Promise<SiteLoginResult> => {
    const result = await siteLogin(username, password);
    if (!result.ok) {
      return result;
    }

    applySession(result.user);

    const verified = await verifySession();
    if (!verified) {
      applySession(null);
      return { ok: false, reason: "network" };
    }

    return result;
  };

  const logout = async (): Promise<void> => {
    if (loggingOut) return;
    setLoggingOut(true);

    try {
      const success = await siteLogout();
      if (!success) {
        setLoggingOut(false);
        return;
      }

      queryClient.clear();
      applySession(null);
      window.location.replace(SIGN_IN_PATH);
    } catch {
      setLoggingOut(false);
    }
  };

  const hasRole = (role: string): boolean => {
    return user?.roles?.includes(role) ?? false;
  };

  return (
    <AuthContext.Provider value={{ isAuthenticated, loading, loggingOut, user, login, logout, hasRole, verifySession }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);
