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;
};
import { MelodicComponent } from '@melodicdev/core';
import { computePosition, offset, flip, shift } from '../../../utils/positioning/index.js';
import { autocompleteTemplate } from './autocomplete.template.js';
import { autocompleteStyles } from './autocomplete.styles.js';
/**
 * ml-autocomplete - Typeahead/autocomplete form component
 *
 * Provides a text input with dropdown suggestions. Supports static option lists
 * or async search via a promise-returning function.
 *
 * @example
 * ```html
 * <ml-autocomplete
 *   label="Search users"
 *   placeholder="Type to search..."
 *   .options=${userOptions}
 * ></ml-autocomplete>
 *
 * <ml-autocomplete
 *   label="Search"
 *   .searchFn=${async (query) => fetch(`/api/search?q=${query}`).then(r => r.json())}
 *   .debounce=${300}
 * ></ml-autocomplete>
 * ```
 *
 * @fires ml:change - Emitted when selection changes
 * @fires ml:search - Emitted when search query changes
 * @fires ml:open - Emitted when dropdown opens
 * @fires ml:close - Emitted when dropdown closes
 */
let AutocompleteComponent = class AutocompleteComponent {
    constructor() {
        /** Label text */
        this.label = '';
        /** Placeholder text */
        this.placeholder = 'Search';
        /** Hint text shown below input */
        this.hint = '';
        /** Error message (shows error state when set) */
        this.error = '';
        /** Component size */
        this.size = 'md';
        /** Disable the component */
        this.disabled = false;
        /** Mark as required */
        this.required = false;
        /** Enable multi-select mode */
        this.multiple = false;
        /** Currently selected value (single mode) */
        this.value = '';
        /** Currently selected values (multi mode) */
        this.values = [];
        /** Static options list */
        this.options = [];
        /** Async search function */
        this.searchFn = null;
        /** Debounce ms for async search */
        this.debounce = 300;
        /** Min chars before searching (0 = show on focus) */
        this.minChars = 0;
        /** Show search icon */
        this.showIcon = true;
        /** Current search query */
        this.search = '';
        /** Whether dropdown is open */
        this.isOpen = false;
        /** Currently focused option index */
        this.focusedIndex = -1;
        /** Loading state for async search */
        this.loading = false;
        /** Resolved async results */
        this.asyncOptions = [];
        this._handleKeyDown = this.onKeyDown.bind(this);
        this._handleDocumentClick = this.onDocumentClick.bind(this);
        this._handleScroll = null;
        this._debounceTimer = null;
        this._syncingValues = false;
        /** Open the dropdown */
        this.open = () => {
            if (this.disabled || this.isOpen)
                return;
            if (this.minChars > 0 && this.search.length < this.minChars)
                return;
            const dropdownEl = this.getDropdownEl();
            if (!dropdownEl)
                return;
            dropdownEl.showPopover();
            this.isOpen = true;
            this.focusedIndex = this.findFirstEnabledIndex();
            this.positionDropdown();
            this.addScrollListener();
            this.addDocumentClickListener();
            this.elementRef.dispatchEvent(new CustomEvent('ml:open', { bubbles: true, composed: true }));
        };
        /** Close the dropdown */
        this.close = () => {
            if (!this.isOpen)
                return;
            this.getDropdownEl()?.hidePopover();
            this.isOpen = false;
            this.focusedIndex = -1;
            this.removeScrollListener();
            this.removeDocumentClickListener();
            this.elementRef.dispatchEvent(new CustomEvent('ml:close', { bubbles: true, composed: true }));
        };
        /** Select an option */
        this.selectOption = (option) => {
            if (option.disabled)
                return;
            if (this.multiple) {
                this.toggleOption(option);
                return;
            }
            this.value = option.value;
            this.search = '';
            this.close();
            this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
                bubbles: true,
                composed: true,
                detail: { value: this.value, option }
            }));
        };
        /** Handle click on option */
        this.handleOptionClick = (event, option) => {
            event.stopPropagation();
            this.selectOption(option);
        };
        /** Handle input changes */
        this.handleInput = (event) => {
            if (this.disabled)
                return;
            const target = event.target;
            this.search = target.value;
            // In single mode, clear the current value when the user types
            if (!this.multiple && this.value) {
                this.value = '';
                this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
                    bubbles: true,
                    composed: true,
                    detail: { value: '', option: null }
                }));
            }
            this.elementRef.dispatchEvent(new CustomEvent('ml:search', {
                bubbles: true,
                composed: true,
                detail: { query: this.search }
            }));
            if (this.minChars > 0 && this.search.length < this.minChars) {
                this.close();
                return;
            }
            this.focusedIndex = this.findFirstEnabledIndex();
            if (!this.isOpen) {
                this.open();
            }
            if (this.searchFn) {
                this.debouncedSearch(this.search);
            }
        };
        /** Handle input focus */
        this.handleFocus = () => {
            if (this.disabled)
                return;
            if (this.minChars === 0) {
                if (this.searchFn && this.asyncOptions.length === 0) {
                    this.debouncedSearch('');
                }
                if (!this.isOpen) {
                    this.open();
                }
            }
        };
        /** Handle input click */
        this.handleInputClick = (event) => {
            event.stopPropagation();
            if (!this.isOpen && this.minChars === 0) {
                if (this.searchFn && this.asyncOptions.length === 0) {
                    this.debouncedSearch('');
                }
                this.open();
            }
        };
        /** Handle tag remove in multi mode */
        this.handleTagRemove = (event, value) => {
            event.stopPropagation();
            if (this.disabled)
                return;
            this.values = this.values.filter((item) => item !== value);
            this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
                bubbles: true,
                composed: true,
                detail: { values: [...this.values], options: this.selectedOptions }
            }));
        };
        /** Clear the current value (single mode) */
        this.handleClear = (event) => {
            event.stopPropagation();
            if (this.disabled)
                return;
            this.value = '';
            this.search = '';
            this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
                bubbles: true,
                composed: true,
                detail: { value: '', option: null }
            }));
            // Focus the input after clearing
            const input = this.elementRef.shadowRoot?.querySelector('.ml-autocomplete__input');
            input?.focus();
        };
    }
    onCreate() {
        this.elementRef.addEventListener('keydown', this._handleKeyDown);
    }
    onDestroy() {
        this.elementRef.removeEventListener('keydown', this._handleKeyDown);
        this.removeScrollListener();
        this.removeDocumentClickListener();
        if (this._debounceTimer) {
            clearTimeout(this._debounceTimer);
        }
    }
    onPropertyChange(name) {
        if (this._syncingValues)
            return;
        if (name === 'multiple') {
            if (this.multiple) {
                if (!this.values.length && this.value) {
                    this.updateValues([this.value]);
                }
                return;
            }
            if (this.values.length) {
                this.value = this.values[0] ?? '';
            }
            this.updateValues([]);
            this.search = '';
            return;
        }
        if (name === 'values') {
            const rawValues = this.values;
            let normalized = [];
            if (typeof rawValues === 'string') {
                normalized = rawValues
                    .split(',')
                    .map((v) => v.trim())
                    .filter((v) => v.length > 0);
            }
            else if (Array.isArray(rawValues)) {
                normalized = rawValues.filter((v) => typeof v === 'string');
            }
            normalized = Array.from(new Set(normalized));
            if (!this.areValuesEqual(this.values, normalized)) {
                this.updateValues(normalized);
            }
            if (!this.multiple) {
                this.value = normalized[0] ?? '';
                this.updateValues([]);
            }
            return;
        }
        if (name === 'value' && this.multiple) {
            if (this.value) {
                const nextValues = Array.from(new Set([...this.values, this.value]));
                if (!this.areValuesEqual(this.values, nextValues)) {
                    this.updateValues(nextValues);
                }
            }
        }
    }
    /** Get the selected option (single mode) */
    get selectedOption() {
        return this.allOptions.find((opt) => opt.value === this.value)
            ?? this.options.find((opt) => opt.value === this.value);
    }
    /** Get selected options (multi mode) */
    get selectedOptions() {
        if (!this.multiple) {
            return this.selectedOption ? [this.selectedOption] : [];
        }
        const found = this.allOptions.filter((opt) => this.values.includes(opt.value));
        const foundValues = new Set(found.map((opt) => opt.value));
        const fallback = this.options.filter((opt) => this.values.includes(opt.value) && !foundValues.has(opt.value));
        return [...found, ...fallback];
    }
    /** All available options (static or async) */
    get allOptions() {
        return this.searchFn ? this.asyncOptions : this.options;
    }
    /** Filtered options for display */
    get filteredOptions() {
        if (this.searchFn) {
            return this.asyncOptions;
        }
        const query = this.search.trim().toLowerCase();
        if (!query)
            return this.options;
        return this.options.filter((option) => {
            const labelMatch = option.label.toLowerCase().includes(query);
            const valueMatch = option.value.toLowerCase().includes(query);
            const subtitleMatch = option.subtitle?.toLowerCase().includes(query) ?? false;
            return labelMatch || valueMatch || subtitleMatch;
        });
    }
    get hasValue() {
        return this.multiple ? this.values.length > 0 : !!this.value;
    }
    /** Display text for the input in single mode */
    get displayText() {
        if (this.multiple)
            return '';
        return this.selectedOption?.label || '';
    }
    toggleOption(option) {
        const exists = this.values.includes(option.value);
        this.values = exists ? this.values.filter((v) => v !== option.value) : [...this.values, option.value];
        this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
            bubbles: true,
            composed: true,
            detail: { values: [...this.values], options: this.selectedOptions, option }
        }));
        // Clear search and refocus input
        this.search = '';
        const input = this.elementRef.shadowRoot?.querySelector('.ml-autocomplete__input');
        input?.focus();
    }
    debouncedSearch(query) {
        if (this._debounceTimer) {
            clearTimeout(this._debounceTimer);
        }
        this._debounceTimer = setTimeout(() => {
            this.executeSearch(query);
        }, this.debounce);
    }
    async executeSearch(query) {
        if (!this.searchFn)
            return;
        this.loading = true;
        try {
            this.asyncOptions = await this.searchFn(query);
            this.focusedIndex = this.findFirstEnabledIndex();
        }
        finally {
            this.loading = false;
        }
    }
    /** Close dropdown on clicks outside the component */
    onDocumentClick(event) {
        const path = event.composedPath();
        if (!path.includes(this.elementRef)) {
            this.close();
        }
    }
    addDocumentClickListener() {
        document.addEventListener('click', this._handleDocumentClick, true);
    }
    removeDocumentClickListener() {
        document.removeEventListener('click', this._handleDocumentClick, true);
    }
    positionDropdown() {
        const triggerEl = this.elementRef.shadowRoot?.querySelector('.ml-autocomplete__trigger');
        const dropdownEl = this.getDropdownEl();
        if (!triggerEl || !dropdownEl)
            return;
        dropdownEl.style.width = `${triggerEl.offsetWidth}px`;
        const { x, y } = computePosition(triggerEl, dropdownEl, {
            placement: 'bottom-start',
            middleware: [offset(4), flip(), shift({ padding: 8 })]
        });
        dropdownEl.style.left = `${x}px`;
        dropdownEl.style.top = `${y}px`;
    }
    addScrollListener() {
        this._handleScroll = (event) => {
            const dropdownEl = this.getDropdownEl();
            if (dropdownEl?.contains(event.target))
                return;
            this.close();
        };
        window.addEventListener('scroll', this._handleScroll, true);
    }
    removeScrollListener() {
        if (this._handleScroll) {
            window.removeEventListener('scroll', this._handleScroll, true);
            this._handleScroll = null;
        }
    }
    getDropdownEl() {
        return this.elementRef.shadowRoot?.querySelector('.ml-autocomplete__dropdown');
    }
    onKeyDown(event) {
        if (this.disabled)
            return;
        switch (event.key) {
            case 'Enter':
                if (this.isOpen && this.focusedIndex >= 0) {
                    event.preventDefault();
                    const option = this.filteredOptions[this.focusedIndex];
                    if (option && !option.disabled) {
                        this.selectOption(option);
                    }
                }
                break;
            case 'Escape':
                event.preventDefault();
                this.close();
                break;
            case 'ArrowDown':
                event.preventDefault();
                if (!this.isOpen) {
                    this.open();
                }
                else {
                    this.focusNextOption();
                }
                break;
            case 'ArrowUp':
                event.preventDefault();
                if (this.isOpen) {
                    this.focusPreviousOption();
                }
                break;
            case 'Backspace':
                if (this.multiple && this.search === '' && this.values.length > 0) {
                    const lastValue = this.values[this.values.length - 1];
                    this.values = this.values.slice(0, -1);
                    this.elementRef.dispatchEvent(new CustomEvent('ml:change', {
                        bubbles: true,
                        composed: true,
                        detail: { values: [...this.values], options: this.selectedOptions, removedValue: lastValue }
                    }));
                }
                break;
            case 'Tab':
                this.close();
                break;
            default:
                break;
        }
    }
    focusNextOption() {
        const options = this.filteredOptions;
        let index = this.focusedIndex + 1;
        while (index < options.length) {
            if (!options[index].disabled) {
                this.focusedIndex = index;
                return;
            }
            index++;
        }
    }
    focusPreviousOption() {
        const options = this.filteredOptions;
        let index = this.focusedIndex - 1;
        while (index >= 0) {
            if (!options[index].disabled) {
                this.focusedIndex = index;
                return;
            }
            index--;
        }
    }
    findFirstEnabledIndex() {
        return this.filteredOptions.findIndex((opt) => !opt.disabled);
    }
    updateValues(values) {
        this._syncingValues = true;
        this.values = values;
        this._syncingValues = false;
    }
    areValuesEqual(left, right) {
        if (left.length !== right.length)
            return false;
        for (let i = 0; i < left.length; i++) {
            if (left[i] !== right[i])
                return false;
        }
        return true;
    }
};
AutocompleteComponent = __decorate([
    MelodicComponent({
        selector: 'ml-autocomplete',
        template: autocompleteTemplate,
        styles: autocompleteStyles,
        attributes: ['label', 'placeholder', 'hint', 'error', 'size', 'disabled', 'required', 'value', 'multiple']
    })
], AutocompleteComponent);
export { AutocompleteComponent };
