/**
 * 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.
 *
 * SPDX-License-Identifier: AGPL-3.0
 */

import { instanceSymbol } from "../../../constants.mjs";
import {
	assembleMethodSymbol,
	registerCustomElement,
} from "../../../dom/customelement.mjs";
import { CustomControl } from "../../../dom/customcontrol.mjs";
import { FilterControlsDefaultsStyleSheet } from "../stylesheet/filter-controls-defaults.mjs";
import { FilterStyleSheet } from "../stylesheet/filter.mjs";
import { getLocaleOfDocument } from "../../../dom/locale.mjs";

export { DatePresets };

/**
 * local symbol
 * @private
 * @type {symbol}
 */
const selectElementSymbol = Symbol("selectElement");

/**
 * The FilterDatePresets component provides pre-defined date ranges.
 *
 * @fragments /fragments/components/datatable/filter/date-presets
 *
 * @example /examples/components/datatable/filter-controls Filter controls
 *
 * @copyright Volker Schukai
 * @summary The FilterDatePresets component provides quick date presets.
 */
class DatePresets extends CustomControl {
	/**
	 * This method is called by the `instanceof` operator.
	 * @return {symbol}
	 */
	static get [instanceSymbol]() {
		return Symbol.for(
			"@schukai/monster/components/filter/date-presets@@instance",
		);
	}

	/**
	 * 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 Main template
	 * @property {Object} labels Text labels
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},
			labels: getTranslations(),
		});
	}

	/**
	 *
	 * @return {string}
	 */
	static getTag() {
		return "monster-filter-date-presets";
	}

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

		initControlReferences.call(this);
		updateOptions.call(this);
	}

	/**
	 * @return Array<CSSStyleSheet>
	 */
	static getCSSStyleSheet() {
		return [FilterControlsDefaultsStyleSheet, FilterStyleSheet];
	}

	/**
	 * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
	 *
	 * @param {*} value
	 */
	set value(value) {
		this[selectElementSymbol].value = value;
	}

	/**
	 * @return {*}
	 */
	get value() {
		return this[selectElementSymbol].value;
	}
}

/**
 * @private
 * @returns {Object}
 */
function getTranslations() {
	const locale = getLocaleOfDocument();
	switch (locale.language) {
		case "de":
			return {
				any: "Beliebig",
				today: "Heute",
				yesterday: "Gestern",
				last7: "Letzte 7 Tage",
				last30: "Letzte 30 Tage",
				thisMonth: "Dieser Monat",
				lastMonth: "Letzter Monat",
			};
		case "es":
			return {
				any: "Cualquiera",
				today: "Hoy",
				yesterday: "Ayer",
				last7: "Últimos 7 días",
				last30: "Últimos 30 días",
				thisMonth: "Este mes",
				lastMonth: "Mes pasado",
			};
		case "zh":
			return {
				any: "不限",
				today: "今天",
				yesterday: "昨天",
				last7: "最近7天",
				last30: "最近30天",
				thisMonth: "本月",
				lastMonth: "上个月",
			};
		case "hi":
			return {
				any: "कोई भी",
				today: "आज",
				yesterday: "कल",
				last7: "पिछले 7 दिन",
				last30: "पिछले 30 दिन",
				thisMonth: "इस माह",
				lastMonth: "पिछला माह",
			};
		case "bn":
			return {
				any: "যেকোনো",
				today: "আজ",
				yesterday: "গতকাল",
				last7: "শেষ ৭ দিন",
				last30: "শেষ ৩০ দিন",
				thisMonth: "এই মাস",
				lastMonth: "গত মাস",
			};
		case "pt":
			return {
				any: "Qualquer",
				today: "Hoje",
				yesterday: "Ontem",
				last7: "Últimos 7 dias",
				last30: "Últimos 30 dias",
				thisMonth: "Este mês",
				lastMonth: "Mês passado",
			};
		case "ru":
			return {
				any: "Любой",
				today: "Сегодня",
				yesterday: "Вчера",
				last7: "Последние 7 дней",
				last30: "Последние 30 дней",
				thisMonth: "Этот месяц",
				lastMonth: "Прошлый месяц",
			};
		case "ja":
			return {
				any: "指定なし",
				today: "今日",
				yesterday: "昨日",
				last7: "過去7日",
				last30: "過去30日",
				thisMonth: "今月",
				lastMonth: "先月",
			};
		case "pa":
			return {
				any: "ਕੋਈ ਵੀ",
				today: "ਅੱਜ",
				yesterday: "ਕੱਲ੍ਹ",
				last7: "ਪਿਛਲੇ 7 ਦਿਨ",
				last30: "ਪਿਛਲੇ 30 ਦਿਨ",
				thisMonth: "ਇਸ ਮਹੀਨੇ",
				lastMonth: "ਪਿਛਲਾ ਮਹੀਨਾ",
			};
		case "mr":
			return {
				any: "कोणतेही",
				today: "आज",
				yesterday: "काल",
				last7: "मागील 7 दिवस",
				last30: "मागील 30 दिवस",
				thisMonth: "हा महिना",
				lastMonth: "मागील महिना",
			};
		case "fr":
			return {
				any: "Tout",
				today: "Aujourd'hui",
				yesterday: "Hier",
				last7: "7 derniers jours",
				last30: "30 derniers jours",
				thisMonth: "Ce mois",
				lastMonth: "Mois dernier",
			};
		case "it":
			return {
				any: "Qualsiasi",
				today: "Oggi",
				yesterday: "Ieri",
				last7: "Ultimi 7 giorni",
				last30: "Ultimi 30 giorni",
				thisMonth: "Questo mese",
				lastMonth: "Mese scorso",
			};
		case "nl":
			return {
				any: "Elke",
				today: "Vandaag",
				yesterday: "Gisteren",
				last7: "Laatste 7 dagen",
				last30: "Laatste 30 dagen",
				thisMonth: "Deze maand",
				lastMonth: "Vorige maand",
			};
		case "sv":
			return {
				any: "Alla",
				today: "Idag",
				yesterday: "Igår",
				last7: "Senaste 7 dagar",
				last30: "Senaste 30 dagar",
				thisMonth: "Denna månad",
				lastMonth: "Förra månaden",
			};
		case "pl":
			return {
				any: "Dowolny",
				today: "Dzisiaj",
				yesterday: "Wczoraj",
				last7: "Ostatnie 7 dni",
				last30: "Ostatnie 30 dni",
				thisMonth: "Ten miesiąc",
				lastMonth: "Poprzedni miesiąc",
			};
		case "da":
			return {
				any: "Alle",
				today: "I dag",
				yesterday: "I går",
				last7: "Seneste 7 dage",
				last30: "Seneste 30 dage",
				thisMonth: "Denne måned",
				lastMonth: "Sidste måned",
			};
		case "fi":
			return {
				any: "Kaikki",
				today: "Tänään",
				yesterday: "Eilen",
				last7: "Viimeiset 7 päivää",
				last30: "Viimeiset 30 päivää",
				thisMonth: "Tämä kuukausi",
				lastMonth: "Edellinen kuukausi",
			};
		case "no":
			return {
				any: "Alle",
				today: "I dag",
				yesterday: "I går",
				last7: "Siste 7 dager",
				last30: "Siste 30 dager",
				thisMonth: "Denne måneden",
				lastMonth: "Forrige måned",
			};
		case "cs":
			return {
				any: "Libovolný",
				today: "Dnes",
				yesterday: "Včera",
				last7: "Posledních 7 dní",
				last30: "Posledních 30 dní",
				thisMonth: "Tento měsíc",
				lastMonth: "Minulý měsíc",
			};
		default:
		case "en":
			return {
				any: "Any",
				today: "Today",
				yesterday: "Yesterday",
				last7: "Last 7 days",
				last30: "Last 30 days",
				thisMonth: "This month",
				lastMonth: "Last month",
			};
	}
}

