import { html } from 'lit';
export const formCollections = new WeakMap();
const reportValidityOverloads = new WeakMap();
const userInteractedControls = new WeakMap();
const interactions = new WeakMap();
export class FormControlController {
    constructor(host, options) {
        this.handleFormData = (event) => {
            const disabled = this.options.disabled(this.host);
            const name = this.options.name(this.host);
            const value = this.options.value(this.host);
            const isButton = this.host.tagName.toLowerCase() === 'sd-6-22-0-button';
            if (!disabled && !isButton && typeof name === 'string' && name.length > 0 && typeof value !== 'undefined') {
                if (Array.isArray(value)) {
                    value.forEach(val => {
                        event.formData.append(name, val.toString());
                    });
                }
                else {
                    event.formData.append(name, value.toString());
                }
            }
        };
        this.handleFormSubmit = (event) => {
            const disabled = this.options.disabled(this.host);
            const reportValidity = this.options.reportValidity;
            if (this.form && !this.form.noValidate) {
                formCollections.get(this.form)?.forEach(control => {
                    this.setUserInteracted(control, true);
                });
            }
            if (this.form && !this.form.noValidate && !disabled && !reportValidity(this.host)) {
                event.preventDefault();
                event.stopImmediatePropagation();
                const invalidElements = this.form?.querySelectorAll('[data-invalid]');
                const sdRadioGroups = Array.from(invalidElements).filter(element => element.tagName.toLowerCase() === 'sd-6-22-0-radio-group');
                sdRadioGroups.forEach(radioGroup => {
                    radioGroup.shadowRoot?.querySelector('input')?.dispatchEvent(new Event('invalid'));
                });
                if (invalidElements?.length)
                    invalidElements[0].focus();
            }
        };
        this.handleFormReset = () => {
            this.options.setValue(this.host, this.options.defaultValue(this.host));
            this.setUserInteracted(this.host, false);
            interactions.set(this.host, []);
        };
        this.handleInteraction = (event) => {
            const emittedEvents = interactions.get(this.host);
            if (!emittedEvents.includes(event.type)) {
                emittedEvents.push(event.type);
            }
            if (emittedEvents.length === this.options.assumeInteractionOn.length) {
                this.setUserInteracted(this.host, true);
            }
        };
        this.fakeUserInteraction = () => {
            this.setUserInteracted(this.host, true);
        };
        (this.host = host).addController(this);
        this.options = {
            form: input => {
                if (input.hasAttribute('form') && input.getAttribute('form') !== '') {
                    const root = input.getRootNode();
                    const formId = input.getAttribute('form');
                    if (formId) {
                        return root.getElementById(formId);
                    }
                }
                return input.closest('form');
            },
            name: input => input.name,
            value: input => input.value,
            defaultValue: input => input.defaultValue,
            disabled: input => input.disabled ?? false,
            reportValidity: input => (typeof input.reportValidity === 'function' ? input.reportValidity() : true),
            setValue: (input, value) => (input.value = value),
            assumeInteractionOn: ['sd-input'],
            ...options
        };
    }
    hostConnected() {
        const form = this.options.form(this.host);
        if (form) {
            this.attachForm(form);
        }
        interactions.set(this.host, []);
        this.options.assumeInteractionOn.forEach(event => {
            this.host.addEventListener(event, this.handleInteraction);
        });
    }
    hostDisconnected() {
        this.detachForm();
        interactions.delete(this.host);
        this.options.assumeInteractionOn.forEach(event => {
            this.host.removeEventListener(event, this.handleInteraction);
        });
    }
    hostUpdated() {
        const form = this.options.form(this.host);
        if (!form) {
            this.detachForm();
        }
        if (form && this.form !== form) {
            this.detachForm();
            this.attachForm(form);
        }
        if (this.host.hasUpdated) {
            this.setValidity(this.host?.validity.valid);
            this.updateValidityStyle();
        }
    }
    attachForm(form) {
        if (form) {
            this.form = form;
            if (formCollections.has(this.form)) {
                formCollections.get(this.form).add(this.host);
            }
            else {
                formCollections.set(this.form, new Set([this.host]));
            }
            this.form.addEventListener('formdata', this.handleFormData);
            this.form.addEventListener('submit', this.handleFormSubmit);
            this.form.addEventListener('reset', this.handleFormReset);
            if (!reportValidityOverloads.has(this.form)) {
                reportValidityOverloads.set(this.form, this.form.reportValidity);
                this.form.reportValidity = () => this.reportFormValidity();
            }
        }
        else {
            this.form = undefined;
        }
    }
    detachForm() {
        if (this.form) {
            formCollections.get(this.form)?.delete(this.host);
            this.form.removeEventListener('formdata', this.handleFormData);
            this.form.removeEventListener('submit', this.handleFormSubmit);
            this.form.removeEventListener('reset', this.handleFormReset);
            if (reportValidityOverloads.has(this.form)) {
                this.form.reportValidity = reportValidityOverloads.get(this.form);
                reportValidityOverloads.delete(this.form);
            }
        }
        this.form = undefined;
    }
    reportFormValidity() {
        if (this.form && !this.form.noValidate) {
            const elements = this.form.querySelectorAll('*');
            for (const element of elements) {
                if (typeof element.reportValidity === 'function') {
                    if (!element.reportValidity()) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    setUserInteracted(el, hasInteracted) {
        userInteractedControls.set(el, hasInteracted);
        el.requestUpdate();
    }
    doAction(type, invoker) {
        if (this.form) {
            const button = document.createElement('button');
            button.type = type;
            button.style.position = 'absolute';
            button.style.width = '0';
            button.style.height = '0';
            button.style.clipPath = 'inset(50%)';
            button.style.overflow = 'hidden';
            button.style.whiteSpace = 'nowrap';
            if (invoker) {
                button.name = invoker.name;
                button.value = invoker.value;
                ['formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget'].forEach(attr => {
                    if (invoker.hasAttribute(attr)) {
                        button.setAttribute(attr, invoker.getAttribute(attr));
                    }
                });
            }
            this.form.append(button);
            button.click();
            button.remove();
        }
    }
    updateValidityStyle() {
        if (this.host.hasAttribute('data-user-valid') && this.host.checkValidity()) {
            if (this.host.showValidStyle !== undefined)
                this.host.showValidStyle = true;
            this.host.showInvalidStyle = false;
        }
        else if (this.host.hasAttribute('data-user-invalid') && !this.host.checkValidity()) {
            if (this.host.showValidStyle !== undefined)
                this.host.showValidStyle = false;
            this.host.showInvalidStyle = true;
        }
        else {
            if (this.host.showValidStyle !== undefined)
                this.host.showValidStyle = false;
            this.host.showInvalidStyle = false;
        }
    }
    getForm() {
        return this.form ?? null;
    }
    renderInvalidMessage(size) {
        const sizeClass = {
            sm: 'text-sm',
            md: 'text-base',
            lg: 'text-base'
        }[size];
        return html `<div
      id="invalid-message"
      class="text-error text-left ${sizeClass}"
      part="invalid-message"
      aria-live="polite"
      ?hidden=${!this.host.showInvalidStyle}
    ></div>`;
    }
    reset(invoker) {
        this.doAction('reset', invoker);
    }
    submit(invoker) {
        this.doAction('submit', invoker);
    }
    setValidity(isValid) {
        const host = this.host;
        const hasInteracted = Boolean(userInteractedControls.get(host));
        const required = Boolean(host.required);
        if (this.form?.noValidate) {
            host.removeAttribute('data-required');
            host.removeAttribute('data-optional');
            host.removeAttribute('data-invalid');
            host.removeAttribute('data-valid');
            host.removeAttribute('data-user-invalid');
            host.removeAttribute('data-user-valid');
            host.removeAttribute('aria-invalid');
        }
        else {
            host.toggleAttribute('data-required', required);
            host.toggleAttribute('data-optional', !required);
            host.toggleAttribute('data-invalid', !isValid);
            host.toggleAttribute('data-valid', isValid);
            host.toggleAttribute('data-user-invalid', !isValid && hasInteracted);
            host.toggleAttribute('data-user-valid', isValid && hasInteracted);
            host.setAttribute('aria-invalid', (!isValid).toString());
        }
    }
    updateValidity() {
        const host = this.host;
        this.setValidity(host?.validity.valid);
    }
    emitInvalidEvent(originalInvalidEvent) {
        originalInvalidEvent?.preventDefault();
        const sdInvalidEvent = new CustomEvent('sd-invalid', {
            bubbles: false,
            composed: false,
            cancelable: true,
            detail: {}
        });
        if (!originalInvalidEvent) {
            sdInvalidEvent.preventDefault();
        }
    }
}
export const validValidityState = Object.freeze({
    badInput: false,
    customError: false,
    patternMismatch: false,
    rangeOverflow: false,
    rangeUnderflow: false,
    stepMismatch: false,
    tooLong: false,
    tooShort: false,
    typeMismatch: false,
    valid: true,
    valueMissing: false
});
export const valueMissingValidityState = Object.freeze({
    ...validValidityState,
    valid: false,
    valueMissing: true
});
export const customErrorValidityState = Object.freeze({
    ...validValidityState,
    valid: false,
    customError: true
});
