import { EServiceTemplateLegalBasis } from "@devowl-wp/api-real-cookie-banner";
import { findUsedGcmConsentTypes } from "@devowl-wp/cookie-consent-web-client";

import { BannerTcfStacks } from "../../components/banner/tcf/stacks.js";
import { GcmConsentTypesList } from "../../components/common/gcm/consentTypesList.js";
import { useStylesheet } from "../../contexts/stylesheet.js";
import {
    calculateSafetyMechanismsFromSpecialTreatments,
    calculateUnsafeCountries,
} from "../../others/calculateSafetyMechanismsFromSpecialTreatments.js";
import { useTcfVendors } from "../tcf/useTcfVendors.js";
import { useBannerOrBlocker } from "../useBannerOrBlocker.js";
import { useLegalLinks } from "../useLegalLinks.js";

import type { VisualService } from "../../types/service.js";
import type { Vendor } from "@iabtechlabtcf/core";
import type { ReactNode } from "react";

function joinWithAndSeparator(arr: string[], andSeparator: string) {
    const useArr = arr.filter(Boolean);
    if (useArr.length > 1) {
        useArr.splice(useArr.length - 1, 0, "{{andSeparator}}");
    }
    return useArr.join(", ").replace(/,\s+{{andSeparator}},\s+/g, andSeparator);
}

const createSuperScriptTagFactory =
    (screenReaderOnlyClass: string) =>
    (sup: Array<[string, string?]>, prefix = "") => {
        const useSup = sup.filter(Boolean);
        const oneLetter = useSup.map(([a]) => a);
        const screenReaderTexts = useSup.map(([, a]) => a).filter(Boolean);

        return oneLetter.length === 0
            ? prefix
            : `${prefix}${supWithLineHeightFix}${oneLetter.join(",")}</sup>${
                  screenReaderTexts.length
                      ? `<span class="${screenReaderOnlyClass}">(${screenReaderTexts.join(", ")})</span>`
                      : ""
              }`;
    };

/**
 * We do currently not use any internationalization packgae due to bundle size.
 */
function generatePluralText(count: number, singular: string, plural: string) {
    return `${count} ${count > 1 ? plural : singular}`;
}

/**
 * @see https://gist.github.com/unruthless/413930
 * @see https://stackoverflow.com/a/23630325/5506547
 */
const supWithLineHeightFix = '<sup aria-hidden="true" style="vertical-align:top;line-height:100%;position:initial;">';

