import { createContext, onMount, onCleanup, useContext, createResource } from "solid-js";
import { ReactiveMap } from "@solid-primitives/map";
// @ts-ignore
import stableHash from "stable-hash";
const QueryContext = createContext();
class ReactiveMapWithStableHash extends ReactiveMap {
    set(key, value) {
        if (stableHash(super.get(key)) === stableHash(value)) {
            return this;
        }
        return super.set(key, value);
    }
}
// TODO:
// - deduplikace
// - infiniteQuery (v postupu)
// - error retry
// - isRefething 
// - refetch on reconnect
export function useQueryContext() {
    return useContext(QueryContext);
}
export const QueryConfig = (props) => {
    const cache = new ReactiveMapWithStableHash(); // Tady možná je trochu overheat, že has atd je reaktivní.
    return (<QueryContext.Provider value={{ fetcher: props.fetcher, cache }}>
            {props.children}
        </QueryContext.Provider>);
};
export function useCache() {
    const { cache } = useQueryContext();
    return cache;
}
export function useMutate() {
    const { fetcher, cache } = useQueryContext();
    return async (key, data) => { cache.set(key, data ? data : await fetcher(key)); };
}
export function useQuery(getKey, initialValue) {
    const { fetcher, cache } = useQueryContext();
    const globalMutate = useMutate();
    const mutate = (data) => globalMutate(getKey(), data);
    const refetch = () => mutate();
    const cacheStorage = (initialValue) => {
        const key = getKey();
        if (!cache.has(key) && initialValue) {
            cache.set(key, initialValue);
        }
        return [
            () => cache.get(key),
            (newValue) => {
                cache.set(key, newValue());
                return cache.get(key);
            }
        ];
    };
    const cacheFetcher = async (key) => {
        if (cache.has(key)) {
            (async () => cache.set(key, await fetcher(key)))();
            return cache.get(key);
        }
        return await fetcher(key);
    };
    // @ts-ignore
    // TODO: opravit type problem.
    const [resource] = createResource(getKey, cacheFetcher, { storage: cacheStorage, initialValue });
    if (typeof window !== "undefined") {
        onMount(() => window.addEventListener("focus", refetch));
        onCleanup(() => window.removeEventListener("focus", refetch));
    }
    return [resource, { mutate }];
}
// TODO: caching, suspense, klasický nekonečný scroll (bez deloadingu), vyčistit kód...
//type UseInfiniteQueryGetKey<T> = (pageIndex: number, previousPageData: T | null) => string;
//type UseInfiniteQuery<T> = [InitializedResource<T[][]>, { setSize: Setter<number>, size: Accessor<number>, loadingNextPage: Accessor<boolean> }];
// export function useInfiniteQuery<T>(getKey: UseInfiniteQueryGetKey<T>, initialValue?: T[]) {
//     const { fetcher, cache } = useQueryContext();
//     const [size, setSize] = createSignal(0);
//     const [loadingPage, setLoadingPage] = createSignal(false);
//     const [data, setData] = createSignal<T[]>([]);
//     let max = 0;
//     onMount(async () => setData(await fetcher(getKey(0, null))));
//     createEffect(() => {
//         if (data().length > max) {
//             max = data().length;
//         }
//         const _size = untrack(size);
//         console.log("Max loaded sofar:", max, "Size:", _size);
//     });
//     async function forward() {
//         if (loadingPage()) {
//             return;
//         }
//         setLoadingPage(true);
//         let _data = untrack(data);
//         const newSize = size() + 1;
//         const fetchedData = await fetcher(getKey(newSize, null));
//         let newData = [..._data, ...fetchedData];
//         if (newSize % 4 == 0) {
//             newData = newData.slice(newData.length - 200, newData.length);
//         }
//         setData(newData);
//         setSize(newSize);
//         setLoadingPage(false);
//     }
//     async function back() {
//         if (loadingPage()) {
//             return;
//         }
//         if (size() <= 3) {
//             if (size() != 0) {
//                 setSize(0);
//             }
//             return;
//         }
//         setLoadingPage(true);
//         let _data = untrack(data);
//         const newSize = size() - 1;
//         const fetchedData = await fetcher(getKey(newSize - 3, null));
//         let newData = [...fetchedData, ..._data];
//         if (newSize % 4 == 0) {
//             newData = newData.slice(0, newData.length - 200);
//         }
//         setData(newData);
//         setSize(newSize);
//         setLoadingPage(false);
//     }
//     return [data, { back, forward }];
// }
