import { useRouter } from "next/router";
import {
	createContext,
	type FC,
	type ReactNode,
	useContext,
	useEffect,
} from "react";
import type { Session, ValidSession } from "~/server/api/trpc";
import { api } from "~/utils/api";

type SessionStatus = "pending" | "success" | "error";
export type SessionContextType = {
	user: Session | null;
	status: SessionStatus;
};

const SessionContext = createContext<SessionContextType>({
	status: "pending",
	user: null,
});

export const SessionProvider: FC<{ children: ReactNode }> = ({ children }) => {
	const router = useRouter();
	const utils = api.useUtils();

	const {
		data: user,
		status,
		isLoading,
		isError,
		error,
	} = api.auth.getSession.useQuery(undefined, {
		refetchInterval: 30000,
		refetchOnMount: true,
		refetchOnReconnect: true,
		refetchOnWindowFocus: true,
		staleTime: 0,
	});

	if (isError && error) {
		console.error("Session fetch error: ", error);
		window.location.reload();
	}

	// Invalidate session on route change
	useEffect(() => {
		const handleRouteChange = () => {
			if (isLoading) return;
			utils.auth.getSession.invalidate().catch((error: Error) => {
				console.error("Error invalidating session on route change:", error);
			});
		};
		router.events.on("routeChangeComplete", handleRouteChange);
		return () => {
			router.events.off("routeChangeComplete", handleRouteChange);
		};
	}, [router, utils]);

	// Invalidate session if user is null
	useEffect(() => {
		if (!user) {
			utils.auth.getSession.invalidate().catch((error: Error) => {
				console.error("Error during session invalidation:", error);
			});
		}
	}, [user]);

	return (
		<SessionContext.Provider value={{ status, user: user || null }}>
			{children}
		</SessionContext.Provider>
	);
};

export const useSession = () => useContext(SessionContext);

export const EnforcedSessionContext = createContext<ValidSession | undefined>(
	undefined,
);
/**
 * AreaRiservata specific session context. It ensures that the session is valid.
 */

export const useEnforcedSession = () => {
	const session = useContext(EnforcedSessionContext);
	if (!session) {
		throw new Error(
			"useValidSession must be used within a ValidSessionProvider",
		);
	}
	return session;
};