function useCommonTeachings({
    services,
    disableListServicesNotice,
    disableTcfPurposes,
}: {
    services: VisualService[];
    disableListServicesNotice?: boolean;
    disableTcfPurposes?: boolean;
}) {
    const {
        blocker,
        groups,
        isAgeNotice,
        isGcm,
        isGcmListPurposes,
        ageNoticeAgeLimit,
        isListServicesNotice,
        isDataProcessingInUnsafeCountries,
        dataProcessingInUnsafeCountriesSafeCountries,
        texts: {
            description,
            dataProcessingInUnsafeCountries,
            ageNoticeBanner,
            ageNoticeBlocker,
            listServicesNotice,
            listServicesLegitimateInterestNotice,
            consentForwardingExternalHosts: consentForwardingExternalHostsText,
        },
        tcf,
        consentForwardingExternalHosts,
        individualPrivacyOpen,
        individualTexts: { description: individualDescription },
        designVersion,
        territorialLegalBasis,
        predefinedDataProcessingInSafeCountriesLists,
        i18n: {
            andSeparator,
            territorialLegalBasisArticles,
            gcm: gcmBannerI18n,
            tcf: tcfBannerI18n,
            deprecated: { dataProcessingInUnsafeCountries: dataProcessingInUnsafeCountriesSuprtScriptDescription },
        },
        keepVariablesInTexts,
    } = useBannerOrBlocker();
    const { privacyPolicy } = useLegalLinks();
    const { screenReaderOnlyClass } = useStylesheet();
    const [tcfConsentVendors, tcfLegIntVendors]: [Vendor[], Vendor[]] =
        process.env.IS_TCF === "1" ? [useTcfVendors("consent"), useTcfVendors("legInt")] : [[], []];

    const createSuperScriptTag = createSuperScriptTagFactory(screenReaderOnlyClass);
    const dataProcessingInUnsafeCountriesSuperScript = designVersion > 9 ? "D" : "U";

    const allServices = groups
        .map((group, groupIdx) =>
            group.items.map((service) => {
                const { legalBasis } = service;

                // Essential-grouped services implicitely set legitimate interest as legal basis
                const useLegalBasis: typeof legalBasis =
                    group.isEssential && designVersion >= 4 && legalBasis === "consent"
                        ? EServiceTemplateLegalBasis.LegitimateInterest
                        : legalBasis;

                return {
                    service,
                    legalBasis: useLegalBasis,
                    group,
                    groupIdx,
                };
            }),
        )
        .flat();

    // Description text (combination of configured text and Consent Forwarding)
    let consentForwardingExternalHostsFinalText = "";
    if (consentForwardingExternalHosts && !keepVariablesInTexts) {
        consentForwardingExternalHostsFinalText = consentForwardingExternalHostsText.replace(
            /{{websites}}/g,
            consentForwardingExternalHosts.join(", "),
        );
    }

    let useDescription = [
        blocker
            ? [blocker.description, designVersion > 2 ? description : false].filter(Boolean).join("\n\n")
            : individualPrivacyOpen
              ? individualDescription
              : description,
        consentForwardingExternalHostsFinalText,
    ]
        .filter(Boolean)
        .join(" ");

    if (!keepVariablesInTexts) {
        // Place policy link into the text https://regex101.com/r/ayeFVy/1
        useDescription = useDescription.replace(
            /{{privacyPolicy}}(.*){{\/privacyPolicy}}/gi,
            privacyPolicy ? `<a href="${privacyPolicy.url}" target="_blank">$1</a>` : "$1",
        );
    }

    // Teaching: Data processing in unsafe countries
    const servicesProcessingUnsafeCountries = services.filter(
        ({ dataProcessingInCountries, dataProcessingInCountriesSpecialTreatments }) =>
            (designVersion > 9
                ? calculateSafetyMechanismsFromSpecialTreatments({
                      predefinedDataProcessingInSafeCountriesLists,
                      isDataProcessingInUnsafeCountries,
                      territorialLegalBasis,
                      service: {
                          dataProcessingInCountries,
                          dataProcessingInCountriesSpecialTreatments,
                      },
                  }).filter((mechanism) => mechanism.startsWith("D"))
                : calculateUnsafeCountries({
                      dataProcessingInCountries,
                      safeCountries: dataProcessingInUnsafeCountriesSafeCountries,
                      specialTreatments: dataProcessingInCountriesSpecialTreatments,
                  })
            ).length > 0,
    );
    const tcfVendorsProcessingUnsafeCountries =
        process.env.IS_TCF && tcf?.gvl
            ? Object.values(tcf.gvl.vendors).filter((vendor) => {
                  const { dataProcessingInCountries, dataProcessingInCountriesSpecialTreatments } =
                      tcf.original.vendorConfigurations[vendor.id];

                  return (
                      (designVersion > 9
                          ? calculateSafetyMechanismsFromSpecialTreatments({
                                predefinedDataProcessingInSafeCountriesLists,
                                isDataProcessingInUnsafeCountries,
                                territorialLegalBasis,
                                service: {
                                    dataProcessingInCountries,
                                    dataProcessingInCountriesSpecialTreatments,
                                },
                            }).filter((mechanism) => mechanism.startsWith("D"))
                          : calculateUnsafeCountries({
                                dataProcessingInCountries,
                                safeCountries: dataProcessingInUnsafeCountriesSafeCountries,
                                specialTreatments: dataProcessingInCountriesSpecialTreatments,
                            })
                      ).length > 0
                  );
              })
            : [];
    let useDataProcessingInUnsafeCountries =
        (servicesProcessingUnsafeCountries.length > 0 || tcfVendorsProcessingUnsafeCountries.length > 0) &&
        (isDataProcessingInUnsafeCountries ? dataProcessingInUnsafeCountries : "");

    if (useDataProcessingInUnsafeCountries && !keepVariablesInTexts) {
        useDataProcessingInUnsafeCountries = useDataProcessingInUnsafeCountries.replace(/{{legalBasis}}/g, () =>
            joinWithAndSeparator(
                territorialLegalBasis.map(
                    (l) => territorialLegalBasisArticles[l].dataProcessingInUnsafeCountries || "",
                ),
                andSeparator,
            ),
        );
    }

    // Teaching: Age notice
    let useAgeNotice = isAgeNotice ? (blocker ? ageNoticeBlocker : ageNoticeBanner) : "";
    if (useAgeNotice && !keepVariablesInTexts) {
        useAgeNotice = useAgeNotice.replace(/{{minAge}}/gi, `${ageNoticeAgeLimit}`);
    }

    // Teaching: List services notice
    let useListServicesNotice = "";
    if (isListServicesNotice && !disableListServicesNotice) {
        const listServicesNoticeLower = listServicesNotice.toLowerCase();
        const hasBothPlaceholders =
            listServicesNoticeLower.indexOf("{{services}}") > -1 &&
            listServicesNoticeLower.indexOf("{{servicegroups}}") > -1;

        const listServicesNames = joinWithAndSeparator(
            allServices.map(({ service, legalBasis, groupIdx, group: { name: groupName, isEssential } }) => {
                const { name } = service;

                if (
                    // Never show "Real Cookie Banner" as service
                    legalBasis === "legal-requirement" ||
                    // For backwards-compatibility stick to the old behavior and not show essential services
                    (designVersion < 4 && isEssential) ||
                    // When a legitimate-interest notice is given, exclude them here and show them in a separate paragraph
                    (listServicesLegitimateInterestNotice && legalBasis !== "consent")
                ) {
                    return undefined;
                }

                return createSuperScriptTag(
                    [
                        hasBothPlaceholders && [`${groupIdx + 1}`, groupName],
                        useDataProcessingInUnsafeCountries &&
                            servicesProcessingUnsafeCountries.indexOf(service) > -1 && [
                                dataProcessingInUnsafeCountriesSuperScript,
                                dataProcessingInUnsafeCountriesSuprtScriptDescription,
                            ],
                    ],
                    name,
                );
            }),
            andSeparator,
        );

        const listServicesLegitimateInterestNames = listServicesLegitimateInterestNotice
            ? joinWithAndSeparator(
                  allServices.map(({ service, legalBasis, groupIdx, group: { name: groupName } }) => {
                      const { name } = service;
                      if (legalBasis !== "legitimate-interest") {
                          return undefined;
                      }

                      return createSuperScriptTag(
                          [
                              hasBothPlaceholders && [`${groupIdx + 1}`, groupName],
                              useDataProcessingInUnsafeCountries &&
                                  servicesProcessingUnsafeCountries.indexOf(service) > -1 && [
                                      dataProcessingInUnsafeCountriesSuperScript,
                                      dataProcessingInUnsafeCountriesSuprtScriptDescription,
                                  ],
                          ],
                          name,
                      );
                  }),
                  andSeparator,
              )
            : "";

        if (listServicesNames) {
            const checkForGroupInComposedString = `${listServicesNames}${listServicesLegitimateInterestNames}`;
            const listServiceGroupsNames = joinWithAndSeparator(
                groups.map(({ name }, groupIdx) => {
                    const groupSuperScript = `${groupIdx + 1}`;

                    // Is this service used in the list above?
                    if (checkForGroupInComposedString.indexOf(`>${groupSuperScript}`) === -1) {
                        return "";
                    }

                    return createSuperScriptTag([hasBothPlaceholders && [groupSuperScript]], name);
                }),
                andSeparator,
            );

            useListServicesNotice = `<span>${listServicesNotice}</span>`;

            if (!keepVariablesInTexts) {
                // Place service list into the text
                useListServicesNotice = useListServicesNotice
                    .replace(/{{services}}/gi, listServicesNames)
                    // Place service group list into the text
                    .replace(/{{serviceGroups}}/gi, listServiceGroupsNames);
            }

            if (useDataProcessingInUnsafeCountries) {
                useDataProcessingInUnsafeCountries += createSuperScriptTag([
                    [dataProcessingInUnsafeCountriesSuperScript],
                ]);
            }
        }

        // Show a separate paragraph about services with legitimate-interest as legal basis (designVersion >= 4)
        if (listServicesLegitimateInterestNames) {
            useListServicesNotice += ` <span>${listServicesLegitimateInterestNotice}</span>`;

            if (!keepVariablesInTexts) {
                // Place service list into the text
                useListServicesNotice = useListServicesNotice.replace(
                    /{{services}}/gi,
                    listServicesLegitimateInterestNames,
                );
            }
        }
    }

    // Show an additional sentence about TCF vendors
    const useTcfParagraph: ReactNode[] = [];
    if (process.env.IS_TCF === "1" && tcf && !individualPrivacyOpen && !disableTcfPurposes) {
        let { teaching } = tcfBannerI18n;
        const {
            vendorsCount: [singular, plural],
        } = tcfBannerI18n;

        if (!keepVariablesInTexts) {
            teaching = teaching
                .replace(/{{consentCount}}/gi, generatePluralText(tcfConsentVendors.length, singular, plural))
                .replace(/{{legIntCount}}/gi, generatePluralText(tcfLegIntVendors.length, singular, plural));
        }

        const tcfParagraph = ` <span>${teaching}</span>`;
        useTcfParagraph.push(<BannerTcfStacks key="tcf" />);
        if (designVersion > 7) {
            useTcfParagraph.unshift(tcfParagraph);
        } else {
            useListServicesNotice += tcfParagraph;
        }
    }

    // Show Google Consent Mode purposes
    const useGcmParagraph: ReactNode[] = [];
    if (!individualPrivacyOpen && isGcm && isGcmListPurposes && findUsedGcmConsentTypes(services).length) {
        useGcmParagraph.push(gcmBannerI18n.teaching, <GcmConsentTypesList key="gcm" services={services} />);
    }

    return {
        description: useDescription,
        teachings: [
            useDataProcessingInUnsafeCountries,
            useAgeNotice,
            useListServicesNotice,
            useTcfParagraph,
            useGcmParagraph,
        ]
            .flat()
            .filter(Boolean),
    };
}

export { useCommonTeachings, generatePluralText, joinWithAndSeparator, createSuperScriptTagFactory };
