/**
 * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
 * Node module: @schukai/monster
 *
 * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
 * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
 *
 * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
 * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
 * For more information about purchasing a commercial license, please contact Volker Schukai.
 */

import { instanceSymbol } from "../../constants.mjs";
import { addAttributeToken } from "../../dom/attributes.mjs";
import {
	ATTRIBUTE_ERRORMESSAGE,
	ATTRIBUTE_ROLE,
} from "../../dom/constants.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
	assembleMethodSymbol,
	registerCustomElement,
} from "../../dom/customelement.mjs";
import { findTargetElementFromEvent, fireEvent } from "../../dom/events.mjs";
import { LoginStyleSheet } from "./stylesheet/login.mjs";
import { fireCustomEvent } from "../../dom/events.mjs";

import "./form.mjs";
import "./field-set.mjs";
import "./password.mjs";
import "./message-state-button.mjs";
import "./digits.mjs";
import "../layout/collapse.mjs";
import "../datatable/datasource/rest.mjs";

import { isArray, isString, isObject, isFunction } from "../../types/is.mjs";

import { InvalidStyleSheet } from "./stylesheet/invalid.mjs";
import { getLocaleOfDocument } from "../../dom/locale.mjs";
import { getWindow } from "../../dom/util.mjs";

export { Login };

/**
 * @private
 * @type {symbol}
 */
const loginElementSymbol = Symbol("loginElement");

/**
 * @private
 * @type {symbol}
 */
const forgotPasswordLinkSymbol = Symbol("forgotPasswordLink");

/**
 * @private
 * @type {symbol}
 */
const forgotPasswordCollapseSymbol = Symbol("forgotPasswordCollapse");

/**
 * @private
 * @type {symbol}
 */
const loginCollapseSymbol = Symbol("loginCollapse");

/**
 * @private
 * @type {symbol}
 */
const loginLinkSymbol = Symbol("loginLink");

/**
 * @private
 * @type {symbol}
 */
const secondFactorCollapseSymbol = Symbol("secondFactorCollapse");

/**
 * @private
 * @type {symbol}
 */
const resetLoginProcessLinksSymbol = Symbol("resetLoginProcessLink");

/**
 * @private
 * @type {symbol}
 */
const loggedInCollapseSymbol = Symbol("loggedInCollapse");

/**
 * @private
 * @type {symbol}
 */
const customCollapseSymbol = Symbol("customCollapse");

/**
 * @private
 * @type {symbol}
 */
const customCollapseHeaderSymbol = Symbol("customCollapseHeader");

/**
 * @private
 * @type {symbol}
 */
const customCollapseContentSymbol = Symbol("customCollapseContent");

/**
 * @private
 * @type {symbol}
 */
const customCollapseFooterSymbol = Symbol("customCollapseFooter");

/**
 * @private
 * @type {symbol}
 */
const loginButtonSymbol = Symbol("loginButton");

/**
 * @private
 * @type {symbol}
 */
const requestLinkButtonSymbol = Symbol("requestLinkButton");

/**
 * @private
 * @type {symbol}
 */
const digitsButtonSymbol = Symbol("digitsButton");

/**
 * @private
 * @type {symbol}
 */
const secondFactorButtonSymbol = Symbol("secondFactorButton");

/**
 * @private
 * @type {symbol}
 */
const digitsCollapseSymbol = Symbol("digitsCollapse");

/**
 * A Login
 *
 * @fragments /fragments/components/form/login/
 *
 * @example /examples/components/form/login-simple
 * @example /examples/components/form/login-with-forgot-password
 * @example /examples/components/form/login-with-second-factor
 * @example /examples/components/form/login-with-success-redirect
 * @example /examples/components/form/login-with-actions
 * @example /examples/components/form/login-custom-callbacks
 * @example /examples/components/form/login-multilingual
 * @example /examples/components/form/login-custom-buttons
 * @example /examples/components/form/login-accessibility
 * @example /examples/components/form/login-style-overrides
 *
 * @since 3.113.0
 * @copyright Volker Schukai
 * @summary A modular and accessible login control for Monster.js applications.
 *
 * @fires login-success                         Fired after successful login (always)
 * @fires redirect-to-first-success-url         Fired if feature redirectToFirstSuccessUrl is enabled
 * @fires second-factor-success                 Fired after successful second factor validation
 * @fires digits-success                        Fired after successful password reset code validation
 * @fires monster-login-clicked                 Fired on any click inside the login control (before internal handling)
 */

class Login extends CustomElement {
	/**
	 * This method is called by the `instanceof` operator.
	 * @returns {symbol}
	 */
	static get [instanceSymbol]() {
		return Symbol.for("@schukai/monster/components/form/login@@instance");
	}

	/**
	 * @return {void}
	 */
	[assembleMethodSymbol]() {
		super[assembleMethodSymbol]();

		setTimeout(() => {
			initControlReferences.call(this);
			initEventHandler.call(this);

			setTimeout(() => {
				this.shadowRoot.querySelector("input[name='username']").focus();
			}, 100);
		}, 0);
	}

	/**
	 * To set the options via the HTML Tag, the attribute `data-monster-options` must be used.
	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
	 *
	 * The individual configuration values can be found in the table.
	 *
	 * @property {Object} templates Template definitions
	 * @property {string} templates.main The main HTML template used for rendering the login form
	 * @property {Object} labels Label definitions used for localization and form messages
	 * @property {string} labels.username Label for the username or email field
	 * @property {string} labels.password Label for the password field
	 * @property {string} labels.login Label for the login button
	 * @property {string} labels.forgotPasswordLink Text for the "forgot password" link
	 * @property {string} labels.mailAddress Label for the email input in password reset flow
	 * @property {string} labels.requestLink Label for the button that sends a password reset code
	 * @property {string} labels.digits Label for the digits input field
	 * @property {string} labels.loginLink Label for the back-to-login link
	 * @property {string} labels.secondFactor Label for the second factor authentication input
	 * @property {string} labels.sendDigits Label for the button that submits the digits input
	 * @property {string} labels.sendSecondFactorDigits Label for the button that submits the second factor code
	 * @property {string} labels.resetLoginProcess Label for the link to return to the login form
	 * @property {string} labels.messageEmptyUserName Message shown when username is empty
	 * @property {string} labels.messageEmptyPassword Message shown when password is empty
	 * @property {string} labels.messageEmptyBoth Message shown when both username and password are empty
	 * @property {string} labels.messageEmptyEmail Message shown when email field is empty
	 * @property {string} labels.messageInvalidEmail Message shown when an invalid email address is entered
	 * @property {string} labels.digitsEmpty Message shown when digits field is empty
	 * @property {string} labels.digitsInvalid Message shown when digits input is invalid
	 * @property {string} labels.messageLoginFailed Message shown on failed login attempt
	 * @property {string} labels.messageForbidden Message shown on successful login with insufficient permissions
	 * @property {string} labels.messageSomethingWentWrong Fallback error message
	 * @property {string} labels.messageThisFormIsNotConfigured Message shown if no backend URL is configured
	 * @property {string} labels.messagePasswordResetDisabled Message shown if password reset is disabled due to 2FA
	 * @property {Object} classes Class definitions for visual styling
	 * @property {string} classes.usernameInvalid CSS class applied when username is invalid
	 * @property {string} classes.passwordInvalid CSS class applied when password is invalid
	 * @property {string} classes.emailInvalid CSS class applied when email input is invalid
	 * @property {string} classes.button CSS class applied to all form buttons
	 * @property {boolean} disabled If true, disables interaction with the component
	 * @property {Object} features Feature flags to toggle optional behavior
	 * @property {boolean} features.forgotPassword Enables the forgot password flow
	 * @property {boolean} features.redirectToFirstSuccessUrl If true, redirects to the first success URL after login
	 * @property {Object} actions Action definitions for custom event handling
	 * @property {Function} actions.click Callback function for generic click actions within the login component
	 * @property {Object} callbacks Optional callback hooks for modifying internal behavior
	 * @property {Function} callbacks.username A function that receives and can transform the entered username before submission
	 * @property {Function} callbacks.forgotPassword A function that receives and can transform the entered email before submission
	 * @property {Object} behavior Behavior options for response handling
	 * @property {number[]} behavior.passwordResetDisabledStatusCodes HTTP status codes that indicate reset is disabled
	 * @property {number} digits Number of digits required for second factor or password reset code input
	 * @property {Object[]} successUrls List of URLs shown after successful login (e.g., home or logout)
	 * @property {string} successUrls.label Label for the success URL (displayed)
	 * @property {string} successUrls.url Actual target URL
	 * @property {number} timeoutForMessage Duration in milliseconds to show error messages
	 * @property {number} timeoutForSuccess Duration in milliseconds before triggering the post-login success state
	 * @property {Object} accessKeys Keyboard access keys for accessibility and shortcuts
	 * @property {string} accessKeys.loginLink Access key for switching to login form
	 * @property {string} accessKeys.username Access key for focusing the username field
	 * @property {string} accessKeys.password Access key for focusing the password field
	 * @property {Object} fetch Definitions for backend requests to support login workflows
	 * @property {Object} placeholder Placeholder text for input fields
	 * @property {string} placeholder.username Placeholder for the username field
	 * @property {string} placeholder.password Placeholder for the password field
	 * @property {string} placeholder.email Placeholder for the email field
	 * @property {Object} autocomplete Autocomplete settings for the form fields
	 * @property {string} autocomplete.username Autocomplete value for the username field
	 * @property {string} autocomplete.password Autocomplete value for the password field
	 * @property {string} autocomplete.email Autocomplete value for the email field
	 * @property {Object} fetch.login Fetch config for login request
	 * @property {string} fetch.login.url Endpoint to post login credentials to
	 * @property {string} fetch.login.method HTTP method for login (e.g., "POST")
	 * @property {string} fetch.login.mode Fetch mode (e.g., "same-origin")
	 * @property {Object} fetch.login.headers HTTP headers to be sent with the login request
	 * @property {string} fetch.login.headers.accept Accept header value
	 * @property {string} fetch.login.headers.Content-Type Content-Type header value
	 * @property {string} fetch.login.credentials Credential mode (e.g., "same-origin")
	 * @property {Object} fetch.login.payload Field mappings for form inputs
	 * @property {string} fetch.login.payload.identifier Name of the identifier field (username/email)
	 * @property {string} fetch.login.payload.password Name of the password field
	 * @property {Object} fetch.forgotPassword Fetch config for password reset code request
	 * @property {string} fetch.forgotPassword.url Endpoint to request a reset link/code
	 * @property {string} fetch.forgotPassword.method HTTP method
	 * @property {string} fetch.forgotPassword.mode Fetch mode
	 * @property {Object} fetch.forgotPassword.headers Headers
	 * @property {string} fetch.forgotPassword.headers.accept Accept header
	 * @property {string} fetch.forgotPassword.headers.Content-Type Content-Type header
	 * @property {string} fetch.forgotPassword.credentials Credential mode
	 * @property {Object} fetch.digits Fetch config for submitting password reset code
	 * @property {string} fetch.digits.url Endpoint for validating the code
	 * @property {string} fetch.digits.method HTTP method
	 * @property {string} fetch.digits.mode Fetch mode
	 * @property {Object} fetch.digits.headers Headers
	 * @property {string} fetch.digits.headers.accept Accept header
	 * @property {string} fetch.digits.headers.Content-Type Content-Type header
	 * @property {string} fetch.digits.credentials Credential mode
	 * @property {Object} fetch.secondFactor Fetch config for submitting second factor code
	 * @property {string} fetch.secondFactor.url Endpoint to validate second factor code
	 * @property {string} fetch.secondFactor.method HTTP method
	 * @property {string} fetch.secondFactor.mode Fetch mode
	 * @property {Object} fetch.secondFactor.headers Headers
	 * @property {string} fetch.secondFactor.headers.accept Accept header
	 * @property {string} fetch.secondFactor.headers.Content-Type Content-Type header
	 * @property {string} fetch.secondFactor.credentials Credential mode
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},
			labels: getTranslations(),
			classes: {
				usernameInvalid: "",
				passwordInvalid: "",
				emailInvalid: "",
				button: "monster-button-outline-primary",
			},
			disabled: false,
			features: {
				forgotPassword: true,
				redirectToFirstSuccessUrl: false,
			},
			actions: {},

			callbacks: {
				username: null,
				forgotPassword: null,
			},

			behavior: {
				passwordResetDisabledStatusCodes: [401],
			},

			digits: 6,

			successUrls: [
				{
					label: "Home",
					url: "/",
				},
				{
					label: "Logout",
					url: "/logout",
				},
			],

			timeoutForMessage: 3500,
			timeoutForSuccess: 1000,

			accessKeys: {
				loginLink: "x",
				username: "u",
				password: "p",
			},

			placeholder: {
				username: "",
				password: "",
				email: "",
			},

			autocomplete: {
				username: "off",
				password: "off",
				email: "off",
			},

			fetch: {
				login: {
					url: "",
					method: "POST",
					mode: "same-origin",
					headers: {
						Accept: "application/json",
						"Content-Type": "application/json; charset=utf-8",
					},
					credentials: "same-origin",

					payload: {
						identifier: "identifier",
						password: "password",
					},
				},
				forgotPassword: {
					url: "",
					method: "POST",
					mode: "same-origin",
					headers: {
						Accept: "application/json",
						"Content-Type": "application/json; charset=utf-8",
					},
					credentials: "same-origin",
				},
				digits: {
					url: "",
					method: "POST",
					mode: "same-origin",
					headers: {
						Accept: "application/json",
						"Content-Type": "application/json; charset=utf-8",
					},
					credentials: "same-origin",
				},
				secondFactor: {
					url: "",
					method: "POST",
					mode: "same-origin",
					headers: {
						Accept: "application/json",
						"Content-Type": "application/json; charset=utf-8",
					},
					credentials: "same-origin",
				},
			},
		});
	}

	/**
	 * Opens the login collapse and focuses the username control.
	 *
	 * @returns {Login}
	 */
	openLogin() {
		this[loginCollapseSymbol].open();
		this.shadowRoot.querySelector("input[name='username']").focus();
		return this;
	}

