/**
 * 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 { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
	assembleMethodSymbol,
	registerCustomElement,
} from "../../dom/customelement.mjs";
import { DayStyleSheet } from "./stylesheet/day.mjs";
import { isString } from "../../types/is.mjs";
import { getLocaleOfDocument } from "../../dom/locale.mjs";
import { Observer } from "../../types/observer.mjs";
import { Processing } from "../../util/processing.mjs";

export { Day };

/**
 * @private
 * @type {symbol}
 */
const dayElementSymbol = Symbol("dayElement");

/**
 * @private
 * @type {symbol}
 */
const dayMonthElementSymbol = Symbol("dayMonthElement");
/**
 * @private
 * @type {symbol}
 */
const dayDayElementSymbol = Symbol("dayDayElement");
/**
 * @private
 * @type {symbol}
 */
const dayWeekdayElementSymbol = Symbol("dayWeekdayElement");
/**
 * @private
 * @type {symbol}
 */
const dayLabelElementSymbol = Symbol("dayLabelElement");

/**
 * A Day Control
 *
 * @fragments /fragments/components/time/day/
 *
 * @example /examples/components/time/day-simple
 * @example /examples/components/time/day-simple-small
 *
 * @since 3.113.0
 * @copyright Volker Schukai
 * @summary A day control that displays the day, month, weekday and a label.
 */
class Day extends CustomElement {
	/**
	 *
	 */
	constructor() {
		super();
		initOptionObserver.call(this);
	}

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

	/**
	 *
	 * @return {Components.Time.Day
	 */
	[assembleMethodSymbol]() {
		super[assembleMethodSymbol]();
		initControlReferences.call(this);
		updateDate.call(this);
		return this;
	}

	/**
	 * 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 {string} date The date of the day
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},

			date: null,

			day: null,
			month: null,
			weekday: null,
			label: null,

			disabled: false,
		});
	}

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

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

	/**
	 * @return {string[]}
	 * @since 3.113.0
	 */
	static get observedAttributes() {
		return ["data-monster-option-date"];
	}
}

/**
 * @private
 */
function initOptionObserver() {
	const self = this;

	let lastDate = this.getOption("date");

	self.attachObserver(
		new Observer(function () {
			new Processing(() => {
				if (lastDate !== self.getOption("date")) {
					lastDate = self.getOption("date");
					updateDate.call(self);
				}
			}).run();
		}),
	);
}

/**
 * @private
 */
function updateDate() {
	const labels = getLabels.call(this);

	let date = this.getOption("date");

	let day = null;
	let label = null;

	if (date === null) {
		date = new Date();
	} else if (isString(date)) {
		date = new Date(date);
	}

	if (!(date instanceof Date)) {
		return;
	}

	day = date.getDate();

	const locale = getLocaleOfDocument();

	this.setOption("day", day);
	this.setOption(
		"month",
		new Intl.DateTimeFormat(locale.toString(), {
			month: "short",
		}).format(date),
	);

	this.setOption(
		"weekday",
		new Intl.DateTimeFormat(locale.toString(), {
			weekday: "long",
		}).format(date),
	);

	if (date.toDateString() === new Date().toDateString()) {
		label = labels.today;
	} else if (
		date.toDateString() ===
		new Date(new Date().setDate(new Date().getDate() - 1)).toDateString()
	) {
		label = labels.yesterday;
	} else if (
		date.toDateString() ===
		new Date(new Date().setDate(new Date().getDate() + 1)).toDateString()
	) {
		label = labels.tomorrow;
	}

	this.setOption("label", label);
}

