import { ready, yieldMainThread } from "@devowl-wp/react-utils";

import { applyOptIn } from "./optIn.js";
import { applyOptInOutManager } from "./optInOutManager.js";
import { applyOptOut } from "./optOut.js";
import { getServiceSelection } from "../decision/getServiceSelection.js";
import { APPLY_INTERACTIVE_EVENT } from "../events/applyInteractive.js";
import { OPT_IN_ALL_EVENT } from "../events/optInAll.js";
import { OPT_OUT_ALL_EVENT } from "../events/optOutAll.js";

import type { ApplyOptInOptions } from "./optIn.js";
import type { GetServiceSelectionOptions } from "../decision/getServiceSelection.js";
import type { ApplyInteractiveEvent } from "../events/applyInteractive.js";
import type { OptInAllEvent } from "../events/optInAll.js";
import type { OptOutAllEvent } from "../events/optOutAll.js";

/**
 * Apply services on frontend. Depending on some other mechanism you can differ
 * between the following "decision" types:
 *
 * - `all`: All services are applied (e. g. bots)
 * - `consent`: All services from the current consent (read from cookie)
 *
 * The services are sent in the following way:
 *
 * 1. Apply opt-in scripts (sequentially)
 * 2. If GTM / MTM is active, wait for the data layer
 *   a) Push all opt-in events (parallel)
 *   b) Wait x seconds to avoid side effects
 *   c) Push all opt-out events (parallel)
 * 3. Apply opt-in scripts (sequentially)
 *
 * @param type
 * @see https://www.simoahava.com/analytics/notify-page-google-tag-manager-loaded/
 * @see https://support.google.com/tagmanager/answer/7679219
 * @see https://developer.matomo.org/guides/tagmanager/datalayer
 */
async function apply(options: GetServiceSelectionOptions & ApplyOptInOptions) {
    const applyBeforeInteractiveServices: ApplyInteractiveEvent["services"] = [];
    await getServiceSelection(options).iterateServices(async (group, service, isOptIn) => {
        if (isOptIn) {
            applyBeforeInteractiveServices.push({ group, service });
        }
    });

    document.dispatchEvent(
        new CustomEvent<ApplyInteractiveEvent>(APPLY_INTERACTIVE_EVENT, {
            detail: {
                services: applyBeforeInteractiveServices,
                triggeredByOtherTab: options.triggeredByOtherTab,
            },
        }),
    );

    // Wait all events are initialized
    await ready();

    // Do opt-in
    const { dataLayer, isManagerOptOut, services, ready: applyOptInReady } = await applyOptIn(options);

    // Wait for manager's dataLayer loaded (can also be none) and do manager's events
    const { ready: applyOptInOutManagerReady } = await applyOptInOutManager(options, dataLayer, isManagerOptOut);

    const optInAllReady = Promise.all([applyOptInReady, applyOptInOutManagerReady]);
    await yieldMainThread();

    document.dispatchEvent(
        new CustomEvent<OptInAllEvent>(OPT_IN_ALL_EVENT, {
            detail: {
                services,
                ready: optInAllReady,
            },
        }),
    );

    // Do opt-out
    const {
        deleteHttpCookies,
        services: allOptOutServices,
        ready: optOutReady,
    } = await applyOptOut(options, isManagerOptOut);

    document.dispatchEvent(
        new CustomEvent<OptOutAllEvent>(OPT_OUT_ALL_EVENT, {
            detail: {
                services: allOptOutServices,
                deleteHttpCookies,
                ready: Promise.all([optInAllReady, optOutReady]),
            },
        }),
    );
}

export { apply };
