import { Fragment, useCallback, useEffect, useMemo, useState } from "react";

import type { TcfGvlDeviceStorageDisclosure } from "@devowl-wp/cookie-consent-web-client";
import { isPurposeEnabledForVendor } from "@devowl-wp/cookie-consent-web-client";
import { extendCommonGroupsStylesheet } from "@devowl-wp/web-cookie-banner";

import { useBanner } from "../../../../contexts/banner.js";
import { useBannerStylesheet } from "../../../../hooks/banner/useBannerStylesheet.js";
import { useAppropriateSafeguards } from "../../../../hooks/common/useAppropriateSafeguards.js";
import { useDurationText } from "../../../../hooks/common/useDurationText.js";
import { Checkbox } from "../../../common/checkbox.js";
import { CookieProperty } from "../../../common/groups/cookieProperty.js";
import { CookiePropertyDataTransmission } from "../../../common/groups/cookiePropertyDataTransmission.js";
import { CookiePropertyListLessRelevant } from "../../../common/groups/cookiePropertyListLessRelevant.js";
import { CookiePropertyTechnicalDefinitionsTable } from "../../../common/groups/cookiePropertyTechnicalDefinitionsTable.js";
import { LinkToggle } from "../../../common/linkToggle.js";

import type { Vendor } from "@iabtechlabtcf/core";
import type { ComponentProps, FC } from "react";

function getTcfDataRetentionTimeForPurpose(
    dataRetention: Vendor["dataRetention"],
    purposeId: number,
    isSpecial: boolean,
) {
    return dataRetention
        ? dataRetention[isSpecial ? "specialPurposes" : "purposes"][`${purposeId}`] || dataRetention.stdRetention
        : undefined;
}

