/**
 * EGroupware eTemplate2 - InvokerMixing
 *
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @package api
 * @link https://www.egroupware.org
 * @author Ralf Becker
 */

/* eslint-disable import/no-extraneous-dependencies */
import {css, html, LitElement, render} from 'lit';
import {Et2InputWidget, Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
import {colorsDefStyles} from "../Styles/colorsDefStyles";
import {dedupeMixin} from "@open-wc/dedupe-mixin";

/**
 * Invoker mixing adds an invoker button to a widget to trigger some action, e.g.:
 * - searchbox to delete input
 * - url to open url
 * - url-email to open mail compose
 *
 * Inspired by Lion date-picker.
 */

type Constructor<T = Et2InputWidgetInterface> = new (...args : any[]) => T;
export const Et2InvokerMixin = dedupeMixin(<T extends Constructor<LitElement>>(superclass : T) =>
{
	class Et2Invoker extends Et2InputWidget(superclass)
	{
		/** @type {any} */
		static get properties()
		{
			return {
				/**
				 * Textual label or image specifier for egw.image()
				 */
				_invokerLabel: {
					type: String,
				},
				_invokerTitle: {
					type: String,
				},
				_invokerAction: {
					type: Function,
				}
			};
		}

		static get styles()
		{
			return [
				...super.styles,
				colorsDefStyles,
				css`
				::slotted(input), input, ::slotted(select) {
					background-color: transparent;
					border: none !important;
				}
				.input-group {
					border: 1px solid var(--input-border-color);
				}
				.input-group__suffix{
					text-align: center;
				}
				.input-group__container {
					align-items: center
				}
				::slotted([slot="suffix"]) {
					border: none !important;
					background-color: transparent !important;
					width: 2rem;
					margin-inline-end: var(--sl-input-spacing-medium);
				}
				::slotted(:disabled) {cursor: default !important;}
				:host(:hover) ::slotted([slot="suffix"]) {
					cursor: pointer;
				}
			`,
			];
		}

		/**
		 * @protected
		 */
		get _invokerNode()
		{
			return /** @type {HTMLElement} */ (this.querySelector(`#${this.__invokerId}`));
		}

		constructor(...args : any[])
		{
			super(...args);
			/** @private */
			this.__invokerId = this.__createUniqueIdForA11y();
			// default for properties
			this._invokerTitle = 'Click to open';
		}

		/** @private */
		__createUniqueIdForA11y()
		{
			return `${this.localName}-${Math.random().toString(36).substr(2, 10)}`;
		}

		/**
		 * @param {PropertyKey} name
		 * @param {?} oldValue
		 */
		requestUpdate(name, oldValue)
		{
			super.requestUpdate(name, oldValue);

			if (name === 'disabled' || name === 'showsFeedbackFor' || name === 'modelValue')
			{
				this._toggleInvokerDisabled();
			}

			if (name === '_invokerLabel' || name === '_invokerTitle')
			{
				this._toggleInvoker();
			}
			if (name === '_invokerAction')
			{
				if (oldValue) this._invokerNode?.removeEventListener('click', oldValue);
				this._invokerNode?.addEventListener('click', this._invokerAction.bind(this), true);
			}
		}

		/**
		 * (Un)Hide invoker, if no label or action defined
		 *
		 * @protected
		 * */
		_toggleInvoker()
		{
			if (this._invokerNode)
			{
				this._invokerNode.style.display = !this._invokerLabel ? 'none' : 'inline-block';
				const img = this._invokerLabel ? this.egw().image(this._invokerLabel) : null;
				if (img)
				{
					this._invokerNode.innerHTML = '<et2-image src='+img+'></et2-image>';
				}
				else
				{
					this._invokerNode.innerHTML = this._invokerLabel || '';
				}
				this._invokerNode.title = this._invokerTitle || '';
			}
		}

		/**
		 * Method to check if invoker can be activated: not disabled, empty or invalid
		 *
		 * @protected
		 * */
		_toggleInvokerDisabled()
		{
			if (this._invokerNode)
			{
				const invokerNode = /** @type {HTMLElement & {disabled: boolean}} */ (this._invokerNode);
				invokerNode.disabled = this.disabled || !this.value || (this.input && !this.input.reportValidity())
			}
		}

		/**
		 * Reimplemented to enable/disable invoker on content change
		 *
		 * @param _ev
		 * @returns
		 */
		_oldChange(_ev: Event): boolean
		{
			this._toggleInvokerDisabled();

			return super._oldChange(_ev);
		}

		/** @param  changedProperties */
		firstUpdated(changedProperties)
		{
			super.firstUpdated(changedProperties);
			render(this._invokerTemplate(), this);
			this._toggleInvokerDisabled();
			this._toggleInvoker();
		}

		/**
		 * Subclassers can replace this with their custom extension invoker,
		 * like `<my-button><calendar-icon></calendar-icon></my-button>`
		 */
		// eslint-disable-next-line class-methods-use-this
		_invokerTemplate()
		{
			return html`
                <button slot="suffix"
                        type="button"
                        class="et2-invoker__button"
                        @click="${this._invokerAction}"
                        id="${this.__invokerId}"
                        aria-label="${this._invokerTitle}"
                        title="${this._invokerTitle}"
                >
                    ${this._invokerLabel}
                </button>
			`;
		}
	}

	return Et2Invoker as unknown as Constructor<Et2Invoker> & T;
})