	/**
	 * Opens the forgot password collapse. If the feature `forgotPassword` is not enabled, an error will be thrown.
	 *
	 * @returns {Login}
	 * @throws {Error} If the feature is not enabled
	 */
	openForgotPassword() {
		if (!this.getOption("features.forgotPassword")) {
			throw new Error("Forgot Password is not enabled");
		}

		this[forgotPasswordCollapseSymbol].open();
		return this;
	}

	/**
	 * Opens the second factor collapse and focuses the second factor control.
	 * @returns {Login}
	 */
	openSecondFactor() {
		this[secondFactorCollapseSymbol].open();
		this.shadowRoot.getElementById("secondFactorButton").focus();
		return this;
	}

	/**
	 * Opens the digits collapse and focuses the digit control.
	 * @returns {Login}
	 */
	openDigits() {
		this[digitsCollapseSymbol].open();
		this.shadowRoot.getElementById("digitsControl").focus();
		return this;
	}

	/**
	 * Opens the logged in collapse. If the feature `redirectToFirstSuccessUrl` is enabled, the user will be redirected to the first success URL.
	 *
	 * @returns {Login}
	 */
	openLoggedIn() {
		fireEvent(this, "login-success");

		if (this.getOption("features.redirectToFirstSuccessUrl")) {
			setTimeout(() => {
				fireEvent(this, "redirect-to-first-success-url");

				const successUrl = this.getOption("successUrls");
				if (isArray(successUrl) && successUrl.length > 0) {
					const success = successUrl[0].url;
					if (success) {
						getWindow().location.href = success;
					}
				} else if (isFunction(successUrl)) {
					const url = successUrl();
					if (url) {
						getWindow().location.href = url;
					}
				} else if (isString(successUrl)) {
					getWindow().location.href = successUrl;
				} else {
					console.warn(
						"[Monster Login] No valid success URL found for redirection.",
					);
				}
			}, this.getOption("timeoutForSuccess"));

			return;
		}

		this[loggedInCollapseSymbol].open();

		return this;
	}

	/**
	 * Opens the custom collapse and focuses the first focusable element if available.
	 *
	 * @returns {Login}
	 */
	openCustomCollapse() {
		this[customCollapseSymbol].open();
		focusFirstCustomCollapseElement.call(this);
		return this;
	}

	/**
	 * Replaces the header content of the custom collapse.
	 *
	 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
	 * @returns {Login}
	 */
	setCustomCollapseHeader(content) {
		setCustomCollapseSectionContent.call(
			this,
			this[customCollapseHeaderSymbol],
			content,
		);
		return this;
	}

	/**
	 * Replaces the main content of the custom collapse.
	 *
	 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
	 * @returns {Login}
	 */
	setCustomCollapseContent(content) {
		setCustomCollapseSectionContent.call(
			this,
			this[customCollapseContentSymbol],
			content,
		);
		return this;
	}

	/**
	 * Replaces the footer content of the custom collapse.
	 *
	 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
	 * @returns {Login}
	 */
	setCustomCollapseFooter(content) {
		setCustomCollapseSectionContent.call(
			this,
			this[customCollapseFooterSymbol],
			content,
		);
		return this;
	}

	/**
	 * Clears all custom collapse sections.
	 *
	 * @returns {Login}
	 */
	clearCustomCollapse() {
		this[customCollapseHeaderSymbol].replaceChildren();
		this[customCollapseContentSymbol].replaceChildren();
		this[customCollapseFooterSymbol].replaceChildren();
		return this;
	}

	/**
	 * Replaces the custom collapse content and opens it.
	 *
	 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|Object|null|undefined} content
	 * @returns {Login}
	 */
	showCustomCollapse(content) {
		if (isObject(content) && !(content instanceof Node) && !isArray(content)) {
			if ("header" in content) {
				this.setCustomCollapseHeader(content.header);
			}
			if ("content" in content) {
				this.setCustomCollapseContent(content.content);
			}
			if ("footer" in content) {
				this.setCustomCollapseFooter(content.footer);
			}
		} else if (content !== undefined) {
			this.setCustomCollapseContent(content);
		}

		return this.openCustomCollapse();
	}

	/**
	 * @return {string}
	 */
	static getTag() {
		return "monster-login";
	}

	/**
	 * @return {CSSStyleSheet[]}
	 */
	static getCSSStyleSheet() {
		return [LoginStyleSheet, InvalidStyleSheet];
	}
}

