import isEqual from "lodash.isequal";
import semver from "semver";
import { ulid } from "ulid";
import { registerSW } from "virtual:pwa-register";

import { useEffect, useState } from "preact/hooks";

import { internalEmit, internalSubscribe } from "./lib/eventEmitter";

import { modalController } from "./controllers/modals/ModalController";
import { APP_VERSION } from "./version";

const INTERVAL_HOUR = 36e5;

let forceUpdate = false;
let registration: ServiceWorkerRegistration | undefined;

export const updateSW = registerSW({
    onNeedRefresh() {
        if (forceUpdate) {
            updateSW(true);
        } else {
            internalEmit("PWA", "update");
        }
    },
    onOfflineReady() {
        console.info("Ready to work offline.");
        // show a ready to work offline to user
    },
    onRegistered(r) {
        registration = r;

        // Check for updates every hour
        setInterval(() => r!.update(), INTERVAL_HOUR);
    },
});

let currentPollRate: number;
let scheduledTask: number;

/**
 * Schedule version checker
 * @param poll_rate Set poll rate in milliseconds
 */
function schedule(poll_rate = INTERVAL_HOUR) {
    if (poll_rate !== currentPollRate) {
        currentPollRate = poll_rate;
        clearInterval(scheduledTask);
        scheduledTask = setInterval(
            checkVersion,
            poll_rate,
        ) as unknown as number;
    }
}

let currentAlert: SystemAlert | undefined;
type SystemAlert = {
    text: string;
    dismissable?: boolean;
    actions?: {
        text: string;
        type: "internal" | "external";
        href: string;
    }[];
};

/**
 * Get the current system alert
 */
export function useSystemAlert() {
    const [alert, setAlert] = useState(currentAlert);
    useEffect(() => internalSubscribe("System", "alert", setAlert as any), []);
    return alert;
}

/**
 * Check whether the client is out of date
 */
async function checkVersion() {
    const { version, poll_rate, alert } = (await fetch(
        "https://health.revolt.chat/api/health",
    ).then((res) => res.json())) as {
        version: string;
        poll_rate?: number;
        alert?: SystemAlert;
    };

    // Re-schedule if necessary
    schedule(poll_rate);

    // Apply any active alerts
    if (!isEqual(alert, currentAlert)) {
        currentAlert = alert;
        internalEmit("System", "alert", alert);
    }

    // Check if we need to update
    if (version !== "0.5.3-7" && !semver.satisfies(APP_VERSION, version)) {
        // Let the worker know we should immediately refresh
        forceUpdate = true;

        // Prompt service worker to update
        registration?.update();

        // Push information that the client is out of date
        modalController.push({
            key: ulid(),
            type: "out_of_date",
            version,
        });
    }
}

if (
    import.meta.env.VITE_API_URL === "https://api.revolt.chat" ||
    import.meta.env.VITE_API_URL === "https://app.revolt.chat/api"
) {
    // Check for critical updates hourly
    schedule();
    checkVersion();
}
