import "keycloakify/tools/Array.prototype.every";
import { assert, type Equals } from "tsafe/assert";
import type {
    PasswordPolicies,
    Attribute,
    Validators
} from "keycloakify/login/KcContext";
import type { KcContext } from "../../KcContext";
import type { KcContextLike as KcContextLike_i18n } from "keycloakify/login/i18n";
import { formatNumber } from "keycloakify/tools/formatNumber";
import type { MessageKey_defaultSet } from "keycloakify/login/i18n";
import { emailRegexp } from "keycloakify/tools/emailRegExp";
import { unFormatNumberOnSubmit } from "./kcNumberUnFormat";
import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
import { id } from "tsafe/id";

export type FormFieldError = {
    advancedMsgArgs: readonly [string, ...string[]];
    source: FormFieldError.Source;
    fieldIndex: number | undefined;
};

export namespace FormFieldError {
    export type Source =
        | Source.Validator
        | Source.PasswordPolicy
        | Source.Server
        | Source.Other;

    export namespace Source {
        export type Validator = {
            type: "validator";
            name: keyof Validators;
        };
        export type PasswordPolicy = {
            type: "passwordPolicy";
            name: keyof PasswordPolicies;
        };
        export type Server = {
            type: "server";
        };

        export type Other = {
            type: "other";
            rule: "passwordConfirmMatchesPassword" | "requiredField";
        };
    }
}

export type FormFieldState = {
    attribute: Attribute;
    displayableErrors: FormFieldError[];
    valueOrValues: string | string[];
};

export type FormState = {
    isFormSubmittable: boolean;
    formFieldStates: FormFieldState[];
};

export type FormAction =
    | {
          action: "update";
          name: string;
          valueOrValues: string | string[];
          /** Default false */
          displayErrorsImmediately?: boolean;
      }
    | {
          action: "focus lost";
          name: string;
          fieldIndex: number | undefined;
      };

export type KcContextLike = KcContextLike_i18n &
    KcContextLike_useGetErrors & {
        profile: {
            attributesByName: Record<string, Attribute>;
            html5DataAnnotations?: Record<string, string>;
        };
        passwordRequired?: boolean;
        realm: { registrationEmailAsUsername: boolean };
        url: {
            resourcesPath: string;
        };
    };

type KcContextLike_useGetErrors = KcContextLike_i18n & {
    messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get">;
    passwordPolicies?: PasswordPolicies;
};

assert<
    Extract<
        Extract<KcContext, { profile: unknown }>,
        { pageId: "register.ftl" }
    > extends KcContextLike
        ? true
        : false
>();

export type UserProfileApi = {
    getFormState: () => FormState;
    subscribeToFormState: (callback: () => void) => { unsubscribe: () => void };
    dispatchFormAction: (action: FormAction) => void;
};

const cachedUserProfileApiByKcContext = new WeakMap<KcContextLike, UserProfileApi>();

export type ParamsOfGetUserProfileApi = {
    kcContext: KcContextLike;
    doMakeUserConfirmPassword: boolean;
};

export function getUserProfileApi(params: ParamsOfGetUserProfileApi): UserProfileApi {
    const { kcContext } = params;

    use_cache: {
        const userProfileApi_cache = cachedUserProfileApiByKcContext.get(kcContext);

        if (userProfileApi_cache === undefined) {
            break use_cache;
        }

        return userProfileApi_cache;
    }

    const userProfileApi = getUserProfileApi_noCache(params);

    cachedUserProfileApiByKcContext.set(kcContext, userProfileApi);

    return userProfileApi;
}

namespace internal {
    export type FormFieldState = {
        attribute: Attribute;
        errors: FormFieldError[];
        hasLostFocusAtLeastOnce: boolean | boolean[];
        valueOrValues: string | string[];
    };

    export type State = {
        formFieldStates: FormFieldState[];
    };
}

function getUserProfileApi_noCache(params: ParamsOfGetUserProfileApi): UserProfileApi {
    const { kcContext, doMakeUserConfirmPassword } = params;

    unFormatNumberOnSubmit();

    let state: internal.State = getInitialState({ kcContext });
    const callbacks = new Set<() => void>();

    return {
        dispatchFormAction: action => {
            state = reducer({ action, kcContext, doMakeUserConfirmPassword, state });

            callbacks.forEach(callback => callback());
        },
        getFormState: () => formStateSelector({ state }),
        subscribeToFormState: callback => {
            callbacks.add(callback);
            return {
                unsubscribe: () => {
                    callbacks.delete(callback);
                }
            };
        }
    };
}

