/**
 * EGroupware eTemplate2 - Password input widget
 *
 * @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 {Et2InvokerMixin} from "../Et2Url/Et2InvokerMixin";
import {Et2Textbox} from "./Et2Textbox";
import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
import {html} from "lit";
import {classMap} from "lit/directives/class-map.js";
import {ifDefined} from "lit/directives/if-defined.js";
import {egw} from "../../jsapi/egw_global";

const isChromium = navigator.userAgentData?.brands.some(b => b.brand.includes('Chromium'));
const isFirefox = isChromium ? false : navigator.userAgent.includes('Firefox');

/**
 * @customElement et2-password
 */
export class Et2Password extends Et2InvokerMixin(Et2Textbox)
{
	// The password is stored encrypted server side, and passed encrypted.
	// This flag is for if we've decrypted the password to show it already
	private encrypted = true;
	private visible = false;

	/** @type {any} */
	static get properties()
	{
		return {
			...super.properties,
			/**
			 * Password is plaintext
			 */
			plaintext: Boolean,
			/**
			 * Suggest password length (0 for off)
			 */
			suggest: Number,
		};
	}

	constructor(...args : any[])
	{
		super(...args);

		this.plaintext = true;
		this.suggest = 0;

		this._invokerLabel = '';
		this._invokerTitle = this.egw().lang("Suggest password");
		this._invokerAction = () =>
		{
			this.suggestPassword();
		};
	}
	
	transformAttributes(attrs)
	{
		if(typeof attrs.suggest !== "undefined")
		{
			attrs.suggest = parseInt(attrs.suggest);
		}
		attrs.type = 'password';

		if(typeof attrs.viewable !== "undefined")
		{
			attrs['togglePassword'] = attrs.viewable;
			delete attrs.viewable;
		}
		if(typeof attrs.togglePassword !== "undefined" && !attrs.togglePassword
			|| typeof attrs.togglePassword == "string" && !this.getArrayMgr("content").parseBoolExpression(attrs.togglePassword))
		{
			// Unset togglePassword if its false.  It's from parent, and it doesn't handle string "false" = false
			delete attrs.togglePassword;
		}

		super.transformAttributes(attrs);
	}

	/**
	 * 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.readonly;
		}
	}

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

		if (name === 'suggest' && this.suggest != oldValue)
		{
			this._invokerLabel = this.suggest ? 'generate_password' : '';
			this._toggleInvokerDisabled();
		}
	}

	/**
	 * @param _len
	 * @deprecated use this.suggest instead
	 */
	set_suggest(_len)
	{
		this.suggest = _len;
	}

	/**
	 * Ask the server for a password suggestion
	 */
	suggestPassword()
	{
		// They need to see the suggestion
		this.encrypted = false;
		this.type = 'text';
		//this.toggle_visibility(true);

		let suggestion = "Suggestion";
		let request = egw.request("EGroupware\\Api\\Etemplate\\Widget\\Password::ajax_suggest", [this.suggest])
			.then(suggestion =>
			{
				this.encrypted = false;
				this.value = suggestion;

				// Check for second password, update it too
				let two = this.getParent().getWidgetById(this.id+'_2');
				if(two && two.getType() == this.getType())
				{
					two.type = 'text';
					two.value = suggestion;
				}
			});
	}

	/**
	 * If the password is viewable, toggle the visibility.
	 * If the password is still encrypted, we'll ask for the user's password then have the server decrypt it.
	 */
	handlePasswordToggle()
	{
		super.handlePasswordToggle();

		this.visible = !this.visible;	// can't access private isPasswordVisible

		if(!this.visible || !this.encrypted || !this.value)
		{
			this.type = this.visible ? 'text' : 'password';
			return;
		}

		if (this.plaintext) return;	// no need to query user-password, if the password is plaintext

		// Need username & password to decrypt
		Et2Dialog.show_prompt(
			(button, user_password) =>
			{
				if(button == Et2Dialog.CANCEL_BUTTON)
				{
					return this.handlePasswordToggle();
				}
				this.egw().request(
					"EGroupware\\Api\\Etemplate\\Widget\\Password::ajax_decrypt",
					[user_password, this.value]).then(decrypted =>
				{
					if (decrypted)
					{
						this.encrypted = false;
						this.value = decrypted;
						this.type = 'text';
					}
					else
					{
						this.set_validation_error(this.egw().lang("invalid password"));
						window.setTimeout(() =>
						{
							this.set_validation_error(false);
						}, 2000);
					}
				});
			},
			this.egw().lang("Enter your password"),
			this.egw().lang("Authenticate")
		);
	}

