import { dispatcher } from "../events/schedule/schedule.js";
import { isLike, isSignalled, ok } from "../is.js";
import { createWaitUntil } from "../fetch/index.js";
import { deregisterSyncTag, getSyncTagRegistrationState, setSyncTagRegistrationState } from "./manager.js";
export function isSyncDurableEventData(event) {
    return !!(isLike(event) &&
        event.type === "sync" &&
        typeof event.tag === "string");
}
export async function dispatchSyncEvent(options) {
    const { event, setRegistrationState, getRegistrationState, dispatch, deregister } = options;
    const { tag, lastChance = false } = event;
    // const initialState = await getRegistrationState(tag);
    // Note that waiting or pending are allowed initial states
    // ok(initialState === "pending" || initialState === "waiting", "Expected event to be in pending state");
    const { signal, controller } = getSignal();
    const { wait, waitUntil } = createWaitUntil();
    try {
        await setRegistrationState(tag, "firing");
        await dispatch({
            lastChance,
            ...event,
            signal,
            waitUntil
        });
        await wait();
    }
    catch (error) {
        if (!signal.aborted) {
            controller?.abort(error);
        }
        if (await dispatchAgainIfReregistered()) {
            return;
        }
        if (lastChance) {
            throw await Promise.reject(error);
        }
        await setRegistrationState(tag, "waiting");
        return;
    }
    finally {
        if (!signal.aborted) {
            controller?.abort();
        }
        await wait();
    }
    if (await dispatchAgainIfReregistered()) {
        return;
    }
    if (deregister) {
        await deregisterSyncTag(tag);
    }
    else {
        await setRegistrationState(tag, "waiting");
    }
    async function dispatchAgainIfReregistered() {
        const state = await getRegistrationState(tag);
        if (state !== "reregisteredWhileFiring") {
            return false;
        }
        await dispatchSyncEvent(options);
        return true;
    }
    function getSignal() {
        if (isSignalled(event)) {
            return { signal: event.signal, controller: undefined };
        }
        const controller = new AbortController();
        return {
            signal: controller.signal,
            controller
        };
    }
}
export const removeSyncDispatcherFunction = dispatcher("sync", async (event, dispatch) => {
    ok(isSyncDurableEventData(event), "Expected sync event with tag");
    await dispatchSyncEvent({
        event,
        setRegistrationState: setSyncTagRegistrationState,
        getRegistrationState: getSyncTagRegistrationState,
        dispatch,
        deregister: true
    });
});
//# sourceMappingURL=dispatch.js.map