function getInitialState(params: { kcContext: KcContextLike }): internal.State {
    const { kcContext } = params;

    const { getErrors } = createGetErrors({ kcContext });

    // NOTE: We don't use te kcContext.profile.attributes directly because
    // they don't includes the password and password confirm fields and we want to add them.
    // We also want to apply some retro-compatibility and consistency patches.
    const attributes: Attribute[] = (() => {
        mock_user_profile_attributes_for_older_keycloak_versions: {
            if (
                "profile" in kcContext &&
                "attributesByName" in kcContext.profile &&
                Object.keys(kcContext.profile.attributesByName).length !== 0
            ) {
                break mock_user_profile_attributes_for_older_keycloak_versions;
            }

            if (
                "register" in kcContext &&
                kcContext.register instanceof Object &&
                "formData" in kcContext.register
            ) {
                //NOTE: Handle legacy register.ftl page
                return (["firstName", "lastName", "email", "username"] as const)
                    .filter(name =>
                        name !== "username"
                            ? true
                            : !kcContext.realm.registrationEmailAsUsername
                    )
                    .map(name =>
                        id<Attribute>({
                            name: name,
                            displayName: id<`\${${MessageKey_defaultSet}}`>(
                                `\${${name}}`
                            ),
                            required: true,
                            value: (kcContext.register as any).formData[name] ?? "",
                            html5DataAnnotations: {},
                            readOnly: false,
                            validators: {},
                            annotations: {},
                            autocomplete: (() => {
                                switch (name) {
                                    case "email":
                                        return "email";
                                    case "username":
                                        return "username";
                                    default:
                                        return undefined;
                                }
                            })()
                        })
                    );
            }

            if ("user" in kcContext && kcContext.user instanceof Object) {
                //NOTE: Handle legacy login-update-profile.ftl
                return (["username", "email", "firstName", "lastName"] as const)
                    .filter(name =>
                        name !== "username"
                            ? true
                            : (kcContext.user as any).editUsernameAllowed
                    )
                    .map(name =>
                        id<Attribute>({
                            name: name,
                            displayName: id<`\${${MessageKey_defaultSet}}`>(
                                `\${${name}}`
                            ),
                            required: true,
                            value: (kcContext as any).user[name] ?? "",
                            html5DataAnnotations: {},
                            readOnly: false,
                            validators: {},
                            annotations: {},
                            autocomplete: (() => {
                                switch (name) {
                                    case "email":
                                        return "email";
                                    case "username":
                                        return "username";
                                    default:
                                        return undefined;
                                }
                            })()
                        })
                    );
            }

            if ("email" in kcContext && kcContext.email instanceof Object) {
                //NOTE: Handle legacy update-email.ftl
                return [
                    id<Attribute>({
                        name: "email",
                        displayName: id<`\${${MessageKey_defaultSet}}`>(`\${email}`),
                        required: true,
                        value: (kcContext.email as any).value ?? "",
                        html5DataAnnotations: {},
                        readOnly: false,
                        validators: {},
                        annotations: {},
                        autocomplete: "email"
                    })
                ];
            }

            assert(false, "Unable to mock user profile from the current kcContext");
        }

        return Object.values(kcContext.profile.attributesByName).map(
            structuredCloneButFunctions
        );
    })();

    // Retro-compatibility and consistency patches
    attributes.forEach(attribute => {
        patch_legacy_group: {
            if (typeof attribute.group !== "string") {
                break patch_legacy_group;
            }

            const {
                group,
                groupDisplayHeader,
                groupDisplayDescription,
                groupAnnotations
            } = attribute as Attribute & {
                group: string;
                groupDisplayHeader?: string;
                groupDisplayDescription?: string;
                groupAnnotations: Record<string, string>;
            };

            delete attribute.group;
            // @ts-expect-error
            delete attribute.groupDisplayHeader;
            // @ts-expect-error
            delete attribute.groupDisplayDescription;
            // @ts-expect-error
            delete attribute.groupAnnotations;

            if (group === "") {
                break patch_legacy_group;
            }

            attribute.group = {
                name: group,
                displayHeader: groupDisplayHeader,
                displayDescription: groupDisplayDescription,
                annotations: groupAnnotations,
                html5DataAnnotations: {}
            };
        }

        // Attributes with options rendered by default as select inputs
        if (
            attribute.validators.options !== undefined &&
            attribute.annotations.inputType === undefined
        ) {
            attribute.annotations.inputType = "select";
        }

        // Consistency patch on values/value property
        {
            if (getIsMultivaluedSingleField({ attribute })) {
                attribute.multivalued = true;
            }

            if (attribute.multivalued) {
                attribute.values ??=
                    attribute.value !== undefined ? [attribute.value] : [];
                delete attribute.value;
            } else {
                attribute.value ??= attribute.values?.[0];
                delete attribute.values;
            }
        }
    });

    add_password_and_password_confirm: {
        if (!kcContext.passwordRequired) {
            break add_password_and_password_confirm;
        }

        attributes.forEach((attribute, i) => {
            if (
                attribute.name !==
                (kcContext.realm.registrationEmailAsUsername ? "email" : "username")
            ) {
                // NOTE: We want to add password and password-confirm after the field that identifies the user.
                // It's either email or username.
                return;
            }

            attributes.splice(
                i + 1,
                0,
                {
                    name: "password",
                    displayName: id<`\${${MessageKey_defaultSet}}`>("${password}"),
                    required: true,
                    readOnly: false,
                    validators: {},
                    annotations: {},
                    autocomplete: "new-password",
                    html5DataAnnotations: {}
                },
                {
                    name: "password-confirm",
                    displayName: id<`\${${MessageKey_defaultSet}}`>("${passwordConfirm}"),
                    required: true,
                    readOnly: false,
                    validators: {},
                    annotations: {},
                    html5DataAnnotations: {},
                    autocomplete: "new-password"
                }
            );
        });
    }

    const initialFormFieldState: {
        attribute: Attribute;
        valueOrValues: string | string[];
    }[] = [];

    for (const attribute of attributes) {
        handle_multi_valued_attribute: {
            if (!attribute.multivalued) {
                break handle_multi_valued_attribute;
            }

            const values = attribute.values?.length ? attribute.values : [""];

            apply_validator_min_range: {
                if (getIsMultivaluedSingleField({ attribute })) {
                    break apply_validator_min_range;
                }

                const validator = attribute.validators.multivalued;

                if (validator === undefined) {
                    break apply_validator_min_range;
                }

                const { min: minStr } = validator;

                if (!minStr) {
                    break apply_validator_min_range;
                }

                const min = parseInt(`${minStr}`);

                for (let index = values.length; index < min; index++) {
                    values.push("");
                }
            }

            initialFormFieldState.push({
                attribute,
                valueOrValues: values
            });

            continue;
        }

        initialFormFieldState.push({
            attribute,
            valueOrValues: attribute.value ?? ""
        });
    }

    const initialState: internal.State = {
        formFieldStates: initialFormFieldState.map(({ attribute, valueOrValues }) => ({
            attribute,
            errors: getErrors({
                attributeName: attribute.name,
                formFieldStates: initialFormFieldState
            }),
            hasLostFocusAtLeastOnce:
                valueOrValues instanceof Array &&
                !getIsMultivaluedSingleField({ attribute })
                    ? valueOrValues.map(() => false)
                    : false,
            valueOrValues: valueOrValues
        }))
    };

    return initialState;
}