const BannerTcfVendor: FC<{
    id: number;
}> = ({ id }) => {
    const { Cookie } = useBannerStylesheet().extend(...extendCommonGroupsStylesheet);

    const [isOpen, setIsOpen] = useState(false);
    const banner = useBanner();
    const generateDurationText = useDurationText();
    const {
        designVersion,
        tcfFilterBy,
        lazyLoadedDataForSecondView,
        tcf: {
            gvl,
            model,
            original: { vendorConfigurations },
        },
    } = banner;
    const {
        vendors: { [id]: vendor },
        purposes,
        specialPurposes,
        features,
        specialFeatures,
        dataCategories,
    } = gvl;
    const {
        name,
        [tcfFilterBy === "consent" ? "purposes" : "legIntPurposes"]: usedPurposes,
        flexiblePurposes,
        specialPurposes: usedSpecialPurposes,
        features: usedFeatures,
        specialFeatures: usedSpecialFeatures,
        dataDeclaration,
        usesCookies,
        cookieMaxAgeSeconds,
        cookieRefresh,
        usesNonCookieAccess,
        dataRetention,
    } = vendor;
    const { dataProcessingInCountries, dataProcessingInCountriesSpecialTreatments } = useMemo(
        () => Object.values(vendorConfigurations).filter(({ vendorId }) => vendorId === id)[0],
        [id],
    );
    const filteredPurposes = useMemo(
        () =>
            [
                ...usedPurposes,
                // Add flexible purposes which are not yet stored in used purposes (unique)
                ...flexiblePurposes.filter((flexiblePurposeId) => usedPurposes.indexOf(flexiblePurposeId) === -1),
            ].filter((purposeId) => isPurposeEnabledForVendor(model, purposeId, tcfFilterBy === "legInt", vendor)),
        [id, tcfFilterBy],
    );
    const {
        group: { descriptionFontSize },
        i18n: {
            tcf: { declarations: i18nTcfDeclarations, dataRetentionPeriod, ...i18nTcf },
            safetyMechanisms: { adequacyDecision, bindingCorporateRules, standardContractualClauses },
            other,
            ...i18n
        },
        activeAction,
    } = banner;
    const { deprecated: i18nDeprecated } = i18n;

    const { urls, additionalInformation, deviceStorageDisclosure } = lazyLoadedDataForSecondView?.tcf?.vendors[id] || {
        urls: [],
        additionalInformation: {},
        deviceStorageDisclosure: {},
    };

    const provider = additionalInformation?.legalAddress;
    const internationalTransfers = !!additionalInformation?.internationalTransfers;
    const transferMechanisms = additionalInformation?.transferMechanisms || [];

    const checkedList = model[tcfFilterBy === "consent" ? "vendorConsents" : "vendorLegitimateInterests"];
    const isDisabled = activeAction === "history";

    // Save checked in own state cause the model does not trigger a re-render
    const modelIsChecked = checkedList.has(id);
    const [isChecked, setIsChecked] = useState(modelIsChecked);

    // Update our checked state depending on the original value (e.g. switch of legInt | consent filter)
    useEffect(() => {
        setIsChecked(modelIsChecked);
    }, [modelIsChecked]);

    const handleCheckbox: ComponentProps<typeof Checkbox>["onToggle"] = useCallback(
        (checked) => {
            try {
                checkedList[checked ? "set" : "unset"](id);
                setIsChecked(checked);
            } catch (e) {
                // Silence is golden.
            }
        },
        [id, checkedList, setIsChecked],
    );

    const { dataProcessingInUnsafeCountries, appropriateSafeguards } = useAppropriateSafeguards({
        dataProcessingInCountries,
        specialTreatments: dataProcessingInCountriesSpecialTreatments,
        tcf: { internationalTransfers, transferMechanisms },
    });

    const { privacy, legIntClaim } = urls?.[0] || {
        langId: "",
        privacy: "",
        legIntClaim: "",
    };

    const listPurposes = useCallback(
        (purposeIds: number[], isSpecial = false) =>
            purposeIds
                .map(
                    (declarationId) =>
                        `${(isSpecial ? specialPurposes : purposes)[declarationId].name}${
                            dataRetention
                                ? ` (${dataRetentionPeriod}: ${generateDurationText(
                                      getTcfDataRetentionTimeForPurpose(dataRetention, declarationId, false),
                                      "d",
                                  )})`
                                : ""
                        }`,
                )
                .join(", "),
        [purposes, specialPurposes, dataRetention],
    );

    return (
        <Cookie>
            <Checkbox
                isChecked={isChecked}
                isDisabled={isDisabled}
                fontSize={descriptionFontSize}
                onToggle={handleCheckbox}
                after={
                    <LinkToggle
                        onToggle={setIsOpen}
                        showMore={i18n.showMore}
                        hideMore={i18n.hideMore}
                        groupLabel={name}
                        bullets
                    />
                }
            >
                <strong>{name}</strong>
            </Checkbox>
            {isOpen && (
                <Fragment>
                    {!!provider && <CookieProperty label={i18n.provider} value={provider.split(";").join(", ")} />}
                    <CookieProperty label={i18n.providerPrivacyPolicyUrl} value={privacy} />
                    {tcfFilterBy === "legInt" && <CookieProperty label={i18nTcf.legIntClaim} value={legIntClaim} />}
                    {designVersion < 10 && dataProcessingInUnsafeCountries.length > 0 && (
                        <CookieProperty
                            label={i18nDeprecated.dataProcessingInUnsafeCountries}
                            value={dataProcessingInUnsafeCountries.join(", ")}
                        />
                    )}
                    {designVersion < 10 && appropriateSafeguards.length > 0 && (
                        <CookieProperty
                            label={i18nDeprecated.appropriateSafeguard}
                            value={appropriateSafeguards.join(", ")}
                        />
                    )}
                    {filteredPurposes.length > 0 && (
                        <CookieProperty
                            label={i18nTcfDeclarations.purposes.title}
                            value={listPurposes(filteredPurposes)}
                        />
                    )}
                    {usedSpecialPurposes.length > 0 && (
                        <CookieProperty
                            label={i18nTcfDeclarations.specialPurposes.title}
                            value={listPurposes(usedSpecialPurposes, true)}
                        />
                    )}
                    {usedFeatures.length > 0 && (
                        <CookieProperty
                            label={i18nTcfDeclarations.features.title}
                            value={usedFeatures.map((declarationId) => features[declarationId].name).join(", ")}
                        />
                    )}
                    {usedSpecialFeatures.length > 0 && (
                        <CookieProperty
                            label={i18nTcfDeclarations.specialFeatures.title}
                            value={usedSpecialFeatures
                                .map((declarationId) => specialFeatures[declarationId].name)
                                .join(", ")}
                        />
                    )}
                    {dataDeclaration?.length > 0 && (
                        <CookieProperty
                            label={i18nTcfDeclarations.dataCategories.title}
                            value={dataDeclaration
                                .map((declarationId) => dataCategories[declarationId].name)
                                .join(", ")}
                        />
                    )}
                    <CookieProperty label={i18n.usesCookies} value={usesCookies} printValueAs="boolean" />
                    {usesCookies && (
                        <CookieProperty
                            label={i18n.duration}
                            value={
                                cookieMaxAgeSeconds <= 0 ? "Session" : generateDurationText(cookieMaxAgeSeconds, "s")
                            }
                        />
                    )}
                    <CookieProperty label={i18n.cookieRefresh} value={cookieRefresh} printValueAs="boolean" />
                    <CookieProperty
                        label={i18n.usesNonCookieAccess}
                        value={usesNonCookieAccess}
                        printValueAs="boolean"
                    />
                    <CookiePropertyListLessRelevant
                        expandable={designVersion > 9}
                        labelModifications={{ [i18n.technicalCookieName]: i18n.technicalCookieDefinitions }}
                        groupLabel={name}
                    >
                        {designVersion > 9 && (
                            <CookiePropertyDataTransmission
                                dataProcessingInCountries={dataProcessingInCountries}
                                dataProcessingInCountriesSpecialTreatments={dataProcessingInCountriesSpecialTreatments}
                                mechanisms={(usedMechanisms) => {
                                    const result = [...usedMechanisms] as string[];

                                    // E.g. Google uses `Adequacy Decision` instead of `Adequacy decision`
                                    const lowerCased = transferMechanisms.map((s) => s.toLowerCase());

                                    if (lowerCased.indexOf("adequacy decision") > -1) {
                                        result.push(adequacyDecision);
                                    }
                                    if (lowerCased.indexOf("sccs") > -1 && usedMechanisms.indexOf("B") === -1) {
                                        result.push(standardContractualClauses);
                                    }
                                    if (lowerCased.indexOf("bcrs") > -1 && usedMechanisms.indexOf("E") === -1) {
                                        result.push(bindingCorporateRules);
                                    }
                                    if (lowerCased.indexOf("other") > -1) {
                                        result.push(other);
                                    }

                                    return result;
                                }}
                            />
                        )}
                        {deviceStorageDisclosure?.disclosures?.length > 0 && (
                            <CookiePropertyTechnicalDefinitionsTable
                                codeDynamics={{}}
                                definitions={deviceStorageDisclosure.disclosures.map(
                                    ({
                                        type,
                                        identifier,
                                        domain,
                                        domains,
                                        maxAgeSeconds,
                                        cookieRefresh,
                                        purposes: disclosurePurposes = [],
                                        specialPurposes: disclosureSpecialPurposes = [],
                                        description,
                                        optOut,
                                    }) => ({
                                        type: getTcfCookieTypeLocalization(type),
                                        name: identifier,
                                        host: domains ? domains.join(",") : domain || "n/a",
                                        isSessionDuration: maxAgeSeconds !== null && maxAgeSeconds <= 0,
                                        duration: maxAgeSeconds,
                                        durationUnit: "s",
                                        description,
                                        purpose: [
                                            ...disclosurePurposes.map((purposeId) => purposes[purposeId]?.name),
                                            ...disclosureSpecialPurposes.map(
                                                (specialPurposeId) => specialPurposes[specialPurposeId]?.name,
                                            ),
                                        ]
                                            .filter(Boolean)
                                            .join(", "),
                                        children: (
                                            <>
                                                {optOut && (
                                                    <CookieProperty label={i18n.optOut} value={i18n.optOutDesc} />
                                                )}
                                                <CookieProperty
                                                    label={i18n.cookieRefresh}
                                                    value={cookieRefresh}
                                                    printValueAs="boolean"
                                                />
                                            </>
                                        ),
                                    }),
                                )}
                            />
                        )}
                    </CookiePropertyListLessRelevant>
                </Fragment>
            )}
        </Cookie>
    );
};

function getTcfCookieTypeLocalization(type: TcfGvlDeviceStorageDisclosure["disclosures"][0]["type"]) {
    switch (type) {
        case "cookie":
            return "HTTP Cookie";
        case "web":
            return "LocalStorage, Session Storage, IndexDB";
        case "app":
            return "App";
        default:
            return type;
    }
}

export { BannerTcfVendor, getTcfCookieTypeLocalization, getTcfDataRetentionTimeForPurpose };
