import { useImmutableContext, useImmutableContextProvider } from "../context/immutable.js";

import type { ImmutableContext } from "../context/immutable.js";

const SUSPSENSE_LOADED_CONTEXT_SYMBOL = Symbol();
type SuspenseLoadedContext = ImmutableContext<{
    initialState: { completed: boolean; loaded: string[] };
    refActions: { onMounted: (name: string) => void };
}>;
const useSuspenseLoaded = (): SuspenseLoadedContext["contextValue"] =>
    useImmutableContext<SuspenseLoadedContext>(SUSPSENSE_LOADED_CONTEXT_SYMBOL);

/**
 * With the power of `SuspenseLoadedContext` we could "wait" for a list of suspended components
 * to be mounted to the DOM. This allows us to e.g. avoid CLS and render the cookie banner when
 * all suspended components are visible.
 */
function useSuspendedComponentsMounted(
    names: string[],
    /**
     * Pass a function which takes a callback. The `completed: true` statement gets updated within
     * this callback. You could use e.g. `fastdom.mutate()` or `window.requestAnimationFrame` for this.
     */
    throttle?: (callback: (...args: any[]) => any) => any,
    /**
     * If set, it will not update the context value `completed` to avoid rerenders and this callback will
     * be fired instead.
     */
    onComplete?: () => void,
) {
    return useImmutableContextProvider<SuspenseLoadedContext>(
        SUSPSENSE_LOADED_CONTEXT_SYMBOL,
        {
            completed: false,
            loaded: [],
        },
        {},
        {
            refActions: {
                onMounted: ({ completed, loaded, set }, name) => {
                    loaded.push(name);
                    if (names.every((i) => loaded.indexOf(i) > -1) && !completed) {
                        const setTrue = onComplete || (() => set({ completed: true }));
                        if (throttle) {
                            throttle(setTrue);
                        } else {
                            setTrue();
                        }
                    }
                },
            },
        },
    );
}

export { useSuspendedComponentsMounted, useSuspenseLoaded };