const formStateByState = new WeakMap<internal.State, FormState>();

function formStateSelector(params: { state: internal.State }): FormState {
    const { state } = params;

    use_memoized_value: {
        const formState = formStateByState.get(state);
        if (formState === undefined) {
            break use_memoized_value;
        }
        return formState;
    }

    return {
        formFieldStates: state.formFieldStates.map(
            ({
                errors,
                hasLostFocusAtLeastOnce: hasLostFocusAtLeastOnceOrArr,
                attribute,
                ...valueOrValuesWrap
            }) => ({
                displayableErrors: errors.filter(error => {
                    const hasLostFocusAtLeastOnce =
                        typeof hasLostFocusAtLeastOnceOrArr === "boolean"
                            ? hasLostFocusAtLeastOnceOrArr
                            : error.fieldIndex !== undefined
                              ? hasLostFocusAtLeastOnceOrArr[error.fieldIndex]
                              : hasLostFocusAtLeastOnceOrArr[
                                    hasLostFocusAtLeastOnceOrArr.length - 1
                                ];

                    switch (error.source.type) {
                        case "server":
                            return true;
                        case "other":
                            switch (error.source.rule) {
                                case "requiredField":
                                    return hasLostFocusAtLeastOnce;
                                case "passwordConfirmMatchesPassword":
                                    return hasLostFocusAtLeastOnce;
                            }
                            assert<Equals<typeof error.source.rule, never>>(false);
                        case "passwordPolicy":
                            switch (error.source.name) {
                                case "length":
                                    return hasLostFocusAtLeastOnce;
                                case "maxLength":
                                    return hasLostFocusAtLeastOnce;
                                case "digits":
                                    return hasLostFocusAtLeastOnce;
                                case "lowerCase":
                                    return hasLostFocusAtLeastOnce;
                                case "upperCase":
                                    return hasLostFocusAtLeastOnce;
                                case "specialChars":
                                    return hasLostFocusAtLeastOnce;
                                case "notUsername":
                                    return true;
                                case "notEmail":
                                    return true;
                            }
                            assert<Equals<typeof error.source, never>>(false);
                        case "validator":
                            switch (error.source.name) {
                                case "length":
                                    return hasLostFocusAtLeastOnce;
                                case "pattern":
                                    return hasLostFocusAtLeastOnce;
                                case "email":
                                    return hasLostFocusAtLeastOnce;
                                case "integer":
                                    return hasLostFocusAtLeastOnce;
                                case "multivalued":
                                    return hasLostFocusAtLeastOnce;
                                case "options":
                                    return hasLostFocusAtLeastOnce;
                            }
                            assert<Equals<typeof error.source, never>>(false);
                    }
                }),
                attribute,
                ...valueOrValuesWrap
            })
        ),
        isFormSubmittable: state.formFieldStates.every(
            ({ errors }) => errors.length === 0
        )
    };
}