function getTranslations() {
	const locale = getLocaleOfDocument();
	switch (locale.language) {
		case "de":
			return {
				username: "Benutzername oder E-Mail",
				password: "Passwort",
				login: "Anmelden",
				forgotPasswordLink: "Passwort vergessen?",
				mailAddress: "E-Mail-Adresse",
				requestLink: "Code anfordern",
				digits: "Code",
				loginLink: "Anmelden",
				secondFactor: "Zweiter Faktor Code",
				sendDigits: "Code senden",
				sendSecondFactorDigits: "Code senden",
				resetLoginProcess: "Zurück zum Login",
				messageEmptyUserName: "Bitte geben Sie Ihren Benutzernamen ein",
				messageEmptyPassword: "Bitte geben Sie Ihr Passwort ein",
				messageEmptyBoth:
					"Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein",
				messageEmptyEmail: "Bitte geben Sie Ihre E-Mail-Adresse ein",
				messageInvalidEmail: "Bitte geben Sie eine gültige E-Mail-Adresse ein",
				digitsEmpty: "Bitte geben Sie Ihren Code ein",
				digitsInvalid: "Bitte geben Sie einen gültigen Code ein",
				messageLoginFailed:
					"Anmeldung fehlgeschlagen, bitte überprüfen Sie Ihre Eingaben.",
				messageForbidden:
					"Die Anmeldung war erfolgreich, aber Sie haben keine Berechtigung. Sie können sich <b>nicht</b> anmelden.<br>Sie können einen anderen Benutzer auswählen oder sich an den Administrator wenden.",
				messageSomethingWentWrong:
					"Etwas ist schief gelaufen, bitte versuchen Sie es später erneut",
				messageThisFormIsNotConfigured:
					"Dieses Formular ist nicht konfiguriert.",
				messagePasswordResetDisabled:
					"Sie können keinen Code anfordern, da die<br>Zwei-Faktor-Authentifizierung bei Ihrem Konto aktiviert ist.<br>Bitte kontaktieren Sie den Administrator.",
			};
		case "es":
			return {
				username: "Nombre de usuario o correo electrónico",
				password: "Contraseña",
				login: "Iniciar sesión",
				forgotPasswordLink: "¿Olvidaste tu contraseña?",
				mailAddress: "Dirección de correo electrónico",
				requestLink: "Solicitar enlace",
				digits: "Código",
				loginLink: "Iniciar sesión",
				secondFactor: "Código de segundo factor",
				sendDigits: "Enviar código",
				sendSecondFactorDigits: "Enviar código",
				resetLoginProcess: "Volver al inicio de sesión",
				messageEmptyUserName: "Por favor ingrese su nombre de usuario",
				messageEmptyPassword: "Por favor ingrese su contraseña",
				messageEmptyBoth: "Por favor ingrese su nombre de usuario y contraseña",
				messageEmptyEmail:
					"Por favor ingrese su dirección de correo electrónico",
				messageInvalidEmail:
					"Por favor ingrese una dirección de correo electrónico válida",
				digitsEmpty: "Por favor ingrese su código",
				digitsInvalid: "Por favor ingrese un código válido",
				messageLoginFailed:
					"Error al iniciar sesión, por favor verifique sus datos.",
				messageForbidden:
					"El inicio de sesión fue exitoso, pero no tienes permisos. <b>No puedes</b> iniciar sesión.<br>Puedes seleccionar otro usuario o contactar al administrador.",
				messageSomethingWentWrong:
					"Algo salió mal, por favor intenta de nuevo más tarde.",
				messageThisFormIsNotConfigured: "Este formulario no está configurado.",
				messagePasswordResetDisabled:
					"Esta función no funciona porque la autenticación de dos factores está activada en su cuenta. Por favor, póngase en contacto con el administrador.",
			};
		case "zh":
			return {
				username: "用户名或电子邮箱",
				password: "密码",
				login: "登录",
				forgotPasswordLink: "忘记密码？",
				mailAddress: "电子邮件地址",
				requestLink: "请求链接",
				digits: "验证码",
				loginLink: "登录",
				secondFactor: "二次验证码",
				sendDigits: "发送验证码",
				sendSecondFactorDigits: "发送验证码",
				resetLoginProcess: "返回登录",
				messageEmptyUserName: "请输入用户名",
				messageEmptyPassword: "请输入密码",
				messageEmptyBoth: "请输入用户名和密码",
				messageEmptyEmail: "请输入电子邮件地址",
				messageInvalidEmail: "请输入有效的电子邮件地址",
				digitsEmpty: "请输入验证码",
				digitsInvalid: "请输入有效的验证码",
				messageLoginFailed: "登录失败，请检查您的输入。",
				messageForbidden:
					"登录成功，但您没有权限。您<b>不能</b>登录。<br>您可以选择其他用户或联系管理员。",
				messageSomethingWentWrong: "出了点问题，请稍后再试。",
				messageThisFormIsNotConfigured: "此表单尚未配置。",
				messagePasswordResetDisabled:
					"此功能无法使用，因为您的帐户启用了二因素身份验证。请联系管理员。",
			};

		case "hi":
			return {
				username: "उपयगकर नम य ई-मल",
				password: "पसवर",
				login: "लग इन कर",
				forgotPasswordLink: "पसवर भल गए?",
				mailAddress: "ई-मल पत",
				requestLink: "लक क अनरध कर",
				digits: "कड",
				loginLink: "लग इन कर",
				secondFactor: "दसर फकर कड",
				sendDigits: "कड भज",
				sendSecondFactorDigits: "कड भज",
				resetLoginProcess: "लगन पर वपस जए",
				messageEmptyUserName: "कपय अपन उपयगकर नम दर कर",
				messageEmptyPassword: "कपय अपन पसवर दर कर",
				messageEmptyBoth: "कपय अपन उपयगकर नम और पसवर दर कर",
				messageEmptyEmail: "कपय अपन ई-मल पत दर कर",
				messageInvalidEmail: "कपय एक वध ई-मल पत दर कर",
				digitsEmpty: "कपय अपन कड दर कर",
				digitsInvalid: "कपय एक वध कड दर कर",
				messageLoginFailed: "लगन वफल हआ, कपय अपन जनकर क जच कर।",
				messageForbidden:
					"लगन सफल रह लकन आपक पस अनमत नह ह। आप <b>लगन नह</b> कर सकत।<br>आप दसर उपयगकर चन सकत ह य पशसक स सपर कर सकत ह।",
				messageSomethingWentWrong: "कछ गलत हआ, कपय बद म पन पयस कर।",
				messageThisFormIsNotConfigured: "यह फर कनगर नह ह।",
				messagePasswordResetDisabled:
					"यह सवध कम नह करत कक आपक खत म द घटक पमणकरण सकम ह। कपय पशसक स सपर कर।",
			};

		case "bn":
			return {
				username: "ইউজরনম ব ই-মইল",
				password: "পসওযর",
				login: "লগইন করন",
				forgotPasswordLink: "পসওযর ভল গছন?",
				mailAddress: "ই-মইল ঠকন",
				requestLink: "লক অনরধ করন",
				digits: "কড",
				loginLink: "লগইন করন",
				secondFactor: "দতয ফকর কড",
				sendDigits: "কড পঠন",
				sendSecondFactorDigits: "কড পঠন",
				resetLoginProcess: "লগইন ফর যন",
				messageEmptyUserName: "দয কর আপনর ইউজরনম লখন",
				messageEmptyPassword: "দয কর আপনর পসওযর লখন",
				messageEmptyBoth: "দয কর আপনর ইউজরনম এব পসওযর লখন",
				messageEmptyEmail: "দয কর আপনর ই-মইল ঠকন লখন",
				messageInvalidEmail: "দয কর একট বধ ই-মইল ঠকন লখন",
				digitsEmpty: "দয কর আপনর কড লখন",
				digitsInvalid: "দয কর একট বধ কড লখন",
				messageLoginFailed: "লগইন বর হযছ, দয কর আপনর ইনপট পরক করন।",
				messageForbidden:
					"লগইন সফল হযছ, কন আপনর অনমত নই। আপন <b>লগইন করত পরবন ন</b>।<br>আপন অন ইউজর নরচন করত পরন অথব অযডমনসটরর সথ যগযগ করত পরন।",
				messageSomethingWentWrong: "কছ ভল হযছ, দয কর পর আবর চষ করন।",
				messageThisFormIsNotConfigured: "এই ফরট কনফগর কর হযন।",
				messagePasswordResetDisabled:
					"এই ফশনট করকর নয করণ আপনর অযকউন দট ফকর পমণকরণ সকয কর আছ। দয কর পশসকর সথ যগযগ করন।",
			};

		case "pt": // Portuguese
			return {
				username: "Nome de usuário ou e-mail",
				password: "Senha",
				login: "Entrar",
				forgotPasswordLink: "Esqueceu a senha?",
				mailAddress: "Endereço de e-mail",
				requestLink: "Solicitar link",
				digits: "Código",
				loginLink: "Entrar",
				secondFactor: "Código do segundo fator",
				sendDigits: "Enviar código",
				sendSecondFactorDigits: "Enviar código",
				resetLoginProcess: "Voltar ao login",
				messageEmptyUserName: "Por favor, insira seu nome de usuário",
				messageEmptyPassword: "Por favor, insira sua senha",
				messageEmptyBoth: "Por favor, insira seu nome de usuário e senha",
				messageEmptyEmail: "Por favor, insira seu endereço de e-mail",
				messageInvalidEmail: "Por favor, insira um endereço de e-mail válido",
				digitsEmpty: "Por favor, insira seu código",
				digitsInvalid: "Por favor, insira um código válido",
				messageLoginFailed: "Falha no login, verifique suas informações.",
				messageForbidden:
					"Login foi bem-sucedido, mas você não tem permissão. Você <b>não pode</b> entrar.<br>Você pode escolher outro usuário ou entrar em contato com o administrador.",
				messageSomethingWentWrong:
					"Algo deu errado, tente novamente mais tarde.",
				messageThisFormIsNotConfigured: "Este formulário não está configurado.",
				messagePasswordResetDisabled:
					"Esta função não funciona porque a autenticação de dois fatores<br>está ativada em sua conta. Por favor, entre em contato com o administrador.",
			};

		case "ru": // Russian
			return {
				username: "Имя пользователя или электронная почта",
				password: "Пароль",
				login: "Войти",
				forgotPasswordLink: "Забыли пароль?",
				mailAddress: "Адрес электронной почты",
				requestLink: "Запросить ссылку",
				digits: "Код",
				loginLink: "Войти",
				secondFactor: "Код второго фактора",
				sendDigits: "Отправить код",
				sendSecondFactorDigits: "Отправить код",
				resetLoginProcess: "Вернуться к входу",
				messageEmptyUserName: "Пожалуйста, введите имя пользователя",
				messageEmptyPassword: "Пожалуйста, введите пароль",
				messageEmptyBoth: "Пожалуйста, введите имя пользователя и пароль",
				messageEmptyEmail: "Пожалуйста, введите адрес электронной почты",
				messageInvalidEmail:
					"Пожалуйста, введите действительный адрес электронной почты",
				digitsEmpty: "Пожалуйста, введите код",
				digitsInvalid: "Пожалуйста, введите действительный код",
				messageLoginFailed: "Ошибка входа, проверьте введенные данные.",
				messageForbidden:
					"Вход выполнен, но у вас нет прав. Вы <b>не можете</b> войти.<br>Вы можете выбрать другого пользователя или связаться с администратором.",
				messageSomethingWentWrong: "Что-то пошло не так, попробуйте позже.",
				messageThisFormIsNotConfigured: "Эта форма не настроена.",
				messagePasswordResetDisabled:
					"Эта функция не работает, потому что двухфакторная аутентификация<br> включена в вашей учетной записи. Пожалуйста, свяжитесь с администратором.",
			};

		case "ja": // Japanese
			return {
				username: "ユーザー名またはメールアドレス",
				password: "パスワード",
				login: "ログイン",
				forgotPasswordLink: "パスワードを忘れましたか？",
				mailAddress: "メールアドレス",
				requestLink: "リンクをリクエスト",
				digits: "コード",
				loginLink: "ログイン",
				secondFactor: "二要素コード",
				sendDigits: "コードを送信",
				sendSecondFactorDigits: "コードを送信",
				resetLoginProcess: "ログインに戻る",
				messageEmptyUserName: "ユーザー名を入力してください",
				messageEmptyPassword: "パスワードを入力してください",
				messageEmptyBoth: "ユーザー名とパスワードを入力してください",
				messageEmptyEmail: "メールアドレスを入力してください",
				messageInvalidEmail: "有効なメールアドレスを入力してください",
				digitsEmpty: "コードを入力してください",
				digitsInvalid: "有効なコードを入力してください",
				messageLoginFailed:
					"ログインに失敗しました。入力内容をご確認ください。",
				messageForbidden:
					"ログイン成功しましたが、権限がありません。<b>ログインできません</b>。<br>他のユーザーを選択するか、管理者に連絡してください。",
				messageSomethingWentWrong:
					"問題が発生しました。後でもう一度お試しください。",
				messageThisFormIsNotConfigured: "このフォームは設定されていません。",
				messagePasswordResetDisabled:
					"この機能は使用できません。アカウントで二要素認証が有効になっているため、管理者に連絡してください。",
			};

		case "pa": // Western Punjabi
			return {
				username: "ਵਰਤਕਰ ਨਮ ਜ ਈ-ਮਲ",
				password: "ਪਸਵਰਡ",
				login: "ਲਗਨ ਕਰ",
				forgotPasswordLink: "ਪਸਵਰਡ ਭਲ ਗਏ?",
				mailAddress: "ਈ-ਮਲ ਪਤ",
				requestLink: "ਲਕ ਦ ਬਨਤ ਕਰ",
				digits: "ਕਡ",
				loginLink: "ਲਗਨ ਕਰ",
				secondFactor: "ਦਜ ਫਕਟਰ ਕਡ",
				sendDigits: "ਕਡ ਭਜ",
				sendSecondFactorDigits: "ਕਡ ਭਜ",
				resetLoginProcess: "ਲਗਨ 'ਤ ਵਪਸ ਜਓ",
				messageEmptyUserName: "ਕਰਪ ਕਰਕ ਆਪਣ ਵਰਤਕਰ ਨਮ ਦਰਜ ਕਰ",
				messageEmptyPassword: "ਕਰਪ ਕਰਕ ਆਪਣ ਪਸਵਰਡ ਦਰਜ ਕਰ",
				messageEmptyBoth: "ਕਰਪ ਕਰਕ ਆਪਣ ਵਰਤਕਰ ਨਮ ਤ ਪਸਵਰਡ ਦਰਜ ਕਰ",
				messageEmptyEmail: "ਕਰਪ ਕਰਕ ਆਪਣ ਈ-ਮਲ ਪਤ ਦਰਜ ਕਰ",
				messageInvalidEmail: "ਕਰਪ ਕਰਕ ਇਕ ਵਧ ਈ-ਮਲ ਪਤ ਦਰਜ ਕਰ",
				digitsEmpty: "ਕਰਪ ਕਰਕ ਆਪਣ ਕਡ ਦਰਜ ਕਰ",
				digitsInvalid: "ਕਰਪ ਕਰਕ ਇਕ ਵਧ ਕਡ ਦਰਜ ਕਰ",
				messageLoginFailed: "ਲਗਨ ਅਸਫਲ ਹਇਆ, ਕਰਪ ਕਰਕ ਆਪਣ ਜਣਕਰ ਦ ਜਚ ਕਰ।",
				messageForbidden:
					"ਲਗਨ ਸਫਲ ਹਇਆ, ਪਰ ਤਹਡ ਕਲ ਅਧਕਰ ਨਹ ਹਨ। ਤਸ <b>ਲਗਨ ਨਹ</b> ਕਰ ਸਕਦ।<br>ਤਸ ਹਰ ਵਰਤਕਰ ਚਣ सकत ਹ ਜ ਪਰਬਧਕ ਨਲ ਸਪਰਕ ਕਰ ਸਕਦ ਹ।",
				messageSomethingWentWrong: "ਕਝ ਗਲਤ ਹ ਗਆ, ਕਰਪ ਕਰਕ ਬਅਦ ਵਚ ਮੜ ਕਸਸ ਕਰ।",
				messageThisFormIsNotConfigured: "ਇਸ ਫਰਮ ਨ ਸਰਚਤ ਨਹ ਕਤ ਗਆ ਹ।",
				messagePasswordResetDisabled:
					"ਇਹ ਫਕਸਨ ਕਮ ਨਹ ਕਰਦ ਕਉਕ ਤਹਡ ਖਤ ਵਚ ਦ ਫਕਟਰ ਪਰਮਣਕਰਣ ਸਰਗਰਮ ਹ। ਕਰਪ ਕਰਕ ਪਰਬਧਕ ਨਲ ਸਪਰਕ ਕਰ।",
			};

		case "mr": // Marathi
			return {
				username: "वपरकरनव कव ईमल",
				password: "सकतशब",
				login: "पवश कर",
				forgotPasswordLink: "सकतशब वसरलत क?",
				mailAddress: "ईमल पत",
				requestLink: "लकच वनत कर",
				digits: "कड",
				loginLink: "पवश कर",
				secondFactor: "दसर घटक कड",
				sendDigits: "कड पठव",
				sendSecondFactorDigits: "कड पठव",
				resetLoginProcess: "पवशवर परत ज",
				messageEmptyUserName: "कपय आपल वपरकरनव पवष कर",
				messageEmptyPassword: "कपय आपल सकतशब पवष कर",
				messageEmptyBoth: "कपय आपल वपरकरनव आण सकतशब पवष कर",
				messageEmptyEmail: "कपय आपल ईमल पत पवष कर",
				messageInvalidEmail: "कपय वध ईमल पत पवष कर",
				digitsEmpty: "कपय आपल कड पवष कर",
				digitsInvalid: "कपय वध कड पवष कर",
				messageLoginFailed: "पवश अयशस, कपय आपल महत तपस.",
				messageForbidden:
					"पवश यशस झल, परत आपलकड परवन नह. आपण <b>पवश कर शकत नह</b>.<br>आपण दसर वपरकर नवड शकत कव ववसपकश सपर सध शकत.",
				messageSomethingWentWrong: "कहतर चकल, कपय नतर पन पयत कर.",
				messageThisFormIsNotConfigured: "ह फर सरचत कलल नह.",
				messagePasswordResetDisabled:
					"य करच कमगर करत नह करण आपल खतत दन घटक पमणकरण सकय कल आह. कपय ववसपकश सपर सध.",
			};

		case "fr": // French
			return {
				username: "Nom d'utilisateur ou e-mail",
				password: "Mot de passe",
				login: "Connexion",
				forgotPasswordLink: "Mot de passe oublié ?",
				mailAddress: "Adresse e-mail",
				requestLink: "Demander un lien",
				digits: "Code",
				loginLink: "Connexion",
				secondFactor: "Code du deuxième facteur",
				sendDigits: "Envoyer le code",
				sendSecondFactorDigits: "Envoyer le code",
				resetLoginProcess: "Retour à la connexion",
				messageEmptyUserName: "Veuillez entrer votre nom d'utilisateur",
				messageEmptyPassword: "Veuillez entrer votre mot de passe",
				messageEmptyBoth:
					"Veuillez entrer votre nom d'utilisateur et votre mot de passe",
				messageEmptyEmail: "Veuillez entrer votre adresse e-mail",
				messageInvalidEmail: "Veuillez entrer une adresse e-mail valide",
				digitsEmpty: "Veuillez entrer votre code",
				digitsInvalid: "Veuillez entrer un code valide",
				messageLoginFailed:
					"Échec de la connexion, veuillez vérifier vos informations.",
				messageForbidden:
					"La connexion a réussi, mais vous n'avez pas les permissions. Vous <b>ne pouvez pas</b> vous connecter.<br>Vous pouvez choisir un autre utilisateur ou contacter l'administrateur.",
				messageSomethingWentWrong:
					"Une erreur s'est produite, veuillez réessayer plus tard.",
				messageThisFormIsNotConfigured: "Ce formulaire n'est pas configuré.",
				messagePasswordResetDisabled:
					"Cette fonctionnalité ne fonctionne pas car l'authentification à deux facteurs est activée sur<br>votre compte. Veuillez contacter l'administrateur.",
			};

		case "it": // Italian
			return {
				username: "Nome utente o e-mail",
				password: "Password",
				login: "Accedi",
				forgotPasswordLink: "Password dimenticata?",
				mailAddress: "Indirizzo e-mail",
				requestLink: "Richiedi link",
				digits: "Codice",
				loginLink: "Accedi",
				secondFactor: "Codice a due fattori",
				sendDigits: "Invia codice",
				sendSecondFactorDigits: "Invia codice",
				resetLoginProcess: "Torna al login",
				messageEmptyUserName: "Per favore, inserisci il tuo nome utente",
				messageEmptyPassword: "Per favore, inserisci la tua password",
				messageEmptyBoth:
					"Per favore, inserisci il tuo nome utente e la tua password",
				messageEmptyEmail: "Per favore, inserisci il tuo indirizzo e-mail",
				messageInvalidEmail: "Per favore, inserisci un indirizzo e-mail valido",
				digitsEmpty: "Per favore, inserisci il tuo codice",
				digitsInvalid: "Per favore, inserisci un codice valido",
				messageLoginFailed: "Accesso fallito, verifica i tuoi dati.",
				messageForbidden:
					"Accesso riuscito, ma non hai i permessi. Non puoi accedere.<br>Puoi scegliere un altro account o contattare l'amministratore.",
				messageSomethingWentWrong:
					"Qualcosa è andato storto, riprova più tardi.",
				messageThisFormIsNotConfigured: "Questo modulo non è configurato.",
				messagePasswordResetDisabled:
					"Questa funzione non funziona perché l'autenticazione a due<br>fattori è attiva sul tuo account. Contatta l'amministratore.",
			};

		case "nl": // Dutch
			return {
				username: "Gebruikersnaam of e-mail",
				password: "Wachtwoord",
				login: "Inloggen",
				forgotPasswordLink: "Wachtwoord vergeten?",
				mailAddress: "E-mailadres",
				requestLink: "Link aanvragen",
				digits: "Code",
				loginLink: "Inloggen",
				secondFactor: "Tweede factor code",
				sendDigits: "Code verzenden",
				sendSecondFactorDigits: "Code verzenden",
				resetLoginProcess: "Terug naar inloggen",
				messageEmptyUserName: "Voer uw gebruikersnaam in",
				messageEmptyPassword: "Voer uw wachtwoord in",
				messageEmptyBoth: "Voer uw gebruikersnaam en wachtwoord in",
				messageEmptyEmail: "Voer uw e-mailadres in",
				messageInvalidEmail: "Voer een geldig e-mailadres in",
				digitsEmpty: "Voer uw code in",
				digitsInvalid: "Voer een geldige code in",
				messageLoginFailed: "Inloggen mislukt, controleer uw gegevens.",
				messageForbidden:
					"Inloggen geslaagd, maar u heeft geen permissies. U <b>kunt niet</b> inloggen.<br>U kunt een andere gebruiker selecteren of de beheerder contacteren.",
				messageSomethingWentWrong:
					"Er is iets fout gegaan, probeer het later opnieuw.",
				messageThisFormIsNotConfigured: "Dit formulier is niet geconfigureerd.",
				messagePasswordResetDisabled:
					"Deze functie werkt niet omdat tweefactorauthenticatie is<br>ingeschakeld op uw account. Neem contact op met de beheerder.",
			};

		case "sv": // Swedish
			return {
				username: "Användarnamn eller e-post",
				password: "Lösenord",
				login: "Logga in",
				forgotPasswordLink: "Glömt lösenord?",
				mailAddress: "E-postadress",
				requestLink: "Begär länk",
				digits: "Kod",
				loginLink: "Logga in",
				secondFactor: "Tvåfaktorkod",
				sendDigits: "Skicka kod",
				sendSecondFactorDigits: "Skicka kod",
				resetLoginProcess: "Tillbaka till inloggning",
				messageEmptyUserName: "Ange ditt användarnamn",
				messageEmptyPassword: "Ange ditt lösenord",
				messageEmptyBoth: "Ange ditt användarnamn och lösenord",
				messageEmptyEmail: "Ange din e-postadress",
				messageInvalidEmail: "Ange en giltig e-postadress",
				digitsEmpty: "Ange din kod",
				digitsInvalid: "Ange en giltig kod",
				messageLoginFailed:
					"Inloggning misslyckades, kontrollera dina uppgifter.",
				messageForbidden:
					"Inloggningen lyckades, men du har inte behörighet. Du <b>kan inte</b> logga in.<br>Du kan välja en annan användare eller kontakta administratören.",
				messageSomethingWentWrong: "Något gick fel, försök igen senare.",
				messageThisFormIsNotConfigured: "Detta formulär är inte konfigurerat.",
				messagePasswordResetDisabled:
					"Denna funktion fungerar inte eftersom tvåfaktorsautentisering är aktiverad på ditt konto. Kontakta administratören.",
			};

		case "pl": // Polish
			return {
				username: "Nazwa użytkownika lub e-mail",
				password: "Hasło",
				login: "Zaloguj się",
				forgotPasswordLink: "Zapomniałeś hasła?",
				mailAddress: "Adres e-mail",
				requestLink: "Zażądaj linku",
				digits: "Kod",
				loginLink: "Zaloguj się",
				secondFactor: "Kod drugiego czynnika",
				sendDigits: "Wyślij kod",
				sendSecondFactorDigits: "Wyślij kod",
				resetLoginProcess: "Powrót do logowania",
				messageEmptyUserName: "Wprowadź swoją nazwę użytkownika",
				messageEmptyPassword: "Wprowadź swoje hasło",
				messageEmptyBoth: "Wprowadź swoją nazwę użytkownika i hasło",
				messageEmptyEmail: "Wprowadź swój adres e-mail",
				messageInvalidEmail: "Wprowadź prawidłowy adres e-mail",
				digitsEmpty: "Wprowadź swój kod",
				digitsInvalid: "Wprowadź prawidłowy kod",
				messageLoginFailed: "Logowanie nie powiodło się, sprawdź swoje dane.",
				messageForbidden:
					"Logowanie powiodło się, ale nie masz uprawnień. <b>Nie możesz</b> się zalogować.<br>Możesz wybrać innego użytkownika lub skontaktować się z administratorem.",
				messageSomethingWentWrong:
					"Coś poszło nie tak, spróbuj ponownie później.",
				messageThisFormIsNotConfigured:
					"Ten formularz nie jest skonfigurowany.",
				messagePasswordResetDisabled:
					"Ta funkcja nie działa, ponieważ włączona jest autoryzacja dwuetapowa na twoim koncie. Skontaktuj się z administratorem.",
			};

		case "da": // Danish
			return {
				username: "Brugernavn eller e-mail",
				password: "Adgangskode",
				login: "Log ind",
				forgotPasswordLink: "Glemt adgangskode?",
				mailAddress: "E-mail-adresse",
				requestLink: "Anmod om link",
				digits: "Kode",
				loginLink: "Log ind",
				secondFactor: "Tofaktorkode",
				sendDigits: "Send kode",
				sendSecondFactorDigits: "Send kode",
				resetLoginProcess: "Tilbage til log ind",
				messageEmptyUserName: "Indtast dit brugernavn",
				messageEmptyPassword: "Indtast din adgangskode",
				messageEmptyBoth: "Indtast dit brugernavn og din adgangskode",
				messageEmptyEmail: "Indtast din e-mail-adresse",
				messageInvalidEmail: "Indtast en gyldig e-mail-adresse",
				digitsEmpty: "Indtast din kode",
				digitsInvalid: "Indtast en gyldig kode",
				messageLoginFailed: "Login mislykkedes, kontrollér dine oplysninger.",
				messageForbidden:
					"Login lykkedes, men du har ikke tilladelse. Du <b>kan ikke</b> logge ind.<br>Du kan vælge en anden bruger eller kontakte administratoren.",
				messageSomethingWentWrong: "Noget gik galt, prøv igen senere.",
				messageThisFormIsNotConfigured: "Denne formular er ikke konfigureret.",
				messagePasswordResetDisabled:
					"Denne funktion fungerer ikke, fordi tofaktorautentificering<br>er aktiveret på din konto. Kontakt administratoren.",
			};

		case "no": // Norwegian
			return {
				username: "Brukernavn eller e-post",
				password: "Passord",
				login: "Logg inn",
				forgotPasswordLink: "Glemt passord?",
				mailAddress: "E-postadresse",
				requestLink: "Be om lenke",
				digits: "Kode",
				loginLink: "Logg inn",
				secondFactor: "Tofaktorkode",
				sendDigits: "Send kode",
				sendSecondFactorDigits: "Send kode",
				resetLoginProcess: "Tilbake til innlogging",
				messageEmptyUserName: "Vennligst skriv inn brukernavnet ditt",
				messageEmptyPassword: "Vennligst skriv inn passordet ditt",
				messageEmptyBoth: "Vennligst skriv inn brukernavn og passord",
				messageEmptyEmail: "Vennligst skriv inn e-postadressen din",
				messageInvalidEmail: "Vennligst skriv inn en gyldig e-postadresse",
				digitsEmpty: "Vennligst skriv inn koden din",
				digitsInvalid: "Vennligst skriv inn en gyldig kode",
				messageLoginFailed:
					"Innlogging mislyktes, vennligst sjekk informasjonen din.",
				messageForbidden:
					"Innloggingen var vellykket, men du har ikke tillatelse. Du <b>kan ikke</b> logge inn.<br>Du kan velge en annen bruker eller kontakte administratoren.",
				messageSomethingWentWrong:
					"Noe gikk galt, vennligst prøv igjen senere.",
				messageThisFormIsNotConfigured: "Dette skjemaet er ikke konfigurert.",
				messagePasswordResetDisabled:
					"Denne funksjonen fungerer ikke fordi<br>tofaktorautentisering er aktivert på kontoen din. Kontakt administratoren.",
			};

		case "cs": // Czech
			return {
				username: "Uživatelské jméno nebo e-mail",
				password: "Heslo",
				login: "Přihlásit se",
				forgotPasswordLink: "Zapomněli jste heslo?",
				mailAddress: "E-mailová adresa",
				requestLink: "Požádat o odkaz",
				digits: "Kód",
				loginLink: "Přihlásit se",
				secondFactor: "Dvoufázový kód",
				sendDigits: "Odeslat kód",
				sendSecondFactorDigits: "Odeslat kód",
				resetLoginProcess: "Zpět na přihlášení",
				messageEmptyUserName: "Zadejte své uživatelské jméno",
				messageEmptyPassword: "Zadejte své heslo",
				messageEmptyBoth: "Zadejte své uživatelské jméno a heslo",
				messageEmptyEmail: "Zadejte svou e-mailovou adresu",
				messageInvalidEmail: "Zadejte platnou e-mailovou adresu",
				digitsEmpty: "Zadejte svůj kód",
				digitsInvalid: "Zadejte platný kód",
				messageLoginFailed: "Přihlášení se nezdařilo, zkontrolujte své údaje.",
				messageForbidden:
					"Přihlášení bylo úspěšné, ale nemáte oprávnění. Nemůžete se přihlásit.<br>Vyberte jiného uživatele nebo kontaktujte správce.",
				messageSomethingWentWrong: "Něco se pokazilo, zkuste to později.",
				messageThisFormIsNotConfigured: "Tento formulář není nakonfigurován.",
				messagePasswordResetDisabled:
					"Tato funkce nefunguje, protože je na vašem<br>účtu aktivováno dvoufaktorové ověřování. Kontaktujte správce.",
			};

		default:
			return {
				username: "Username or E-Mail",
				password: "Password",
				login: "Login",
				forgotPasswordLink: "Forgot Password?",
				mailAddress: "E-Mail-Address",
				requestLink: "Request Link",
				digits: "Code",
				loginLink: "Login",
				secondFactor: "Second Factor Code",
				sendDigits: "Send Code",
				sendSecondFactorDigits: "Send Code",
				resetLoginProcess: "Back to Login",
				messageEmptyUserName: "Please enter your username",
				messageEmptyPassword: "Please enter your password",
				messageEmptyBoth: "Please enter your username and password",
				messageEmptyEmail: "Please enter your email address",
				messageInvalidEmail: "Please enter a valid email address",
				digitsEmpty: "Please enter your code",
				digitsInvalid: "Please enter a valid code",
				messageLoginFailed: "Login failed, please check your input.",
				messageForbidden:
					"Login was successful, but you have no permission. You <b>cannot</b> login.<br>You can select another user or contact the administrator.",
				messageSomethingWentWrong:
					"Something went wrong, please try again later.",
				messageThisFormIsNotConfigured: "This form is not configured.",
				messagePasswordResetDisabled:
					"This function does not work because two-factor<br>authentication is enabled on your account.<br>Please contact the administrator.",
			};
	}
}

