// #region Imports
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Themeable } from '../../../Controls/Behaviors/Themeable';
import { ContentAlignable } from '../../Behaviors/ContentAlignable';
import { Disableable } from '../../Behaviors/Disableable';
import { Labelable } from '../../Behaviors/Labelable';
import { Attribute } from '../../Decorators/AttributeDecorator';
import { Component } from '../../Decorators/ComponentDecorator';
import { Property } from '../../Decorators/PropertyDecorator';
import { Watch } from '../../Decorators/WatchDecorator';
import { HorizontalAlignment } from '../../Types/HorizontalAlignment';
import { VerticalAlignment } from '../../Types/VerticalAlignment';
import { CustomFormElement } from '../Abstracts/CustomFormElement';
import { TextBoxElement } from '../Inputs/TextBox/TextBoxElement';
import { StackElement } from '../Layouts/Stack/StackElement';
import { ErrorElement } from '../Primitives/Error/ErrorElement';
import { HintElement } from '../Primitives/Hint/HintElement';
import { TextElement } from '../Primitives/Text/TextElement';
import { TooltipElement } from '../Primitives/Tooltip/TooltipElement';
import { formFieldElementTemplate } from './FormFieldElementTemplate';
import { formFieldElementJoyStyle } from './Themes/FormFieldElement.Joy';
import { formFieldElementMemphisStyle } from './Themes/FormFieldElement.Memphis';
import { toErros, toValidation } from './Utils';
import { formFieldElementCosmopolitanStyle } from './Themes/FormFieldElement.Cosmopolitan';
// #endregion
/**
 * Form Field - A component that represents a form field with associated label, hints, and validation messages.
 *
 * @description
 * The FormFieldElement component is designed to handle the layout and presentation of form fields in a user-friendly manner. It includes slots for displaying labels, error messages, hints, and additional information. This component integrates with various other elements to provide a complete form field experience.
 *
 * @remarks
 * Actual limitations:
 * - The first child of the form field must be the control element.
 *
 * @name Form Field
 * @element mosaik-form-field
 * @category Forms
 *
 * @slot - The default slot for the form field control.
 * @slot info - Slot for additional information or helper text.
 *
 * @csspart label - The part representing the label associated with the form field.
 * @csspart detail - The part representing detailed information or additional text related to the form field.
 * @csspart error - The part representing error messages related to the form field.
 * @csspart hint - The part representing hints or suggestions for the form field.
 *
 * @cssprop {unknown} --cell-padding-top - The padding at the top of the cell.
 * @cssprop {unknown} --cell-padding-right - The padding at the right of the cell.
 * @cssprop {unknown} --cell-padding-bottom - The padding at the bottom of the cell.
 * @cssprop {unknown} --cell-padding-left - The padding at the left of the cell.
 * @cssprop {unknown} --cell-gap - The gap between cells.
 *
 * @dependency {StackElement} - The Stack element for layout management.
 * @dependency {ErrorElement} - The Error element for displaying error messages.
 * @dependency {HintElement} - The Hint element for providing hints to the user.
 * @dependency {TextElement} - The Text element for displaying textual information.
 * @dependency {TextBoxElement} - The Text Box element for user input.
 * @dependency {TooltipElement} - The Tooltip element for additional contextual information.
 *
 * @example
 * ```html
 * <mosaik-form-field>
 *   <label for="username">Username</label>
 *   <text-box-element id="username"></text-box-element>
 *   <hint-element>Enter your desired username</hint-element>
 *   <error-element>Username is required</error-element>
 * </mosaik-form-field>
 * ```
 *
 * @public
 */
