import limiter from "p-limit";
import { Client, INavTree } from "src";
import { Workbox } from "workbox-window";

export const getOfflineStatus = () => {
    const val = window.localStorage.getItem("prefetch_status");

    if (val === "complete") {
        return "COMPLETE";
    } else {
        return "INCOMPLETE";
    }
};

export const prefetchAll = async ({ status_callback, client, parallelism = 10 }: { status_callback: (status: string) => void; client: Client; parallelism?: number }) => {
    if (getOfflineStatus() === "COMPLETE") {
        status_callback("Ready");
        return;
    }
    const do_fetch = async () => {

        const paths = ["/"];

        const hidden_fetcher = document.createElement("div");
        hidden_fetcher.hidden = true;

        const limit = limiter(parallelism);

        const sections = await client.content.getSections();
        let processed = 0;
        let total = 0;

        const section_promise_map: { [key: string]: Array<Promise<void>> } = {};

        const loadLeaf = async (section, leaf: {href: string}) => {
            try {
                const content = await client.content.getContent(leaf.href);
                paths.push(`/content/${leaf.href}`);
                hidden_fetcher.innerHTML = content.content;
                processed++;
                status_callback(`Processing ${section}: ${processed}/${total}`);
            } catch (e) {
                // womp womp, still try and load stuff anyway
            }
        };

        const handleTree = (section, tree: INavTree) => {
            if (!section_promise_map[section]) {
                section_promise_map[section] = [];
            }

            section_promise_map[section].push(limit(() => loadLeaf(section, tree)));

            total++;

            for (const child of tree.children) {
                handleTree(section, child);
            }
        };

        for (const section of sections) {
            const navTree = await client.content.getNavTree(section.href);

            if (!section_promise_map[section.title]) {
                section_promise_map[section.title] = [];
            }
            section_promise_map[section.title].push(limit(() => loadLeaf(section.title, {href: section.href})));
            total++;

            handleTree(section.title, navTree);
        }

        for (const [section_name, promises] of Object.entries(section_promise_map)) {
            await Promise.all(promises);
        }

        return paths;
        // status_callback("Offline Ready!");
    };

    const wb = new Workbox("/service-worker.js");

    wb.addEventListener("installed", (event) => {
        // @ts-ignore
        if (!event.isUpdate) {
            status_callback("First Service Worker load!");
            window.location.reload();
        }
    });

    status_callback("Waiting for sw to control page...");

    // tslint:disable-next-line: no-floating-promises
    wb.register();

    await wb.controlling;

    const urlsToCache = await do_fetch();

    status_callback(`Prefetching ${urlsToCache.length} content pages...`);

    // @ts-ignore
    const success = await wb.messageSW({
        type: "CACHE_URLS",
        payload: {
            urlsToCache,
        },
    });

    status_callback(`Prefetch ${success ? "success" : "failure"}`);

    window.localStorage.setItem("prefetch_status", "complete");
    status_callback("All set!");
};