/**
 * @private
 * @return {DatePresets}
 */
function initControlReferences() {
	if (!this.shadowRoot) {
		throw new Error("no shadow-root is defined");
	}

	this[selectElementSymbol] = this.shadowRoot.querySelector(
		"[data-monster-role=query]",
	);
	return this;
}

/**
 * @private
 * @return {void}
 */
function updateOptions() {
	const labels = this.getOption("labels");
	const currentValue = this[selectElementSymbol].value;
	const presets = buildPresets(labels);

	this[selectElementSymbol].innerHTML = "";

	for (const preset of presets) {
		const option = document.createElement("option");
		option.value = preset.value;
		option.textContent = preset.label;
		this[selectElementSymbol].append(option);
	}

	if (currentValue) {
		this[selectElementSymbol].value = currentValue;
	}
}

/**
 * @private
 * @param {Object} labels
 * @return {Array<{label: string, value: string}>}
 */
function buildPresets(labels) {
	const today = new Date();
	const todayString = formatDate(today);
	const yesterday = formatDate(addDays(today, -1));
	const last7 = buildRange(addDays(today, -6), today);
	const last30 = buildRange(addDays(today, -29), today);
	const thisMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);
	const thisMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
	const lastMonthStart = new Date(today.getFullYear(), today.getMonth() - 1, 1);
	const lastMonthEnd = new Date(today.getFullYear(), today.getMonth(), 0);

	return [
		{ label: labels.any, value: "" },
		{ label: labels.today, value: todayString },
		{ label: labels.yesterday, value: yesterday },
		{ label: labels.last7, value: last7 },
		{ label: labels.last30, value: last30 },
		{
			label: labels.thisMonth,
			value: buildRange(thisMonthStart, thisMonthEnd),
		},
		{
			label: labels.lastMonth,
			value: buildRange(lastMonthStart, lastMonthEnd),
		},
	];
}

/**
 * @private
 * @param {Date} date
 * @return {string}
 */
function formatDate(date) {
	const year = date.getFullYear();
	const month = String(date.getMonth() + 1).padStart(2, "0");
	const day = String(date.getDate()).padStart(2, "0");
	return `${year}-${month}-${day}`;
}

/**
 * @private
 * @param {Date} from
 * @param {Date} to
 * @return {string}
 */
function buildRange(from, to) {
	return `${formatDate(from)} - ${formatDate(to)}`;
}

/**
 * @private
 * @param {Date} date
 * @param {number} amount
 * @return {Date}
 */
function addDays(date, amount) {
	const d = new Date(date);
	d.setDate(d.getDate() + amount);
	return d;
}

/**
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `
        <div data-monster-role="control" part="control">
            <slot></slot>
            <select name="query" data-monster-role="query" data-monster-attributes="value path:query"></select>
        </div>
    `;
}

registerCustomElement(DatePresets);