let FormFieldElement = class FormFieldElement extends Themeable(Disableable(Labelable(ContentAlignable(CustomFormElement)))) {
    // #region Fields
    _onChange;
    _onReset;
    _onTouched;
    _validatorInstances = [];
    _error;
    _hint;
    _info;
    _required;
    _validators;
    _accessor;
    _validationMessage = '';
    _control;
    _controlValue;
    _accessorInstance;
    _invalid;
    _name;
    _isTouched;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this.verticalContentAlignment = VerticalAlignment.Stretch;
        this.horizontalContentAlignment = HorizontalAlignment.Stretch;
        this._error = '';
        this._hint = '';
        this._info = '';
        this._validators = [];
        this._accessor = null;
        this._validationMessage = '';
        this._required = false;
        this._control = null;
        this._controlValue = null;
        this._accessorInstance = null;
        this._validatorInstances = [];
        this._invalid = false;
        this._name = '';
        this._isTouched = false;
        this._onChange = (value) => {
            this._controlValue = value;
            if (this._isTouched) {
                this.invalidate();
            }
        };
        this._onReset = () => {
            this.error = '';
            this.validationMessage = '';
        };
        this._onTouched = () => {
            this._isTouched = true;
            this.invalidate();
        };
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-form-field';
    }
    /**
     * Gets or sets the `error` property.
     *
     * @public
     * @attr
     */
    get error() {
        return this._error;
    }
    set error(value) {
        if (this._error !== value) {
            this._error = value;
            this.requestUpdate('error');
        }
    }
    /**
     * Gets or sets the `hint` property.
     *
     * @public
     * @attr
     */
    get hint() {
        return this._hint;
    }
    set hint(value) {
        if (this._hint !== value) {
            this._hint = value;
            this.requestUpdate('hint');
        }
    }
    /**
     * Gets or sets the `info` property.
     *
     * @public
     * @attr
     */
    get info() {
        return this._info;
    }
    set info(value) {
        if (this._info !== value) {
            this._info = value;
            this.requestUpdate('info');
        }
    }
    /**
     * @public
     * @readonly
     */
    get validators() {
        return this._validators;
    }
    set validators(value) {
        if (this._validators !== value) {
            this._validators = value;
            this.requestUpdate('validators');
        }
    }
    /**
     * Gets or sets the `accessor` property.
     *
     * @public
     */
    get accessor() {
        return this._accessor;
    }
    set accessor(value) {
        if (this._accessor !== value) {
            this._accessor = value;
            this.requestUpdate('accessor');
        }
    }
    /**
     * Gets or sets the `required` property.
     *
     * @public
     * @attr
     */
    get required() {
        return this._required;
    }
    set required(value) {
        if (this._required !== value) {
            this._required = value;
            this.requestUpdate('required');
        }
    }
    /**
     * Gets or sets the `validationMessage` property.
     *
     * @public
     * @override
     */
    get validationMessage() {
        return this._validationMessage = super.validationMessage;
    }
    set validationMessage(value) {
        if (this._validationMessage !== value) {
            this._validationMessage = value;
            this.requestUpdate('validationMessage');
        }
    }
    /**
     * Gets or sets the `invalid` property.
     *
     * @public
     * @attr
     */
    get invalid() {
        return this._invalid;
    }
    set invalid(value) {
        if (this._invalid !== value) {
            this._invalid = value;
            this.requestUpdate('invalid');
        }
    }
    /**
     * Gets or sets the `name` property.
     *
     * @public
     * @attr
     */
    get name() {
        return super.name;
    }
    set name(value) {
        if (this._name !== value) {
            this._name = value;
            this.requestUpdate('name');
        }
    }
    // #endregion
    // #region Methods
    /**
     * @public
     * @override
     */
    checkValidity() {
        this.invalidate();
        const isValid = !this.invalid && super.checkValidity();
        return isValid;
    }
    /**
     * @public
     * @override
     */
    reportValidity() {
        this.invalidate();
        const isValid = !this.invalid && super.reportValidity();
        return isValid;
    }
    /**
     * @public
     */
    formDisabledCallback(disabled) {
        this._accessorInstance?.setDisabled?.(disabled);
    }
    /**
     * @public
     */
    formResetCallback() {
        this._accessorInstance?.resetValue();
    }
    /**
     * @public
     */
    formStateRestoreCallback(state, reason) {
        this._accessorInstance?.writeValue(state);
    }
    /**
     * @public
     * @override
     */
    getFormValue() {
        return this._controlValue;
    }
    /**
     * @internal
     * @template
     */
    onSlotChange(event) {
        const slot = event.target;
        this._control = slot.assignedElements().at(0);
        void this.updateComplete.then(() => {
            this._accessorInstance?.registerOnChange(this._onChange);
            this._accessorInstance?.registerOnReset(this._onReset);
            this._accessorInstance?.registerOnTouched(this._onTouched);
            this._validatorInstances.forEach((x) => {
                x.registerOnValidatorChange?.(this._onChange);
            });
        });
    }
    /**
     * @protected
     */
    onAccessorPropertyChange(prev, next) {
        void this.updateComplete.then(() => {
            // If prev has a value means that accessor is already initialized.
            // No other checks are needed.
            if (prev) {
                if (this._accessorInstance) {
                    this.removeController(this._accessorInstance);
                }
            }
            if (next) {
                if (this._control) {
                    this._accessorInstance = next(this._control);
                    this.addController(this._accessorInstance);
                }
            }
        });
    }
    /**
     * @protected
     */
    onValidatorsPropertyChange(prev, next) {
        void this.updateComplete.then(() => {
            // If prev has a value means that accessor is already initialized.
            // No other checks are needed.
            if (prev) {
                for (const instance of this._validatorInstances) {
                    this.removeController(instance);
                }
            }
            if (next) {
                if (this._control) {
                    for (const fn of next) {
                        const instance = fn([this._control]);
                        this._validatorInstances.push(instance);
                        this.addController(instance);
                    }
                }
            }
        });
    }
    /**
     * @protected
     */
    onDisabledPropertyChange(_prev, next) {
        this._accessorInstance?.setDisabled?.(next ?? false);
    }
    /**
     * @private
     */
    invalidate() {
        if (!this._control) {
            return;
        }
        const validity = toErros(this._validatorInstances.map((instance) => instance.validate()));
        const hasAnyError = Object.entries(validity).some(([, v]) => Array.isArray(v) ? v[0] : v);
        const firstError = Object.entries(validity).find(([, v]) => Array.isArray(v) ? v[0] : v);
        const errorMessage = hasAnyError ? firstError?.[1][1] : undefined;
        if (firstError && hasAnyError) {
            this.internals.setValidity(toValidation(validity), errorMessage ?? undefined);
            this.invalid = true;
        }
        else {
            // If there are no custom errors, we need to reset the validity state.
            // We have to set all the validity state properties to false.
            this.internals.setValidity({
                badInput: false,
                customError: false,
                patternMismatch: false,
                rangeOverflow: false,
                rangeUnderflow: false,
                stepMismatch: false,
                tooLong: false,
                tooShort: false,
                typeMismatch: false,
                valueMissing: false
            });
            this.invalid = false;
        }
        this.validationMessage = this.internals.validationMessage;
        // This.error = this.internals.validationMessage;
    }
};
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FormFieldElement.prototype, "error", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FormFieldElement.prototype, "hint", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FormFieldElement.prototype, "info", null);
__decorate([
    Property({ type: (Array) }),
    __metadata("design:type", Array),
    __metadata("design:paramtypes", [Array])
], FormFieldElement.prototype, "validators", null);
__decorate([
    Property({ type: Object }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], FormFieldElement.prototype, "accessor", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], FormFieldElement.prototype, "required", null);
