import { useCallback } from "react";

import { EServiceTemplateLegalBasis } from "@devowl-wp/api-real-cookie-banner";
import { slugify } from "@devowl-wp/react-utils";
import type { ClientServiceTemplate } from "@devowl-wp/service-cloud-consumer";

import { useFormHandler } from "./useFormHandler.js";
import { TECHNICAL_DEFINITION_DEFAULTS } from "../components/forms/service/fields/technical/compose.js";
import { useI18n } from "../contexts/i18n.js";

import type { FormServiceContext } from "../contexts/formService.js";
import type { FormServiceValueProps } from "../types/formService.js";
import type { FormSettingsValueProps } from "../types/formSettings.js";

function formServiceAttributesFromTemplate(
    template: ClientServiceTemplate["use"],
    {
        groups,
    }: {
        groups: FormServiceContext["initialState"]["groups"];
    },
) {
    // Determine the group to use for a given preset, otherwise use current opened group tab
    const useGroup = template
        ? template.group
            ? groups.filter(({ name }) => name === template.group)?.[0]?.id || "preset-group-not-found"
            : -1
        : undefined;

    const result: Partial<FormServiceValueProps> & { templateGroupNotFound: boolean } = {
        name: template?.name,
        group: typeof useGroup === "number" ? useGroup : undefined,
        templateGroupNotFound: useGroup === "preset-group-not-found",
        purpose: template?.purpose,
        isProviderCurrentWebsite: template?.isProviderCurrentWebsite,
        provider: template?.provider,
        providerContact: template?.providerContact
            ? {
                  phone: "",
                  email: "",
                  link: "",
                  ...(JSON.parse(JSON.stringify(template.providerContact)) as (typeof template)["providerContact"]),
              }
            : undefined,
        providerPrivacyPolicyUrl: template?.providerPrivacyPolicyUrl,
        providerLegalNoticeUrl: template?.providerLegalNoticeUrl,
        uniqueName: template?.identifier,
        isEmbeddingOnlyExternalResources: template?.isEmbeddingOnlyExternalResources,
        dataProcessingInCountries: template?.dataProcessingInCountries
            ? (JSON.parse(
                  JSON.stringify(template.dataProcessingInCountries),
              ) as (typeof template)["dataProcessingInCountries"])
            : undefined,
        dataProcessingInCountriesSpecialTreatments: template?.dataProcessingInCountriesSpecialTreatments
            ? (JSON.parse(
                  JSON.stringify(template.dataProcessingInCountriesSpecialTreatments),
              ) as (typeof template)["dataProcessingInCountriesSpecialTreatments"])
            : undefined,
        legalBasis: template?.legalBasis,
        technicalDefinitions: template?.technicalDefinitions
            ? (JSON.parse(JSON.stringify(template.technicalDefinitions)) as (typeof template)["technicalDefinitions"])
            : undefined,
        tagManagerOptInEventName: template?.tagManagerOptInEventName,
        tagManagerOptOutEventName: template?.tagManagerOptOutEventName,
        googleConsentModeConsentTypes: template?.googleConsentModeConsentTypes
            ? (JSON.parse(
                  JSON.stringify(template.googleConsentModeConsentTypes),
              ) as (typeof template)["googleConsentModeConsentTypes"])
            : undefined,
        executePriority: template?.executePriority || 10,
        codeOptIn: template?.codeOptIn,
        executeCodeOptInWhenNoTagManagerConsentIsGiven: template?.executeCodeOptInWhenNoTagManagerConsentIsGiven,
        codeOptOut: template?.codeOptOut,
        codeOnPageLoad: template?.codeOnPageLoad,
        executeCodeOptOutWhenNoTagManagerConsentIsGiven: template?.executeCodeOptOutWhenNoTagManagerConsentIsGiven,
        deleteTechnicalDefinitionsAfterOptOut: template?.deleteTechnicalDefinitionsAfterOptOut,
    };

    // Remove undefined and nulled values as the result of this function should only contain
    // real values so it can be used e.g. with `Object.assign`.
    for (const key in result) {
        if (["group"].indexOf(key) === -1 && (result[key] === undefined || result[key] === null)) {
            delete result[key];
        }
    }

    return result;
}

type UseFormHandler = typeof useFormHandler<FormServiceValueProps, ClientServiceTemplate["use"]>;