	render()
	{
		const hasLabelSlot = this.hasSlotController.test('label');
		const hasHelpTextSlot = this.hasSlotController.test('help-text');
		const hasLabel = this.label ? true : !!hasLabelSlot;
		const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
		const hasClearIcon =
			this.clearable && !this.disabled && !this.readonly && (typeof this.value === 'number' || this.value.length > 0);

		return html`
            <div
                    part="form-control"
                    class=${classMap({
                        'form-control': true,
                        'form-control--small': this.size === 'small',
                        'form-control--medium': this.size === 'medium',
                        'form-control--large': this.size === 'large',
                        'form-control--has-label': hasLabel,
                        'form-control--has-help-text': hasHelpText
                    })}
            >
                <label
                        part="form-control-label"
                        class="form-control__label"
                        for="input"
                        aria-hidden=${hasLabel ? 'false' : 'true'}
                >
                    <slot name="label">${this.label}</slot>
                </label>
                <div part="form-control-input" class="form-control-input">
                    <div
                            part="base"
                            class=${classMap({
                                input: true,
                                // Sizes
                                'input--small': this.size === 'small',
                                'input--medium': this.size === 'medium',
                                'input--large': this.size === 'large',
                                // States
                                'input--pill': this.pill,
                                'input--standard': !this.filled,
                                'input--filled': this.filled,
                                'input--disabled': this.disabled,
                                'input--focused': this.hasFocus,
                                'input--empty': !this.value,
                                'input--no-spin-buttons': this.noSpinButtons,
                                'input--is-firefox': isFirefox
                            })}
                    >
                        <slot name="prefix" part="prefix" class="input__prefix"></slot>
                        <input
                                part="input"
                                id="input"
                                class="input__control"
                                type=${this.type === 'password' && this.passwordVisible ? 'text' : this.type}
                                title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
                                name=${ifDefined(this.name)}
                                ?disabled=${this.disabled}
                                ?readonly=${this.readonly || this.autocomplete == "new-password"}
                                ?required=${this.required}
                                placeholder=${ifDefined(this.placeholder)}
                                minlength=${ifDefined(this.minlength)}
                                maxlength=${ifDefined(this.maxlength)}
                                min=${ifDefined(this.min)}
                                max=${ifDefined(this.max)}
                                step=${ifDefined(this.step as number)}
                                .value=${this.value}
                                autocapitalize=${ifDefined(this.type === 'password' ? 'off' : this.autocapitalize)}
                                autocomplete=${ifDefined(this.autocomplete)}
                                autocorrect="off"
                                ?autofocus=${this.autofocus}
                                spellcheck=${this.spellcheck}
                                pattern=${ifDefined(this.pattern)}
                                enterkeyhint=${ifDefined(this.enterkeyhint)}
                                inputmode=${ifDefined(this.inputmode)}
                                aria-describedby="help-text"
                                @change=${this.handleChange}
                                @input=${this.handleInput}
                                @invalid=${this.handleInvalid}
                                @keydown=${this.handleKeyDown}
                                @focus=${this.handleFocus}
                                @blur=${this.handleBlur}
                        />
                        ${
                                hasClearIcon
                                ? html`
                                    <button
                                            part="clear-button"
                                            class="input__clear"
                                            type="button"
                                            aria-label=${this.localize.term('clearEntry')}
                                            @click=${this.handleClearClick}
                                            tabindex="-1"
                                    >
                                        <slot name="clear-icon">
                                            <sl-icon name="x-circle-fill" library="system"></sl-icon>
                                        </slot>
                                    </button>
                                `
                                : ''
                        }
                        ${
                                this.togglePassword && !this.disabled
                                ? html`
                                    <button
                                            part="password-toggle-button"
                                            class="input__password-toggle"
                                            type="button"
                                            aria-label=${this.localize.term(this.isPasswordVisible ? 'hidePassword' : 'showPassword')}
                                            @click=${this.handlePasswordToggle}
                                            tabindex="-1"
                                    >
                                        ${this.isPasswordVisible
                                          ? html`
                                                    <slot name="show-password-icon">
                                                        <sl-icon name="eye-slash" library="system"></sl-icon>
                                                    </slot>
                                                `
                                          : html`
                                                    <slot name="hide-password-icon">
                                                        <sl-icon name="eye" library="system"></sl-icon>
                                                    </slot>
                                                `}
                                    </button>
                                `
                                : ''
                        }
                        <slot name="suffix" part="suffix" class="input__suffix"></slot>
                    </div>
                </div>
                <slot
                        name="help-text"
                        part="form-control-help-text"
                        id="help-text"
                        class="form-control__help-text"
                        aria-hidden=${hasHelpText ? 'false' : 'true'}
                >
                    ${this.helpText}
                </slot>
            </div>
            </div>
		`;
	}

	handleFocus(e : FocusEvent)
	{
		if(!this.readonly)
		{
			this.shadowRoot.querySelector("input").removeAttribute("readonly");
		}
		super.handleFocus(e);
	}

}
// @ts-ignore TypeScript is not recognizing that this is a LitElement
customElements.define("et2-password", Et2Password);