function getLabels() {
	switch (getLocaleOfDocument().language) {
		case "de": // German
			return {
				today: "Heute",
				tomorrow: "Morgen",
				yesterday: "Gestern",
			};

		case "es": // Spanish
			return {
				today: "Hoy",
				tomorrow: "Mañana",
				yesterday: "Ayer",
			};

		case "zh": // Mandarin
			return {
				today: "今天",
				tomorrow: "明天",
				yesterday: "昨天",
			};

		case "hi": // Hindi
			return {
				today: "आज",
				tomorrow: "कल",
				yesterday: "बीता कल",
			};

		case "bn": // Bengali
			return {
				today: "আজ",
				tomorrow: "আগামীকাল",
				yesterday: "গতকাল",
			};

		case "pt": // Portuguese
			return {
				today: "Hoje",
				tomorrow: "Amanhã",
				yesterday: "Ontem",
			};

		case "ru": // Russian
			return {
				today: "Сегодня",
				tomorrow: "Завтра",
				yesterday: "Вчера",
			};

		case "ja": // Japanese
			return {
				today: "今日",
				tomorrow: "明日",
				yesterday: "昨日",
			};

		case "pa": // Western Punjabi
			return {
				today: "ਅਜ",
				tomorrow: "ਕਲ",
				yesterday: "ਬੀਤੇ ਕਲ",
			};

		case "mr": // Marathi
			return {
				today: "आज",
				tomorrow: "उद्या",
				yesterday: "काल",
			};

		case "fr": // French
			return {
				today: "Aujourd'hui",
				tomorrow: "Demain",
				yesterday: "Hier",
			};

		case "it": // Italian
			return {
				today: "Oggi",
				tomorrow: "Domani",
				yesterday: "Ieri",
			};

		case "nl": // Dutch
			return {
				today: "Vandaag",
				tomorrow: "Morgen",
				yesterday: "Gisteren",
			};

		case "sv": // Swedish
			return {
				today: "Idag",
				tomorrow: "Imorgon",
				yesterday: "Igår",
			};

		case "pl": // Polish
			return {
				today: "Dziś",
				tomorrow: "Jutro",
				yesterday: "Wczoraj",
			};

		case "da": // Danish
			return {
				today: "I dag",
				tomorrow: "I morgen",
				yesterday: "I går",
			};

		case "fi": // Finnish
			return {
				today: "Tänään",
				tomorrow: "Huomenna",
				yesterday: "Eilen",
			};

		case "no": // Norwegian
			return {
				today: "I dag",
				tomorrow: "I morgen",
				yesterday: "I går",
			};

		case "cs": // Czech
			return {
				today: "Dnes",
				tomorrow: "Zítra",
				yesterday: "Včera",
			};

		default:
			return {
				today: "Today",
				tomorrow: "Tomorrow",
				yesterday: "Yesterday",
			};
	}
}

/**
 * @private
 * @param format
 * @returns {*[]}
 */
function getWeekdays(format = "long") {
	const locale = getLocaleOfDocument();

	const weekdays = [];
	for (let i = 1; i < 8; i++) {
		const date = new Date(1970, 0, 4 + i); // 4. Jan. 1970 = Sonntag
		weekdays.push(
			new Intl.DateTimeFormat(locale, { weekday: format }).format(date),
		);
	}

	return weekdays;
}

/**
 * @private
 * @return {void}
 */
function initControlReferences() {
	this[dayElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="control"]`,
	);
	this[dayMonthElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="month"]`,
	);
	this[dayDayElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="day"]`,
	);
	this[dayWeekdayElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="weekday"]`,
	);
	this[dayLabelElementSymbol] = this.shadowRoot.querySelector(
		`[${ATTRIBUTE_ROLE}="label"]`,
	);
}

/**
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `
        <div data-monster-role="control" part="control">
            <div class="month" data-monster-role="month" part="month" data-monster-replace="path:month"></div>
            <div data-monster-role="day" part="day" data-monster-replace="path:day">12</div>
            <div style="width: 100%; height: 0;" part="divider"></div>
            <div data-monster-role="weekday" part="weekday" data-monster-replace="path:weekday">Wed</div>
            <div data-monster-role="label" part="label" data-monster-replace="path:label">Tomorrow</div>
        </div>`;
}

registerCustomElement(Day);