__decorate([
    Property({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FormFieldElement.prototype, "validationMessage", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], FormFieldElement.prototype, "invalid", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FormFieldElement.prototype, "name", null);
__decorate([
    Watch('accessor'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function, Function]),
    __metadata("design:returntype", void 0)
], FormFieldElement.prototype, "onAccessorPropertyChange", null);
__decorate([
    Watch('validators'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array, Array]),
    __metadata("design:returntype", void 0)
], FormFieldElement.prototype, "onValidatorsPropertyChange", null);
__decorate([
    Watch('disabled'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Boolean, Boolean]),
    __metadata("design:returntype", void 0)
], FormFieldElement.prototype, "onDisabledPropertyChange", null);
FormFieldElement = __decorate([
    Component({
        selector: 'mosaik-form-field',
        template: formFieldElementTemplate,
        themes: {
            joy: formFieldElementJoyStyle,
            memphis: formFieldElementMemphisStyle,
            cosmopolitan: formFieldElementCosmopolitanStyle
        },
        imports: [
            StackElement,
            ErrorElement,
            HintElement,
            TextElement,
            TextBoxElement,
            TooltipElement
        ]
    }),
    __metadata("design:paramtypes", [])
], FormFieldElement);
export { FormFieldElement };
//# sourceMappingURL=FormFieldElement.js.map