/**
 * Normalize the result of a callback into a unified structure.
 *
 * Supported return types:
 *  - undefined  → no change
 *  - string     → transformed value
 *  - false      → valid=false
 *  - { value?, valid?, message?, stop? } → structured result
 *
 * @private
 * @param {*} callbackResult
 * @param {string} currentValue
 * @return {{ value: string, valid: (boolean|undefined), message: (string|undefined), stop: boolean, raw: * }}
 */
function normalizeCallbackResult(callbackResult, currentValue) {
	const normalized = {
		value: currentValue,
		valid: undefined,
		message: undefined,
		stop: false,
		raw: callbackResult,
	};

	if (callbackResult === undefined) {
		return normalized;
	}

	if (typeof callbackResult === "string") {
		normalized.value = callbackResult;
		return normalized;
	}

	if (callbackResult === false) {
		normalized.valid = false;
		return normalized;
	}

	if (callbackResult && typeof callbackResult === "object") {
		if (Object.prototype.hasOwnProperty.call(callbackResult, "value")) {
			normalized.value = callbackResult.value;
		}
		if (Object.prototype.hasOwnProperty.call(callbackResult, "valid")) {
			normalized.valid = callbackResult.valid;
		}
		if (Object.prototype.hasOwnProperty.call(callbackResult, "message")) {
			normalized.message = callbackResult.message;
		}
		if (Object.prototype.hasOwnProperty.call(callbackResult, "stop")) {
			normalized.stop = Boolean(callbackResult.stop);
		}

		return normalized;
	}

	return normalized;
}