function reducer(params: {
    state: internal.State;
    kcContext: KcContextLike;
    doMakeUserConfirmPassword: boolean;
    action: FormAction;
}): internal.State {
    const { kcContext, doMakeUserConfirmPassword, action } = params;
    let { state } = params;

    const { getErrors } = createGetErrors({ kcContext });

    const formFieldState = state.formFieldStates.find(
        ({ attribute }) => attribute.name === action.name
    );

    assert(formFieldState !== undefined);

    (() => {
        switch (action.action) {
            case "update":
                formFieldState.valueOrValues = action.valueOrValues;

                apply_formatters: {
                    const { attribute } = formFieldState;

                    const { kcNumberFormat } = attribute.html5DataAnnotations ?? {};

                    if (!kcNumberFormat) {
                        break apply_formatters;
                    }

                    if (formFieldState.valueOrValues instanceof Array) {
                        formFieldState.valueOrValues = formFieldState.valueOrValues.map(
                            value => formatNumber(value, kcNumberFormat)
                        );
                    } else {
                        formFieldState.valueOrValues = formatNumber(
                            formFieldState.valueOrValues,
                            kcNumberFormat
                        );
                    }
                }

                formFieldState.errors = getErrors({
                    attributeName: action.name,
                    formFieldStates: state.formFieldStates
                });

                simulate_focus_lost: {
                    const { displayErrorsImmediately = false } = action;

                    if (!displayErrorsImmediately) {
                        break simulate_focus_lost;
                    }

                    for (const fieldIndex of action.valueOrValues instanceof Array
                        ? action.valueOrValues.map((...[, index]) => index)
                        : [undefined]) {
                        state = reducer({
                            state,
                            kcContext,
                            doMakeUserConfirmPassword,
                            action: {
                                action: "focus lost",
                                name: action.name,
                                fieldIndex
                            }
                        });
                    }
                }

                update_password_confirm: {
                    if (doMakeUserConfirmPassword) {
                        break update_password_confirm;
                    }

                    if (action.name !== "password") {
                        break update_password_confirm;
                    }

                    state = reducer({
                        state,
                        kcContext,
                        doMakeUserConfirmPassword,
                        action: {
                            action: "update",
                            name: "password-confirm",
                            valueOrValues: action.valueOrValues,
                            displayErrorsImmediately: action.displayErrorsImmediately
                        }
                    });
                }

                trigger_password_confirm_validation_on_password_change: {
                    if (!doMakeUserConfirmPassword) {
                        break trigger_password_confirm_validation_on_password_change;
                    }

                    if (action.name !== "password") {
                        break trigger_password_confirm_validation_on_password_change;
                    }

                    state = reducer({
                        state,
                        kcContext,
                        doMakeUserConfirmPassword,
                        action: {
                            action: "update",
                            name: "password-confirm",
                            valueOrValues: (() => {
                                const formFieldState = state.formFieldStates.find(
                                    ({ attribute }) =>
                                        attribute.name === "password-confirm"
                                );

                                assert(formFieldState !== undefined);

                                return formFieldState.valueOrValues;
                            })(),
                            displayErrorsImmediately: action.displayErrorsImmediately
                        }
                    });
                }

                return;
            case "focus lost":
                if (formFieldState.hasLostFocusAtLeastOnce instanceof Array) {
                    const { fieldIndex } = action;
                    assert(fieldIndex !== undefined);
                    formFieldState.hasLostFocusAtLeastOnce[fieldIndex] = true;
                    return;
                }

                formFieldState.hasLostFocusAtLeastOnce = true;
                return;
        }
        assert<Equals<typeof action, never>>(false);
    })();

    return { ...state };
}

