import { useMemo } from "react";

import { getUserGcmDecision } from "@devowl-wp/cookie-consent-web-client";
import type { BannerContext, BannerHistoryEntry } from "@devowl-wp/react-cookie-banner";
import {
    Banner,
    getLazyDataForSecondView,
    useBannerConsentListener,
    useBannerProvider,
    useBannerStateContextCallbacks,
    useStateContextCallbacks,
    useTcf,
} from "@devowl-wp/react-cookie-banner";
import { YieldLazyLoad, useFirstResolvedPromise, yieldLazyLoad } from "@devowl-wp/react-utils";
import { commonRequest } from "@devowl-wp/utils";
import { Recorder } from "@devowl-wp/web-html-element-interaction-recorder";

import { BannerProductionNotice } from "./bannerProductionNotice.js";
import { useBannerPreDecisionGateway } from "../hooks/useBannerPreDecisionGateway.js";
import { getCookieConsentManager } from "../others/getCookieConsentManager.js";
import { persistConsent } from "../others/persistConsent.js";
import { getOptionsFromWindow } from "../utils/getOptionsFromWindow.js";
import { getOtherOptionsFromWindow } from "../utils/getOtherOptionsFromWindow.js";
import { locationRestConsentGet } from "../wp-api/consent.get.js";
import { locationRestRevisionSecondView } from "../wp-api/revisionSecondView.get.js";

import type { ParamsRouteConsentGet, RequestRouteConsentGet, ResponseRouteConsentGet } from "../wp-api/consent.get.js";
import type {
    ParamsRouteRevisionSecondView,
    RequestRouteRevisionSecondView,
    ResponseRouteRevisionSecondView,
} from "../wp-api/revisionSecondView.get.js";
import type { FC } from "react";

const YieldBanner = yieldLazyLoad(Promise.resolve(Banner));

/**
 * Simply connects a context store to the banner itself. On the website itself
 * it should not rely
 */
const WebsiteBanner: FC<{ poweredLink?: BannerContext["contextValue"]["poweredLink"] }> = ({ poweredLink }) => {
    const {
        frontend,
        customizeValuesBanner,
        pageRequestUuid4,
        iso3166OneAlpha2,
        bannerDesignVersion,
        bannerI18n,
        isPro,
        isLicensed,
        isDevLicense,
        affiliate,
        isCurrentlyInTranslationEditorPreview,
    } = useMemo(() => getOtherOptionsFromWindow(), []);
    const { restNamespace, restRoot, restQuery, restNonce, restPathObfuscateOffset } = getOptionsFromWindow();
    const { decisionCookieName } = frontend;
    const manager = getCookieConsentManager();
    const userDecision = manager.getUserDecision(true);
    const userDecisionButtonClicked = userDecision === false ? undefined : userDecision.buttonClicked;
    const gcmConsent = getUserGcmDecision(manager.getOption("gcmCookieName"), userDecisionButtonClicked);

    // TCF compatibility
    const tcfObject = useTcf(
        frontend.isTcf,
        frontend.tcf,
        frontend.tcfMetadata,
        manager.getOptions(),
        userDecisionButtonClicked,
    );
    const [lazyLoadedDataForSecondView, fetchLazyLoadedDataForSecondView] = getLazyDataForSecondView(
        () =>
            commonRequest<
                RequestRouteRevisionSecondView,
                ParamsRouteRevisionSecondView,
                ResponseRouteRevisionSecondView
            >({
                location: locationRestRevisionSecondView,
                options: {
                    restNamespace,
                    restRoot,
                    restQuery,
                    restNonce,
                    restPathObfuscateOffset,
                },
                params: {
                    revisionHash: frontend.revisionHash,
                },
                sendRestNonce: false,
            }),
        frontend.hasLazyData,
    );

    // Server-side rendered overlay handler
    const overlay = document.getElementById(pageRequestUuid4);
    const recorder = useMemo(() => new Recorder(overlay), []);
    const modifiers = useStateContextCallbacks<BannerContext>();
    const modifiersBanner = useBannerStateContextCallbacks();

    const [BannerContextProvider, bannerContextValue] = useBannerProvider(
        {
            ...customizeValuesBanner,

            ...frontend,
            // `frontend` also keeps an array of `blocker` which has a conflict with the Blocker context
            // -> we need to exclude this property in the context as the cookie banner should not have any
            // `blocker` property.
            ...{ blocker: undefined },

            recorder,
            productionNotice: (
                <BannerProductionNotice
                    isPro={isPro}
                    isLicensed={isLicensed}
                    isDevLicense={isDevLicense}
                    i18n={bannerI18n}
                />
            ),
            pageRequestUuid4,
            iso3166OneAlpha2,
            gcmConsent,
            tcf: undefined,
            tcfFilterBy: "legInt",
            poweredLink,
            visible: false,
            skipOverlay: true,
            previewCheckboxActiveState: false,
            individualPrivacyOpen: false,
            designVersion: bannerDesignVersion,
            i18n: bannerI18n,
            keepVariablesInTexts: isCurrentlyInTranslationEditorPreview,
            affiliate,
            consent: {
                ...(userDecision === false ? {} : userDecision.consent),
                // Always override essential cookies
                ...getCookieConsentManager().getDefaultDecision(userDecision === false),
            },
            onPersistConsent: persistConsent,
            onApplyConsent: () => getCookieConsentManager().applyCookies({ type: "consent" }),
            didGroupFirstChange: false,
            fetchLazyLoadedDataForSecondView,
            suspense: {
                tcf: tcfObject,
                lazyLoadedDataForSecondView,
            },
        },
        {
            ...modifiers,
            ...modifiersBanner,
            fetchHistory: async () => {
                const items: BannerHistoryEntry[] = [];
                try {
                    items.push(
                        ...(await commonRequest<RequestRouteConsentGet, ParamsRouteConsentGet, ResponseRouteConsentGet>(
                            {
                                location: locationRestConsentGet,
                                options: {
                                    restNamespace,
                                    restRoot,
                                    restQuery,
                                    restNonce,
                                    restPathObfuscateOffset,
                                },
                                cookieValueAsParam: [decisionCookieName],
                                sendRestNonce: false,
                            },
                        )),
                    );
                } catch (e) {
                    // Silence is golden.
                }

                // Retrieve locally saved items
                for (const { createdClientTime } of manager.getConsentQueue()) {
                    items.unshift({
                        created: new Date(createdClientTime).toISOString(),
                        isDoNotTrack: false,
                        isForwarded: false,
                        isUnblock: false,
                        context: undefined,
                        id: new Date(createdClientTime).getTime(),
                        uuid: undefined,
                    });
                }

                return items;
            },
            onLanguageSwitch: (_, { url }) => {
                window.location.href = url;
            },
        },
        {
            deps: [tcfObject],
        },
    );

    useBannerPreDecisionGateway(bannerContextValue);
    useBannerConsentListener(bannerContextValue, decisionCookieName);

    // The banner should not be suspended when the TCF promise changes
    // as this could lead to issues with `renderedOnce` in the banner component.
    // E.g. when accepting the cookie banner on another tab, it does close the dialog
    // but not hide the overlay.
    const yieldTcfObjectPromise = useFirstResolvedPromise(tcfObject);

    return (
        <BannerContextProvider value={bannerContextValue}>
            <YieldLazyLoad promise={yieldTcfObjectPromise}>
                <YieldBanner />
            </YieldLazyLoad>
        </BannerContextProvider>
    );
};

export { WebsiteBanner };