/**
 * @private
 * @return {initEventHandler}
 */
function initEventHandler() {
	const self = this;
	const element = this[loginElementSymbol];

	const type = "click";

	element.addEventListener(type, function (event) {
		const callback = self.getOption("actions.click");

		fireCustomEvent(self, "monster-login-clicked", {
			element: self,
		});

		if (!isFunction(callback)) {
			return;
		}

		const element = findTargetElementFromEvent(
			event,
			ATTRIBUTE_ROLE,
			"control",
		);

		if (!(element instanceof Node && self.hasNode(element))) {
			return;
		}

		callback.call(self, event);
	});

	this[forgotPasswordLinkSymbol].addEventListener(type, (event) => {
		event.preventDefault();
		this[forgotPasswordCollapseSymbol].open();
		setTimeout(() => {
			this.shadowRoot.querySelector("input[name='email']").focus();
		}, 0);
	});

	this[loginLinkSymbol].addEventListener(type, (event) => {
		event.preventDefault();
		this[loginCollapseSymbol].open();

		setTimeout(() => {
			this.shadowRoot.querySelector("input[name='username']").focus();
		}, 0);
	});

	for (const e of this[resetLoginProcessLinksSymbol]) {
		e.addEventListener(type, (event) => {
			event.preventDefault();
			this[loginCollapseSymbol].open();
			setTimeout(() => {
				this.shadowRoot.querySelector("input[name='username']").focus();
			}, 0);
		});
	}

	this[loginCollapseSymbol].addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
			this[loginButtonSymbol].click();
		}
	});

	this[secondFactorCollapseSymbol].addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
			this[secondFactorButtonSymbol].click();
		}
	});

	this[forgotPasswordCollapseSymbol].addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
			this[requestLinkButtonSymbol].click();
		}
	});

	this[digitsCollapseSymbol].addEventListener("keydown", (event) => {
		if (event.key === "Enter") {
			this[digitsButtonSymbol].click();
		}
	});

	this[loginButtonSymbol].setOption("actions.click", (event) => {
		let username = this.shadowRoot.querySelector(
			"input[name='username']",
		).value;

		// New structured callback handling for username (backwards compatible)
		const userCallback = this.getOption("callbacks.username");
		if (isFunction(userCallback)) {
			const result = normalizeCallbackResult(
				userCallback.call(this, username),
				username,
			);
			username = result.value;

			// If callback explicitly marks invalid or wants to stop, handle immediately
			if (result.valid === false || result.stop === true) {
				const timeout = this.getOption("timeoutForMessage");
				const msg =
					result.message || this.getOption("labels.messageEmptyUserName");

				this.setOption("classes.usernameInvalid", "invalid");
				this[loginButtonSymbol].setMessage(msg);
				this[loginButtonSymbol].showMessage(timeout);
				this[loginButtonSymbol].setState("failed", timeout);

				setTimeout(() => {
					this.shadowRoot.querySelector("input[name='username']").focus();
				}, 0);
				return;
			}
		}

		const password = this.shadowRoot.querySelector("monster-password").value;

		let missingBits = 0;
		if (username === "" || username === null) {
			this.setOption("classes.usernameInvalid", "invalid");
			missingBits |= 1; // Set bit 1 for username
		} else {
			this.setOption("classes.usernameInvalid", "");
		}

		if (password === "" || password === null) {
			this.setOption("classes.passwordInvalid", "invalid");
			missingBits |= 2; // Set bit 2 for password
		} else {
			this.setOption("classes.passwordInvalid", "");
		}

		let msg = null;
		if (missingBits === 1) {
			// missing username
			msg = this.getOption("labels.messageEmptyUserName");

			setTimeout(() => {
				this.shadowRoot.querySelector("input[name='username']").focus();
			}, 0);
		} else if (missingBits === 2) {
			// missing password
			msg = this.getOption("labels.messageEmptyPassword");

			setTimeout(() => {
				this.shadowRoot.querySelector("monster-password").focus();
			});
		} else if (missingBits === 3) {
			msg = this.getOption("labels.messageEmptyBoth");

			setTimeout(() => {
				this.shadowRoot.querySelector("input[name='username']").focus();
			}, 0);
		}

		const timeout = this.getOption("timeoutForMessage");

		if (msg !== null && msg !== undefined) {
			this[loginButtonSymbol].setMessage(msg);
			this[loginButtonSymbol].showMessage(timeout);
			this[loginButtonSymbol].setState("failed", timeout);
			return;
		}

		const url = this.getOption("fetch.login.url");

		if (url === "" || url === null || url === undefined) {
			this[loginButtonSymbol].setMessage(
				this.getOption("labels.messageThisFormIsNotConfigured"),
			);
			this[loginButtonSymbol].showMessage(timeout);
			this[loginButtonSymbol].setState("failed", timeout);
			return;
		}

		const identifierField =
			this.getOption("fetch.login.payload.identifier") || "username";

		const passwordField =
			this.getOption("fetch.login.payload.password") || "password";

		const payload = {
			[identifierField]: username,
			[passwordField]: password,
		};

		const options = {
			method: this.getOption("fetch.login.method"),
			mode: this.getOption("fetch.login.mode"),
			headers: this.getOption("fetch.login.headers"),
			credentials: this.getOption("fetch.login.credentials"),
			body: JSON.stringify(payload),
		};

		getWindow()
			.fetch(url, options)
			.then((response) => {
				if (response.ok) {
					this[loginButtonSymbol].setState("successful", timeout);
					setTimeout(() => {
						this.openLoggedIn();
					}, 1200);
				} else {
					if (response.status === 403) {
						this[loginButtonSymbol].setMessage(
							this.getOption("labels.messageForbidden"),
						);
					} else if (response.status === 401) {
						const wwwAuthenticateHeader =
							response.headers.get("www-authenticate");
						if (wwwAuthenticateHeader) {
							const wwwAuthenticateParts = wwwAuthenticateHeader
								.split(/,\s*/)
								.map((part) => {
									const [key, value] = part.split("=");
									return {
										key: key.trim(),
										value: value ? value.trim().replace(/^"|"$/g, "") : null,
									};
								});

							const filteredParts = wwwAuthenticateParts.filter(
								(part) => part.key.toLowerCase() === "2fa",
							);
							if (filteredParts.length > 0) {
								const timeout = this.getOption("timeoutForSuccess");
								this[loginButtonSymbol].setState("successful", timeout);
								setTimeout(() => {
									this.openSecondFactor();
									const digitsElement = this.shadowRoot.getElementById(
										"secondFactorControl",
									);
									digitsElement.focus();
								}, timeout);
								return;
							}
						}

						this[loginButtonSymbol].setMessage(
							this.getOption("labels.messageLoginFailed"),
						);
					} else {
						this[loginButtonSymbol].setMessage(
							this.getOption("labels.messageSomethingWentWrong"),
						);
					}
					this[loginButtonSymbol].showMessage(timeout);
					this[loginButtonSymbol].setState("failed", timeout);

					setTimeout(() => {
						this.shadowRoot.querySelector("input[name='username']").focus();
					}, 0);
				}
			})
			.catch((error) => {
				this[loginButtonSymbol].setMessage(
					this.getOption("labels.messageSomethingWentWrong"),
				);
				this[loginButtonSymbol].showMessage(timeout);
				this[loginButtonSymbol].setState("failed", timeout);

				setTimeout(() => {
					this.shadowRoot.querySelector("input[name='username']").focus();
				}, 0);
			});
	});

	this[requestLinkButtonSymbol].setOption("actions.click", (event) => {
		const emailElement = this.shadowRoot.querySelector("input[name='email']");

		// get email value and HTML validity
		let mail = emailElement.value;
		let valid = emailElement.checkValidity();

		const timeout = this.getOption("timeoutForMessage");

		// New structured callback handling for forgotPassword (backwards compatible)
		const mailCallback = this.getOption("callbacks.forgotPassword");
		if (isFunction(mailCallback)) {
			const result = normalizeCallbackResult(
				mailCallback.call(this, mail),
				mail,
			);

			mail = result.value;

			if (result.valid !== undefined) {
				valid = result.valid;
			}

			if (result.valid === false || result.stop === true) {
				this.setOption("classes.emailInvalid", "invalid");

				const msg =
					result.message ||
					(mail === "" || mail === null
						? this.getOption("labels.messageEmptyEmail")
						: this.getOption("labels.messageInvalidEmail"));

				this[requestLinkButtonSymbol].setMessage(msg);
				this[requestLinkButtonSymbol].showMessage(timeout);
				this[requestLinkButtonSymbol].setState("failed", timeout);
				return;
			}
		}

		let msg = null;
		if (mail === "" || mail === null) {
			this.setOption("classes.emailInvalid", "invalid");
			msg = this.getOption("labels.messageEmptyEmail");
		} else if (!valid) {
			this.setOption("classes.emailInvalid", "invalid");
			msg = this.getOption("labels.messageInvalidEmail");
		} else {
			this.setOption("classes.emailInvalid", "");
		}

		if (msg !== null && msg !== undefined) {
			this[requestLinkButtonSymbol].setMessage(msg);
			this[requestLinkButtonSymbol].showMessage(timeout);
			this[requestLinkButtonSymbol].setState("failed", timeout);
			return;
		}

		const url = this.getOption("fetch.forgotPassword.url");

		if (url === "" || url === null || url === undefined) {
			this[requestLinkButtonSymbol].setMessage(
				this.getOption("labels.messageThisFormIsNotConfigured"),
			);
			this[requestLinkButtonSymbol].showMessage(timeout);
			this[requestLinkButtonSymbol].setState("failed", timeout);
			return;
		}

		const options = {
			method: this.getOption("fetch.forgotPassword.method"),
			mode: this.getOption("fetch.forgotPassword.mode"),
			headers: this.getOption("fetch.forgotPassword.headers"),
			credentials: this.getOption("fetch.forgotPassword.credentials"),
			body: JSON.stringify({ mail }),
		};

		getWindow()
			.fetch(url, options)
			.then((response) => {
				const disabledCodes = this.getOption(
					"behavior.passwordResetDisabledStatusCodes",
				) || [401];
				const isResetDisabled =
					disabledCodes.includes(response.status) &&
					response.headers.has("x-password-reset") &&
					response.headers.get("x-password-reset").includes("disabled");

				if (response.ok) {
					const timeoutSuccess = this.getOption("timeoutForSuccess");
					this[requestLinkButtonSymbol].setState("successful", timeoutSuccess);
					setTimeout(() => {
						this.openDigits();
					}, timeoutSuccess);
				} else if (isResetDisabled) {
					this[requestLinkButtonSymbol].setMessage(
						this.getOption("labels.messagePasswordResetDisabled"),
					);
				} else if (response.status === 403) {
					this[requestLinkButtonSymbol].setMessage(
						this.getOption("labels.messageForbidden"),
					);
				} else if (response.status === 401) {
					this[requestLinkButtonSymbol].setMessage(
						this.getOption("labels.messageLoginFailed"),
					);
				} else {
					this[requestLinkButtonSymbol].setMessage(
						this.getOption("labels.messageSomethingWentWrong"),
					);
				}
				if (!response.ok) {
					this[requestLinkButtonSymbol].showMessage(timeout);
					this[requestLinkButtonSymbol].setState("failed", timeout);
				}
			})
			.catch(() => {
				this[requestLinkButtonSymbol].setMessage(
					this.getOption("labels.messageSomethingWentWrong"),
				);
				this[requestLinkButtonSymbol].showMessage(timeout);
				this[requestLinkButtonSymbol].setState("failed", timeout);
			});
	});

	this[secondFactorButtonSymbol].setOption("actions.click", (event) => {
		const digitsElement = this.shadowRoot.getElementById("secondFactorControl");

		const digits = digitsElement.value;
		const valid = digitsElement.checkValidity();

		let msg = null;
		if (digits === "" || digits === null) {
			msg = this.getOption("labels.digitsEmpty");
		} else if (!valid) {
			msg = this.getOption("labels.digitsInvalid");
		}

		const timeout = this.getOption("timeoutForMessage");

		if (msg !== null && msg !== undefined) {
			this[secondFactorButtonSymbol].setMessage(msg);
			this[secondFactorButtonSymbol].showMessage(timeout);
			this[secondFactorButtonSymbol].setState("failed", timeout);
			return;
		}

		const url = this.getOption("fetch.secondFactor.url");

		if (url === "" || url === null || url === undefined) {
			this[secondFactorButtonSymbol].setMessage(
				this.getOption("labels.messageThisFormIsNotConfigured"),
			);
			this[secondFactorButtonSymbol].showMessage(timeout);
			this[secondFactorButtonSymbol].setState("failed", timeout);
			return;
		}

		const options = {
			method: this.getOption("fetch.secondFactor.method"),
			mode: this.getOption("fetch.secondFactor.mode"),
			headers: this.getOption("fetch.secondFactor.headers"),
			credentials: this.getOption("fetch.secondFactor.credentials"),
			body: JSON.stringify({ digits }),
		};

		getWindow()
			.fetch(url, options)
			.then((response) => {
				if (response.ok) {
					const timeoutSuccess = this.getOption("timeoutForSuccess");
					this[secondFactorButtonSymbol].setState("successful", timeoutSuccess);
					fireEvent(this, "second-factor-success");
					setTimeout(() => {
						this.openLoggedIn();
					}, timeoutSuccess);
				} else {
					if (response.status === 403) {
						this[secondFactorButtonSymbol].setMessage(
							this.getOption("labels.messageForbidden"),
						);
					} else if (response.status === 401) {
						this[secondFactorButtonSymbol].setMessage(
							this.getOption("labels.messageLoginFailed"),
						);
					} else {
						this[secondFactorButtonSymbol].setMessage(
							this.getOption("labels.messageSomethingWentWrong"),
						);
					}
					this[secondFactorButtonSymbol].showMessage(timeout);
					this[secondFactorButtonSymbol].setState("failed", timeout);
					setTimeout(() => {
						digitsElement.focus();
					}, 0);
				}
			})
			.catch(() => {
				this[secondFactorButtonSymbol].setMessage(
					this.getOption("labels.messageSomethingWentWrong"),
				);
				this[secondFactorButtonSymbol].showMessage(timeout);
				this[secondFactorButtonSymbol].setState("failed", timeout);
				setTimeout(() => {
					digitsElement.focus();
				}, 0);
			});
	});

	this[digitsButtonSymbol].setOption("actions.click", (event) => {
		const digitsElement = this.shadowRoot.getElementById("digitsControl");

		const digits = digitsElement.value;
		const valid = digitsElement.checkValidity();

		let msg = null;
		if (digits === "" || digits === null || digits === undefined) {
			msg = this.getOption("labels.digitsEmpty");
		} else if (!valid) {
			msg = this.getOption("labels.digitsInvalid");
		}

		const timeout = this.getOption("timeoutForMessage");
		if (msg !== null && msg !== undefined) {
			this[digitsButtonSymbol].setMessage(msg);
			this[digitsButtonSymbol].showMessage(timeout);
			this[digitsButtonSymbol].setState("failed", timeout);
			return;
		}

		const url = this.getOption("fetch.digits.url");

		if (url === "" || url === null || url === undefined) {
			this[digitsButtonSymbol].setMessage(
				this.getOption("labels.messageThisFormIsNotConfigured"),
			);
			this[digitsButtonSymbol].showMessage(timeout);
			this[digitsButtonSymbol].setState("failed", timeout);
			return;
		}

		const options = {
			method: this.getOption("fetch.digits.method"),
			mode: this.getOption("fetch.digits.mode"),
			headers: this.getOption("fetch.digits.headers"),
			credentials: this.getOption("fetch.digits.credentials"),
			body: JSON.stringify({ digits }),
		};

		getWindow()
			.fetch(url, options)
			.then((response) => {
				const timeoutSuccess = this.getOption("timeoutForSuccess");

				if (response.ok) {
					this[digitsButtonSymbol].setState("successful", timeoutSuccess);
					fireEvent(this, "digits-success");
					setTimeout(() => {
						this.openLoggedIn();
					}, timeoutSuccess);
				} else {
					if (response.status === 403) {
						this[digitsButtonSymbol].setMessage(
							this.getOption("labels.messageForbidden"),
						);
					} else if (response.status === 401) {
						this[digitsButtonSymbol].setMessage(
							this.getOption("labels.messageLoginFailed"),
						);
					} else {
						this[digitsButtonSymbol].setMessage(
							this.getOption("labels.messageSomethingWentWrong"),
						);
					}
					this[digitsButtonSymbol].showMessage(timeout);
					this[digitsButtonSymbol].setState("failed", timeout);
					setTimeout(() => {
						digitsElement.focus();
					}, 0);
				}
			})
			.catch(() => {
				this[digitsButtonSymbol].setMessage(
					this.getOption("labels.messageSomethingWentWrong"),
				);
				this[digitsButtonSymbol].showMessage(timeout);
				this[digitsButtonSymbol].setState("failed", timeout);
				setTimeout(() => {
					digitsElement.focus();
				}, 0);
			});
	});

	return this;
}