function useFormServiceHandler(
    opts: Pick<FormSettingsValueProps, "isGcm" | "setCookiesViaManager"> &
        Pick<FormServiceContext["initialState"], "territorialLegalBasis" | "groups"> & {
            /**
             * The selected group in e.g. the tab menu. Use `0` for none.
             */
            selectedGroup: number;
            allowContentBlockerCreation?: boolean;
            shouldUncheckContentBlockerCheckbox?: boolean;
        } & Omit<Parameters<UseFormHandler>[0], "defaultValues" | "i18n">,
): ReturnType<UseFormHandler> & {
    onFinish: (values: FormServiceValueProps) => void;
    onValuesChange: (changedValues: FormServiceValueProps, values: FormServiceValueProps) => void;
    contextValue: Partial<FormServiceContext["contextValue"]>;
} {
    const { __ } = useI18n();
    const {
        setCookiesViaManager,
        isGcm,
        territorialLegalBasis,
        selectedGroup,
        groups,
        allowContentBlockerCreation,
        shouldUncheckContentBlockerCheckbox,
        template,
        attributes,
        isEdit,
    } = opts;

    const contentBlockerTemplates = template?.consumerData.contentBlockerTemplates || [];

    const { templateGroupNotFound, ...defaultValuesFromTemplate } = formServiceAttributesFromTemplate(template, {
        groups,
    });
    const defaultValues: FormServiceValueProps = {
        // General
        name: "",
        // Can be `0`, so fallback to no selection (e.g. Cookie form modal in Content Blocker)
        group: selectedGroup || undefined,
        status: "publish",
        purpose: "",
        isProviderCurrentWebsite: false,
        provider: "",
        providerContact: {
            phone: "",
            email: "",
            link: "",
        },
        providerPrivacyPolicyUrl: "",
        providerLegalNoticeUrl: "",
        uniqueName: "",
        isEmbeddingOnlyExternalResources: false,
        dataProcessingInCountries: [],
        dataProcessingInCountriesSpecialTreatments: [],
        legalBasis: EServiceTemplateLegalBasis.Consent,
        technicalDefinitions: [TECHNICAL_DEFINITION_DEFAULTS],
        codeDynamics: {} as Record<string, string>,
        tagManagerOptInEventName: "",
        tagManagerOptOutEventName: "",
        googleConsentModeConsentTypes: [],
        codeOptIn: "",
        executeCodeOptInWhenNoTagManagerConsentIsGiven: false,
        codeOptOut: "",
        codeOnPageLoad: "",
        executeCodeOptOutWhenNoTagManagerConsentIsGiven: false,
        deleteTechnicalDefinitionsAfterOptOut: false,
        createContentBlocker: template?.consumerData.technicalHandlingIntegration?.name
            ? false
            : typeof attributes?.createContentBlocker === "boolean"
              ? attributes.createContentBlocker
              : contentBlockerTemplates.length > 0 &&
                allowContentBlockerCreation &&
                !shouldUncheckContentBlockerCheckbox,
        createContentBlockerId: attributes?.createContentBlockerId,
        templateCheck: !template,
        succeessorDeletionCheck: template?.consumerData.successorOf.length > 0 && !isEdit,
        ...defaultValuesFromTemplate,
        ...(attributes || {}),
    };

    // Reset group to empty value (e.g. VG Wort)
    defaultValues.group = defaultValues.group === -1 ? undefined : defaultValues.group;

    const handlers = useFormHandler<FormServiceValueProps, ClientServiceTemplate["use"]>({
        ...opts,
        defaultValues,
        i18n: {
            successMessage: __("You have successfully saved the service."),
            validationError: __("The service could not be saved due to missing/invalid form values."),
            unloadConfirm: __("You have unsaved changes. If you leave this page, your changes will be discarded."),
        },
    });
    const { form, setIsBusy, isTemplateUpdate, templateCheck } = handlers;

    // Overwrite onFinish to handle empty unique name and automatically generate it
    const onFinish = useCallback(
        async (values: FormServiceValueProps) => {
            const { uniqueName, name } = values;
            if (!uniqueName) {
                setIsBusy(true);
                form.setFieldsValue({
                    uniqueName: slugify(name),
                });

                const { uniqueName } = await form.validateFields(["uniqueName"]);
                values.uniqueName = uniqueName;
            }

            handlers.onFinish(values);
        },
        [handlers.onFinish, form, setIsBusy],
    );

    return {
        ...handlers,
        template,
        onFinish: onFinish as any,
        defaultValues,
        contextValue: {
            form,
            template,
            territorialLegalBasis,
            isEdit,
            isTemplateUpdate,
            templateCheck,
            setCookiesViaManager,
            isGcm,
            defaultTemplateValues: template ? defaultValues : {},
            allowLegalBasisLegalRequirement: template?.identifier === "real-cookie-banner",
            groups,
            preselectGroup: templateGroupNotFound ? "preset-group-not-found" : defaultValues.group,
            allowContentBlockerCreation,
            contentBlockerTemplates,
        },
    };
}

export { useFormServiceHandler, formServiceAttributesFromTemplate };
