import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useState } from "react";
import { PageSkeleton } from "./PageSkeleton.jsx";

const PageLoadContext = createContext(null);
const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;

export function PageLoadBoundary({ children, pageKey }) {
  const [loaders, setLoaders] = useState(() => new Map());
  const [released, setReleased] = useState(false);

  useEffect(() => {
    setLoaders(new Map());
    setReleased(false);
  }, [pageKey]);

  const reportLoading = useCallback((id, loading) => {
    setLoaders((current) => {
      if (loading === null) {
        if (!current.has(id)) {
          return current;
        }
        const next = new Map(current);
        next.delete(id);
        return next;
      }

      if (current.get(id) === loading) {
        return current;
      }

      const next = new Map(current);
      next.set(id, loading);
      return next;
    });
  }, []);

  const hasPendingLoader = useMemo(() => Array.from(loaders.values()).some(Boolean), [loaders]);
  const showSkeleton = !released && hasPendingLoader;

  useIsomorphicLayoutEffect(() => {
    if (!released && loaders.size > 0 && !hasPendingLoader) {
      setReleased(true);
    }
  }, [hasPendingLoader, loaders.size, released]);

  const contextValue = useMemo(() => ({ reportLoading }), [reportLoading]);

  return (
    <PageLoadContext.Provider value={contextValue}>
      <div className="page-load-boundary">
        {showSkeleton ? <PageSkeleton /> : null}
        <div
          aria-hidden={showSkeleton ? "true" : undefined}
          className={["page-load-boundary__content", showSkeleton ? "page-load-boundary__content--hidden" : ""]
            .filter(Boolean)
            .join(" ")}
        >
          {children}
        </div>
      </div>
    </PageLoadContext.Provider>
  );
}

export function usePageLoadingStatus(loading, enabled = true) {
  const context = useContext(PageLoadContext);
  const id = useId();

  useIsomorphicLayoutEffect(() => {
    if (!context || !enabled) {
      return undefined;
    }

    context.reportLoading(id, Boolean(loading));

    return () => {
      context.reportLoading(id, null);
    };
  }, [context, enabled, id, loading]);
}
