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

import {
	assembleMethodSymbol,
	registerCustomElement,
} from "../../dom/customelement.mjs";
import { DetailsStyleSheet } from "./stylesheet/details.mjs";
import { ATTRIBUTE_BUTTON_LABEL } from "../host/constants.mjs";
import { isString } from "../../types/is.mjs";
import { generateUniqueConfigKey } from "../host/util.mjs";
import { Collapse, nameSymbol } from "./collapse.mjs";
import { instanceSymbol } from "../../constants.mjs";

export { Details };

/**
 * @private
 * @type {symbol}
 */
const buttonElementSymbol = Symbol("buttonElement");

/**
 * @private
 * @type {symbol}
 */
const buttonEventHandlerSymbol = Symbol("buttonEventHandler");

/**
 * The Details component is used to show a details.
 *
 * <img src="./images/details.png">
 *
 * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
 *
 * You can create this control either by specifying the HTML tag <monster-details />` directly in the HTML or using
 * Javascript via the `document.createElement('monster-details');` method.
 *
 * ```html
 * <monster-details></monster-details>
 * ```
 *
 * Or you can create this CustomControl directly in Javascript:
 *
 * ```js
 * import '@schukai/component-state/source/details.mjs';
 * document.createElement('monster-details');
 * ```
 *
 * The Body should have a class "hidden" to ensure that the styles are applied correctly.
 *
 * ```css
 * body.hidden {
 *    visibility: hidden;
 * }
 * ```
 *
 * @startuml details.png
 * skinparam monochrome true
 * skinparam shadowing false
 * HTMLElement <|-- CustomElement
 * CustomElement <|-- Collapse
 * Collapse <|-- Details
 * @enduml
 *
 * @copyright schukai GmbH
 * @memberOf Monster.Components.Layout
 * @summary A simple details component
 * @fires Monster.Components.Layout.Details.event:monster-details-before-open
 * @fires Monster.Components.Layout.Details.event:monster-details-open
 * @fires Monster.Components.Layout.Details.event:monster-details-before-close
 * @fires Monster.Components.Layout.Details.event:monster-details-closed
 */
class Details extends Collapse {
	/**
	 * This method is called by the `instanceof` operator.
	 * @returns {symbol}
	 */
	static get [instanceSymbol]() {
		return Symbol.for("@schukai/monster/components/layout/details@@instance");
	}

	/**
	 *
	 */
	constructor() {
		super();
		// the name is only used for the host config and the event name
		this[nameSymbol] = "details";
	}

	/**
	 * 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} classes CSS classes
	 * @property {string} classes.button CSS class for the button
	 * @property {Object} button Button configuration
	 * @property {string} button.label Button label
	 * @property {Object} features Feature configuration
	 * @property {boolean} features.accordion Enable accordion mode
	 * @property {boolean} features.persistState Persist the state in the host configuration
	 */
	get defaults() {
		return Object.assign({}, super.defaults, {
			templates: {
				main: getTemplate(),
			},
			labels: {
				button: "Details",
			},
		});
	}

	/**
	 *
	 * @returns {Monster.Components.Layout.Details}
	 */
	[assembleMethodSymbol]() {
		super[assembleMethodSymbol]();

		initButtonLabel.call(this);
		initControlReferences.call(this);
		initEventHandler.call(this);
	}

	connectedCallback() {
		super.connectedCallback();

		const containDocument = this.shadowRoot;

		if (containDocument !== null) {
			const previousElement = this.previousElementSibling;
			if (previousElement && previousElement.tagName === "MONSTER-DETAILS") {
				this[buttonElementSymbol].style.borderTop = "0";
			}
		}
	}

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

	/**
	 * @return {Array<CSSStyleSheet>}
	 */
	static getCSSStyleSheet() {
		const css = super.getCSSStyleSheet();
		css.push(DetailsStyleSheet);
		return css;
	}
}

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

	this[buttonElementSymbol] = this.shadowRoot.querySelector(
		"[data-monster-role=button]",
	);
}

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

	this[buttonEventHandlerSymbol] = (event) => {
		this.toggle();
	};

	this[buttonElementSymbol].addEventListener(
		"click",
		this[buttonEventHandlerSymbol],
	);

	return this;
}

/**
 * @private
 * @return {string}
 */
function initButtonLabel() {
	let label;
	const setLabel = false;
	if (this.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
		label = this.getAttribute(ATTRIBUTE_BUTTON_LABEL);
	} else {
		label = this.innerText;
	}

	if (!isString(label)) {
		label = "";
	}

	label = label.trim();

	if (label === "") {
		label = this.getOption("labels.button", "Details");
	}

	if (label.length > 100) {
		label = `${label.substring(0, 99)}…`;
	}

	this.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
	this.setOption("labels.button", label);

	return label;
}

/**
 * @private
 * @returns {string}
 */
function getConfigKey() {
	return generateUniqueConfigKey("details", this.id, "state");
}

/**
 * @private
 * @return {string}
 */
function getTemplate() {
	// language=HTML
	return `
        <div data-monster-role="control" part="control" class="overflow-hidden">
            <div data-monster-role="summary" part="summary">
                <button part="button" data-monster-attributes="class path:classes.button"
                        data-monster-role="button"
                        data-monster-replace="path:labels.button | default:click me">click me
                </button>
            </div>
            <div data-monster-role="detail">
                <div data-monster-attributes="class path:classes.container" part="container"
                     data-monster-role="container">
                    <slot></slot>
                </div>
                <div class="deco-line" data-monster-role="deco"></div>
            </div>
        </div>`;
}

registerCustomElement(Details);