function createGetErrors(params: { kcContext: KcContextLike_useGetErrors }) {
    const { kcContext } = params;

    const { messagesPerField, passwordPolicies } = kcContext;

    function getErrors(params: {
        attributeName: string;
        formFieldStates: {
            attribute: Attribute;
            valueOrValues: string | string[];
        }[];
    }): FormFieldError[] {
        const { attributeName, formFieldStates } = params;

        const formFieldState = formFieldStates.find(
            ({ attribute }) => attribute.name === attributeName
        );

        assert(formFieldState !== undefined);

        const { attribute } = formFieldState;

        const valueOrValues = (() => {
            let { valueOrValues } = formFieldState;

            unFormat_number: {
                const { kcNumberUnFormat } = attribute.html5DataAnnotations ?? {};

                if (!kcNumberUnFormat) {
                    break unFormat_number;
                }

                if (valueOrValues instanceof Array) {
                    valueOrValues = valueOrValues.map(value =>
                        formatNumber(value, kcNumberUnFormat)
                    );
                } else {
                    valueOrValues = formatNumber(valueOrValues, kcNumberUnFormat);
                }
            }

            return valueOrValues;
        })();

        assert(attribute !== undefined);

        server_side_error: {
            if (attribute.multivalued) {
                const defaultValues = attribute.values?.length ? attribute.values : [""];

                assert(valueOrValues instanceof Array);

                const values = valueOrValues;

                if (
                    JSON.stringify(defaultValues) !==
                    JSON.stringify(values.slice(0, defaultValues.length))
                ) {
                    break server_side_error;
                }
            } else {
                const defaultValue = attribute.value ?? "";

                assert(typeof valueOrValues === "string");

                const value = valueOrValues;

                if (defaultValue !== value) {
                    break server_side_error;
                }
            }

            let doesErrorExist: boolean;

            try {
                doesErrorExist = messagesPerField.existsError(attributeName);
            } catch {
                break server_side_error;
            }

            if (!doesErrorExist) {
                break server_side_error;
            }

            const errorMessageStr = messagesPerField.get(attributeName);

            return [
                {
                    advancedMsgArgs: [errorMessageStr],
                    fieldIndex: undefined,
                    source: {
                        type: "server"
                    }
                }
            ];
        }

        handle_multi_valued_multi_fields: {
            if (!attribute.multivalued) {
                break handle_multi_valued_multi_fields;
            }

            if (getIsMultivaluedSingleField({ attribute })) {
                break handle_multi_valued_multi_fields;
            }

            assert(valueOrValues instanceof Array);

            const values = valueOrValues;

            const errors = values
                .map((...[, index]) => {
                    const specificValueErrors = getErrors({
                        attributeName,
                        formFieldStates: formFieldStates.map(formFieldState => {
                            if (formFieldState.attribute.name === attributeName) {
                                assert(formFieldState.valueOrValues instanceof Array);
                                return {
                                    attribute: {
                                        ...attribute,
                                        annotations: {
                                            ...attribute.annotations,
                                            inputType: undefined
                                        },
                                        multivalued: false
                                    },
                                    valueOrValues: formFieldState.valueOrValues[index]
                                };
                            }

                            return formFieldState;
                        })
                    });

                    return specificValueErrors
                        .filter(error => {
                            if (
                                error.source.type === "other" &&
                                error.source.rule === "requiredField"
                            ) {
                                return false;
                            }

                            return true;
                        })
                        .map(
                            (error): FormFieldError => ({
                                ...error,
                                fieldIndex: index
                            })
                        );
                })
                .reduce((acc, errors) => [...acc, ...errors], []);

            required_field: {
                if (!attribute.required) {
                    break required_field;
                }

                if (values.every(value => value !== "")) {
                    break required_field;
                }

                errors.push({
                    advancedMsgArgs: [
                        "error-user-attribute-required" satisfies MessageKey_defaultSet
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "other",
                        rule: "requiredField"
                    }
                });
            }

            return errors;
        }

        handle_multi_valued_single_field: {
            if (!attribute.multivalued) {
                break handle_multi_valued_single_field;
            }

            if (!getIsMultivaluedSingleField({ attribute })) {
                break handle_multi_valued_single_field;
            }

            const validatorName = "multivalued";

            const validator = attribute.validators[validatorName];

            if (validator === undefined) {
                return [];
            }

            const { min: minStr } = validator;

            const min = minStr ? parseInt(`${minStr}`) : attribute.required ? 1 : 0;

            assert(!isNaN(min));

            const { max: maxStr } = validator;

            const max = !maxStr ? Infinity : parseInt(`${maxStr}`);

            assert(!isNaN(max));

            assert(valueOrValues instanceof Array);

            const values = valueOrValues;

            if (min <= values.length && values.length <= max) {
                return [];
            }

            return [
                {
                    advancedMsgArgs: [
                        "error-invalid-multivalued-size" satisfies MessageKey_defaultSet,
                        `${min}`,
                        `${max}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "validator",
                        name: validatorName
                    }
                }
            ];
        }

        assert(typeof valueOrValues === "string");

        const value = valueOrValues;

        const errors: FormFieldError[] = [];

        check_password_policies: {
            if (attributeName !== "password") {
                break check_password_policies;
            }

            if (passwordPolicies === undefined) {
                break check_password_policies;
            }

            check_password_policy_x: {
                const policyName = "length";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const minLength = policy;

                if (value.length >= minLength) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMinLengthMessage" satisfies MessageKey_defaultSet,
                        `${minLength}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "maxLength";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const maxLength = policy;

                if (value.length <= maxLength) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMaxLengthMessage" satisfies MessageKey_defaultSet,
                        `${maxLength}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "digits";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const minNumberOfDigits = policy;

                if (
                    value.split("").filter(char => !isNaN(parseInt(char))).length >=
                    minNumberOfDigits
                ) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMinDigitsMessage" satisfies MessageKey_defaultSet,
                        `${minNumberOfDigits}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "lowerCase";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const minNumberOfLowerCaseChar = policy;

                if (
                    value
                        .split("")
                        .filter(
                            char =>
                                char === char.toLowerCase() && char !== char.toUpperCase()
                        ).length >= minNumberOfLowerCaseChar
                ) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMinLowerCaseCharsMessage" satisfies MessageKey_defaultSet,
                        `${minNumberOfLowerCaseChar}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "upperCase";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const minNumberOfUpperCaseChar = policy;

                if (
                    value
                        .split("")
                        .filter(
                            char =>
                                char === char.toUpperCase() && char !== char.toLowerCase()
                        ).length >= minNumberOfUpperCaseChar
                ) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMinUpperCaseCharsMessage" satisfies MessageKey_defaultSet,
                        `${minNumberOfUpperCaseChar}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "specialChars";

                const policy = passwordPolicies[policyName];

                if (!policy) {
                    break check_password_policy_x;
                }

                const minNumberOfSpecialChar = policy;

                if (
                    value.split("").filter(char => !char.match(/[a-zA-Z0-9]/)).length >=
                    minNumberOfSpecialChar
                ) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordMinSpecialCharsMessage" satisfies MessageKey_defaultSet,
                        `${minNumberOfSpecialChar}`
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "notUsername";

                const notUsername = passwordPolicies[policyName];

                if (!notUsername) {
                    break check_password_policy_x;
                }

                const usernameFormFieldState = formFieldStates.find(
                    formFieldState => formFieldState.attribute.name === "username"
                );

                if (!usernameFormFieldState) {
                    break check_password_policy_x;
                }

                const usernameValue = (() => {
                    let { valueOrValues } = usernameFormFieldState;

                    assert(typeof valueOrValues === "string");

                    unFormat_number: {
                        const { kcNumberUnFormat } = attribute.html5DataAnnotations ?? {};

                        if (!kcNumberUnFormat) {
                            break unFormat_number;
                        }

                        valueOrValues = formatNumber(valueOrValues, kcNumberUnFormat);
                    }

                    return valueOrValues;
                })();

                if (usernameValue === "") {
                    break check_password_policy_x;
                }

                if (value !== usernameValue) {
                    break check_password_policy_x;
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordNotUsernameMessage" satisfies MessageKey_defaultSet
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }

            check_password_policy_x: {
                const policyName = "notEmail";

                const notEmail = passwordPolicies[policyName];

                if (!notEmail) {
                    break check_password_policy_x;
                }

                const emailFormFieldState = formFieldStates.find(
                    formFieldState => formFieldState.attribute.name === "email"
                );

                if (!emailFormFieldState) {
                    break check_password_policy_x;
                }

                assert(typeof emailFormFieldState.valueOrValues === "string");

                {
                    const emailValue = emailFormFieldState.valueOrValues;

                    if (emailValue === "") {
                        break check_password_policy_x;
                    }

                    if (value !== emailValue) {
                        break check_password_policy_x;
                    }
                }

                errors.push({
                    advancedMsgArgs: [
                        "invalidPasswordNotEmailMessage" satisfies MessageKey_defaultSet
                    ] as const,
                    fieldIndex: undefined,
                    source: {
                        type: "passwordPolicy",
                        name: policyName
                    }
                });
            }
        }

        password_confirm_matches_password: {
            if (attributeName !== "password-confirm") {
                break password_confirm_matches_password;
            }

            const passwordFormFieldState = formFieldStates.find(
                formFieldState => formFieldState.attribute.name === "password"
            );

            assert(passwordFormFieldState !== undefined);

            assert(typeof passwordFormFieldState.valueOrValues === "string");

            {
                const passwordValue = passwordFormFieldState.valueOrValues;

                if (value === passwordValue) {
                    break password_confirm_matches_password;
                }
            }

            errors.push({
                advancedMsgArgs: [
                    "invalidPasswordConfirmMessage" satisfies MessageKey_defaultSet
                ] as const,
                fieldIndex: undefined,
                source: {
                    type: "other",
                    rule: "passwordConfirmMatchesPassword"
                }
            });
        }

        const { validators } = attribute;

        required_field: {
            if (!attribute.required) {
                break required_field;
            }

            if (value !== "") {
                break required_field;
            }

            errors.push({
                advancedMsgArgs: [
                    "error-user-attribute-required" satisfies MessageKey_defaultSet
                ] as const,
                fieldIndex: undefined,
                source: {
                    type: "other",
                    rule: "requiredField"
                }
            });
        }

        validator_x: {
            const validatorName = "length";

            const validator = validators[validatorName];

            if (!validator) {
                break validator_x;
            }

            const {
                "ignore.empty.value": ignoreEmptyValue = false,
                max,
                min
            } = validator;

            if (ignoreEmptyValue && value === "") {
                break validator_x;
            }

            const source: FormFieldError.Source = {
                type: "validator",
                name: validatorName
            };

            if (max && value.length > parseInt(`${max}`)) {
                errors.push({
                    advancedMsgArgs: [
                        "error-invalid-length-too-long" satisfies MessageKey_defaultSet,
                        `${max}`
                    ] as const,
                    fieldIndex: undefined,
                    source
                });
            }

            if (min && value.length < parseInt(`${min}`)) {
                errors.push({
                    advancedMsgArgs: [
                        "error-invalid-length-too-short" satisfies MessageKey_defaultSet,
                        `${min}`
                    ] as const,
                    fieldIndex: undefined,
                    source
                });
            }
        }

        validator_x: {
            const validatorName = "pattern";

            const validator = validators[validatorName];

            if (validator === undefined) {
                break validator_x;
            }

            const {
                "ignore.empty.value": ignoreEmptyValue = false,
                pattern,
                "error-message": errorMessageKey
            } = validator;

            if (ignoreEmptyValue && value === "") {
                break validator_x;
            }

            if (new RegExp(pattern).test(value)) {
                break validator_x;
            }

            const msgArgs = [
                errorMessageKey ?? ("shouldMatchPattern" satisfies MessageKey_defaultSet),
                pattern
            ] as const;

            errors.push({
                advancedMsgArgs: msgArgs,
                fieldIndex: undefined,
                source: {
                    type: "validator",
                    name: validatorName
                }
            });
        }

        validator_x: {
            {
                const lastError = errors[errors.length - 1];
                if (
                    lastError !== undefined &&
                    lastError.source.type === "validator" &&
                    lastError.source.name === "pattern"
                ) {
                    break validator_x;
                }
            }

            const validatorName = "email";

            const validator = validators[validatorName];

            if (validator === undefined) {
                break validator_x;
            }

            const { "ignore.empty.value": ignoreEmptyValue = false } = validator;

            if (ignoreEmptyValue && value === "") {
                break validator_x;
            }

            if (emailRegexp.test(value)) {
                break validator_x;
            }

            errors.push({
                advancedMsgArgs: [
                    "invalidEmailMessage" satisfies MessageKey_defaultSet
                ] as const,
                fieldIndex: undefined,
                source: {
                    type: "validator",
                    name: validatorName
                }
            });
        }

        validator_x: {
            const validatorName = "integer";

            const validator = validators[validatorName];

            if (validator === undefined) {
                break validator_x;
            }

            const {
                "ignore.empty.value": ignoreEmptyValue = false,
                max,
                min
            } = validator;

            if (ignoreEmptyValue && value === "") {
                break validator_x;
            }

            const intValue = parseInt(value);

            const source: FormFieldError.Source = {
                type: "validator",
                name: validatorName
            };

            if (isNaN(intValue)) {
                const msgArgs = ["mustBeAnInteger"] as const;

                errors.push({
                    advancedMsgArgs: msgArgs,
                    fieldIndex: undefined,
                    source
                });

                break validator_x;
            }

            if (max && intValue > parseInt(`${max}`)) {
                errors.push({
                    advancedMsgArgs: [
                        "error-number-out-of-range-too-big" satisfies MessageKey_defaultSet,
                        `${max}`
                    ] as const,
                    fieldIndex: undefined,
                    source
                });

                break validator_x;
            }

            if (min && intValue < parseInt(`${min}`)) {
                errors.push({
                    advancedMsgArgs: [
                        "error-number-out-of-range-too-small" satisfies MessageKey_defaultSet,
                        `${min}`
                    ] as const,
                    fieldIndex: undefined,
                    source
                });
                break validator_x;
            }
        }

        validator_x: {
            const validatorName = "options";

            const validator = validators[validatorName];

            if (validator === undefined) {
                break validator_x;
            }

            if (value === "") {
                break validator_x;
            }

            if (validator.options.indexOf(value) >= 0) {
                break validator_x;
            }

            errors.push({
                advancedMsgArgs: [
                    "notAValidOption" satisfies MessageKey_defaultSet
                ] as const,
                fieldIndex: undefined,
                source: {
                    type: "validator",
                    name: validatorName
                }
            });
        }

        //TODO: Implement missing validators. See Validators type definition.

        return errors;
    }

    return { getErrors };
}

function getIsMultivaluedSingleField(params: { attribute: Attribute }) {
    const { attribute } = params;

    return attribute.annotations.inputType?.startsWith("multiselect") ?? false;
}

export function getButtonToDisplayForMultivaluedAttributeField(params: {
    attribute: Attribute;
    values: string[];
    fieldIndex: number;
}) {
    const { attribute, values, fieldIndex } = params;

    const hasRemove = (() => {
        if (values.length === 1) {
            return false;
        }

        const minCount = (() => {
            const { multivalued } = attribute.validators;

            if (multivalued === undefined) {
                return undefined;
            }

            const minStr = multivalued.min;

            if (minStr === undefined) {
                return undefined;
            }

            return parseInt(`${minStr}`);
        })();

        if (minCount === undefined) {
            return true;
        }

        if (values.length === minCount) {
            return false;
        }

        return true;
    })();

    const hasAdd = (() => {
        if (fieldIndex + 1 !== values.length) {
            return false;
        }

        const maxCount = (() => {
            const { multivalued } = attribute.validators;

            if (multivalued === undefined) {
                return undefined;
            }

            const maxStr = multivalued.max;

            if (maxStr === undefined) {
                return undefined;
            }

            return parseInt(`${maxStr}`);
        })();

        if (maxCount === undefined) {
            return true;
        }

        return values.length !== maxCount;
    })();

    return { hasRemove, hasAdd };
}