/**
 * @private
 * @return {void}
 */
function initControlReferences() {
	this[loginElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="control"]`,
	);

	// data-monster-role="forgot-password-link"

	this[forgotPasswordLinkSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="forgot-password-link"]`,
	);

	this[forgotPasswordCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="forgot-password-collapse"]`,
	);

	this[loginCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="login-collapse"]`,
	);

	this[loginLinkSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="login-link"]`,
	);

	this[secondFactorCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="second-factor-collapse"]`,
	);

	this[resetLoginProcessLinksSymbol] = this.shadowRoot.querySelectorAll(
		`[data-monster-role="reset-login-process-link"]`,
	);

	this[loggedInCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="logged-in-collapse"]`,
	);

	this[customCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="custom-collapse"]`,
	);

	this[customCollapseHeaderSymbol] = this.shadowRoot.getElementById(
		"customCollapseHeader",
	);
	this[customCollapseContentSymbol] = this.shadowRoot.getElementById(
		"customCollapseContent",
	);
	this[customCollapseFooterSymbol] = this.shadowRoot.getElementById(
		"customCollapseFooter",
	);

	this[digitsCollapseSymbol] = this.shadowRoot.querySelector(
		`[data-monster-role="digits-collapse"]`,
	);

	this[loginButtonSymbol] = this.shadowRoot.getElementById("loginButton");
	this[requestLinkButtonSymbol] =
		this.shadowRoot.getElementById("requestLinkButton");
	this[secondFactorButtonSymbol] =
		this.shadowRoot.getElementById("secondFactorButton");
	this[digitsButtonSymbol] = this.shadowRoot.getElementById("digitsButton");
}

