/**
 * 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.de.html
 *
 * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
 * For more information about purchasing a commercial license, please contact Volker Schukai.
 */

import { instanceSymbol } from "../../constants.mjs";
import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
	assembleMethodSymbol,
	registerCustomElement,
} from "../../dom/customelement.mjs";
import { isFunction, isObject, isArray } from "../../types/is.mjs";
import { LocalePickerStyleSheet } from "./stylesheet/locale-picker.mjs";
import { detectUserLanguagePreference } from "../../i18n/util.mjs";

import "../form/select.mjs";

export { LocaleSelect as LocalSelector };

/**
 * @private
 * @type {symbol}
 * Reference to the select element
 */
const selectElementSymbol = Symbol("selectElement");

/**
 * @private
 * @type {symbol}
 * Stores detected languages
 */
const detectedLanguagesSymbol = Symbol("detectedLanguages");

/**
 * Returns the localized label for the language selection.
 * @return {string}
 */
function getLocalizedLabel() {
	const lang = document.documentElement.lang || navigator.language || "en";
	switch (lang.split("-")[0]) {
		case "de":
			return "Sprache wählen";
		case "fr":
			return "Sélectionnez une langue";
		case "es":
			return "Seleccione un idioma";
		case "it":
			return "Seleziona una lingua";
		case "pt":
			return "Selecione um idioma";
		case "nl":
			return "Selecteer een taal";
		case "pl":
			return "Wybierz język";
		case "ru":
			return "Выберите язык";
		case "cs":
			return "Vyberte jazyk";
		case "sk":
			return "Vyberte jazyk";
		case "bg":
			return "Изберете език";
		case "hr":
			return "Odaberite jezik";
		case "fi":
			return "Valitse kieli";
		case "sv":
			return "Välj ett språk";
		case "el":
			return "Επιλέξτε γλώσσα";
		case "hu":
			return "Válasszon egy nyelvet";
		case "ro":
			return "Selectați o limbă";
		case "da":
			return "Vælg et sprog";
		case "no":
			return "Velg et språk";
		case "hi":
			return "एक भाषा चुनें";
		case "bn":
			return "একটি ভাষা নির্বাচন করুন";
		case "ta":
			return "ஒரு மொழியைத் தேர்ந்தெடுக்கவும்";
		case "te":
			return "భాషను ఎంచుకోండి";
		case "mr":
			return "एक भाषा निवडा";
		case "zh":
			return "选择一种语言";
		case "ja":
			return "言語を選択してください";
		default:
			return "Select a language";
	}
}

/**
 * LocaleSelect component
 *
 * Displays a monster-select with all available languages except the current document language.
 *
 * @fragments /fragments/components/accessibility/locale-select/
 *
 * @example /examples/components/accessibility/locale-select-simple Simple language switcher
 *
 * @since 3.97.0
 * @copyright Volker Schukai
 * @summary A select-based locale switcher for multilingual forms, apps and documentation surfaces.
 */
class LocaleSelect extends CustomElement {
	/**
	 * Used by the instanceof operator.
	 * @returns {symbol}
	 */
	static get [instanceSymbol]() {
		return Symbol.for(
			"@schukai/monster/components/accessibility/local-selector@@instance",
		);
	}

	/**
	 * Initializes the component.
	 * @return {LocaleSelect}
	 */
	[assembleMethodSymbol]() {
		super[assembleMethodSymbol]();
		initControlReferences.call(this);
		initSelectOptions.call(this);
		initEventHandler.call(this);
		return this;
	}

	/**
	 * Default options
	 *
	 * @properties {Object} templates - Templates for the component.
	 * @properties {string} templates.main - Main template.
	 * @properties {Object} features - Feature toggles.
	 * @properties {boolean} features.resetCookie - Whether to show a reset cookie button.
	 * @properties {Object} cookie - Cookie settings.
	 * @properties {string} cookie.name - Name of the cookie to store the selected language.
	 * @properties {Object} labels - Localized labels.
	 * @properties {string} labels["select-an-option"] - Label for the select option.
	 * @properties {Object} callbacks - Callback functions.
	 * @properties {boolean} disabled - Whether the component is disabled.
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},
			features: {
				resetCookie: true,
			},
			cookie: {
				name: "language",
			},
			labels: {
				"select-an-option": getLocalizedLabel(),
			},
			callbacks: {},
			disabled: false,
		});
	}

	/**
	 * connectedCallback
	 */
	connectedCallback() {
		super.connectedCallback();
		this[detectedLanguagesSymbol] = detectUserLanguagePreference();
		initSelectOptions.call(this);
	}

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

	/**
	 * @return {CSSStyleSheet[]}
	 */
	static getCSSStyleSheet() {
		return [LocalePickerStyleSheet];
	}

	/**
	 * Export parts from monster-select to make them available for styling outside.
	 */
	static get exportparts() {
		// The parts from monster-select, as defined in source/components/form/select.mjs:
		return [
			"control",
			"container",
			"popper",
			"option",
			"option-label",
			"option-control",
			"badge",
			"badge-label",
			"remove-badge",
			"summary",
			"status-or-remove-badges",
			"remote-info",
			"no-options",
			"selection",
			"inline-filter",
			"popper-filter",
			"content",
		].join(",");
	}
}

/**
 * Initializes the reference to the select element.
 * @private
 */
function initControlReferences() {
	this[selectElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="select"]`,
	);
}

/**
 * Initializes the options of the select element.
 * @private
 */
function initSelectOptions() {
	const detected =
		this[detectedLanguagesSymbol] || detectUserLanguagePreference();

	let options = [];
	if (Array.isArray(detected.available)) {
		const currentLang = detected.current;
		options = detected.available.filter(
			(lang) => lang.baseLang !== currentLang && lang.fullLang !== currentLang,
		);
	} else if (Array.isArray(detected.allOfferable)) {
		options = detected.allOfferable;
	}

	options = detected.allOfferable;

	if (Array.isArray(options)) {
		options = options.filter(
			(lang) => lang.baseLang !== "x-default" && lang.fullLang !== "x-default",
		);
	}

	if (this[selectElementSymbol] && isArray(options)) {
		this[selectElementSymbol].setOption("mapping.labelTemplate", "${label}");
		this[selectElementSymbol].setOption("mapping.valueTemplate", "${href}");
		this[selectElementSymbol].setOption(
			"labels.select-an-option",
			this.getOption("labels.select-an-option"),
		);
		this[selectElementSymbol].importOptions(options);
	}
}

/**
 * Initializes the event handler for the select element.
 * @private
 */
function initEventHandler() {
	if (!this[selectElementSymbol]) return;

	this[selectElementSymbol].addEventListener("change", (event) => {
		const selected = event.target?.value;
		if (selected) {
			if (
				this.getOption("features.resetCookie") &&
				this.getOption("cookie.name")
			) {
				document.cookie = `${this.getOption("cookie.name")}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
			}

			window.location.href = selected;
		}
	});
}

/**
 * Returns the template for the component.
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `
    <monster-select exportparts="control,container,popper,option,option-label,option-control,badge,badge-label,remove-badge,summary,status-or-remove-badges,remote-info,no-options,selection,inline-filter,popper-filter,content" data-monster-role="select"></monster-select>
  `;
}

registerCustomElement(LocaleSelect);