/**
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `

        <template id="urls">
            <li><a data-monster-attributes="href path:urls.url"
                   data-monster-replace="path:urls.label"></a></li>
        </template>

        <div data-monster-role="control" part="control">

            <monster-collapse data-monster-option-openByDefault="true"
                              data-monster-role="login-collapse"
                              exportparts="control:collapse-login-control,
										   container:collapse-login-container,
										   deco:collapse-login-deco"
                              part="login-collapse">
                <monster-field-set
                        exportparts="
						control:field-set-login-control,
						header:field-set-login-header,
						title:field-set-login-title,
						container:field-set-login-container,
						content:field-set-login-content,
						collapse:field-set-login-collapse"
                        part="login-field-set">
                    <slot name="login-header"></slot>
                    <label part="login-label" data-monster-replace="path:labels.username"></label>
                    <input part="login-username"
						   data-monster-attributes="placeholder path:placeholder.username, autocomplete path:autocomplete.username"
						   type="text" name="username" autofocus>
                    <div data-monster-attributes="class path:classes.usernameInvalid"></div>
                    <label part="login-password-label" data-monster-replace="path:labels.password"></label>
                    <monster-password
						data-monster-attributes="data-monster-option-placeholder path:placeholder.password, data-monster-option-autocomplete path:autocomplete.password"
						exportparts="input-group:input-group-password,
						control:input-group-password-control,
						input:input-group-password-input" part="login-password"
						data-monster-bind="path:password"></monster-password>
                    <div data-monster-attributes="class path:classes.passwordInvalid"></div>
                    <monster-message-state-button id="loginButton"
                                                  part="login-button"
												  data-monster-attributes="data-monster-option-classes-button path:classes.button"
                                                  exportparts="
														control:login-button-control,
														button:login-button-button,
														popper:login-button-popper,
														message:login-button-message,
														button-button:login-button-button-button,
														button-control:login-button-control"
                                                  data-monster-replace="path:labels.login"></monster-message-state-button>
                    <a href="#" data-monster-role="forgot-password-link" part="forgot-password-link"
                       data-monster-attributes="class path:features.forgotPassword | ?::hidden"
                       data-monster-replace="path:labels.forgotPasswordLink"></a>
                    <slot name="login-footer"></slot>
                </monster-field-set>


            </monster-collapse>
            <monster-collapse data-monster-role="forgot-password-collapse"
                              exportparts="control:collapse-forgot-password-control,
											container:collapse-forgot-password-container,
											deco:collapse-forgot-password-deco"
                              part="forgot-password-collapse">

                <monster-field-set part="forgot-password-field-set"
                                   exportparts="
									control:field-set-forgot-password-control,
									header:field-set-forgot-password-header,
									title:field-set-forgot-password-title,
									container:field-set-forgot-password-container,
									content:field-set-forgot-password-content,
									collapse:field-set-forgot-password-collapse">
                    <slot name="forgot-password-header"></slot>
                    <label part="forgot-password-label" data-monster-replace="path:labels.mailAddress"></label>
                    <input type="email" name="email"
						   part="field-set-forgot-password-email"
                           data-monster-attributes="accesskey path:accessKeys.username,
                                                    placeholder path:placeholder.email,
                                                    autocomplete path:autocomplete.email"
                           >
                    <div data-monster-attributes="class path:classes.emailInvalid"></div>
                    <monster-message-state-button id="requestLinkButton"
                                                  part="request-link-button"
												  data-monster-attributes="data-monster-option-classes-button path:classes.button"
                                                  exportparts="
														control:forgot-password-button-control,
														button:forgot-password-button-button,
														popper:forgot-password-button-popper,
														message:forgot-password-button-message,
														button-button:forgot-password-button-button-button,
														button-control:forgot-password-button-control"
                                                  data-monster-attributes="accesskey path:accessKeys.password"
                                                  data-monster-replace="path:labels.requestLink"></monster-message-state-button>
                    <a href="#"
                       data-monster-attributes="accesskey path:accessKeys.loginLink"
                       data-monster-role="login-link"
                       part="login-link"
                       data-monster-replace="path:labels.resetLoginProcess"></a>
                    <slot name="forgot-password-footer"></slot>
                </monster-field-set>

            </monster-collapse>
            <monster-collapse data-monster-role="second-factor-collapse"
                              exportparts="control:collapse-second-factor-control,
										   container:collapse-second-factor-container,
										   deco:collapse-second-factor-deco"
                              part="second-factor-collapse">

                <monster-field-set part="second-factor-field-set"
                                   exportparts="
										control:field-set-second-factor-control,
										header:field-set-second-factor-header,
										title:field-set-second-factor-title,
										container:field-set-second-factor-container,
										content:field-set-second-factor-content,
										collapse:field-set-second-factor-collapse">
                    <slot name="second-factor-header"></slot>
                    <label part="second-factor-label" data-monster-replace="path:labels.secondFactor"></label>
                    <div>
                        <monster-digits data-monster-attributes="data-monster-option-digits path:digits"
										part="second-factor-digits-control"
                                        id="secondFactorControl"></monster-digits>
                    </div>
                    <!-- SECOND FACTOR COLLAPSE -->
                    <monster-message-state-button id="secondFactorButton"
                                                  part="secondFactor-digits-button"
												  data-monster-attributes="data-monster-option-classes-button path:classes.button"
                                                  exportparts="
														control:second-factor-button-control,
														button:second-factor-button-button,
														popper:second-factor-button-popper,
														message:second-factor-button-message,
														button-button:second-factor-button-button-button,
														button-control:second-factor-button-control"
                                                  data-monster-replace="path:labels.sendSecondFactorDigits"></monster-message-state-button>

                    <a href="#" data-monster-role="reset-login-process-link"
                       part="reset-login-process-link"
                       data-monster-replace="path:labels.resetLoginProcess"></a>
                    <slot name="second-factor-footer"></slot>
                </monster-field-set>

            </monster-collapse>
            <monster-collapse data-monster-role="digits-collapse"
                              exportparts="control:collapse-digits-control,
										container:collapse-digits-container,
										deco:collapse-digits-deco"
                              part="digits-collapse">

                <monster-field-set part="digits-field-set"
                                   exportparts="
										control:field-set-digits-control,
										header:field-set-digits-header,
										title:field-set-digits-title,
										container:field-set-digits-container,
										content:field-set-digits-content,
										collapse:field-set-digits-collapse">
                    <slot name="digits-header"></slot>
                    <label part="digits-label" data-monster-replace="path:labels.digits"></label>
                    <div>
                        <monster-digits part="digits-control" 
							data-monster-attributes="data-monster-option-digits path:digits"
                                        id="digitsControl"></monster-digits>
                    </div>
                    <monster-message-state-button id="digitsButton"
                                                  part="digits-button"
												  data-monster-attributes="data-monster-option-classes-button path:classes.button"
                                                  exportparts="
														control:digits-button-control,
														button:digits-button-button,
														popper:digits-button-popper,
														message:digits-button-message,
														button-button:digits-button-button,
														button-control:digits-button-control"
                                                  data-monster-replace="path:labels.sendDigits"></monster-message-state-button>
                    <a href="#" data-monster-role="reset-login-process-link"
                       part="reset-login-process-link"
                       data-monster-replace="path:labels.resetLoginProcess"></a>
                    <slot name="digits-footer"></slot>
                </monster-field-set>

            </monster-collapse>
            <monster-collapse data-monster-role="logged-in-collapse"
                              exportparts="
								control:collapse-success-control,
								container:collapse-success-container,
								deco:collapse-success-deco"
                              part="logged-in-collapse">
                <div>
                    <slot name="logged-in-header"></slot>
                    <ul data-monster-insert="urls path:successUrls"></ul>
                    <slot name="logged-in-content"></slot>
                    <slot name="logged-in-footer"></slot>
                </div>
            </monster-collapse>
            <monster-collapse data-monster-role="custom-collapse"
                              exportparts="
								control:collapse-custom-control,
								container:collapse-custom-container,
								deco:collapse-custom-deco"
                              part="custom-collapse">
                <div part="custom-collapse-wrapper">
                    <div id="customCollapseHeader" part="custom-collapse-header"></div>
                    <div id="customCollapseContent" part="custom-collapse-content"></div>
                    <div id="customCollapseFooter" part="custom-collapse-footer"></div>
                </div>
            </monster-collapse>

        </div>`;
}

/**
 * @private
 * @param {HTMLElement} target
 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
 * @returns {void}
 */
function setCustomCollapseSectionContent(target, content) {
	if (!(target instanceof HTMLElement)) {
		return;
	}

	target.replaceChildren(...normalizeCustomCollapseContent.call(this, content));

	if (this[customCollapseSymbol]?.isOpen()) {
		this[customCollapseSymbol].adjustHeight();
	}
}

/**
 * @private
 * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
 * @returns {Node[]}
 */
function normalizeCustomCollapseContent(content) {
	if (content === null || content === undefined) {
		return [];
	}

	if (content instanceof Node) {
		return [content];
	}

	if (isArray(content)) {
		return content.flatMap((entry) =>
			normalizeCustomCollapseContent.call(this, entry),
		);
	}

	return [this.ownerDocument.createTextNode(String(content))];
}

/**
 * @private
 * @returns {void}
 */
function focusFirstCustomCollapseElement() {
	setTimeout(() => {
		const focusable = this[customCollapseSymbol]?.querySelector(
			'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
		);

		focusable?.focus?.();
	}, 0);
}

registerCustomElement(Login);
