import { buildCustomEvent } from "./event-utils.js";
import { defer } from "./utils.js";
class AriaLiveRegionController {
    getAriaLiveElement() {
        const event = buildCustomEvent('atomic/accessibility/findAriaLive', {});
        document.dispatchEvent(event);
        const { element } = event.detail;
        return element;
    }
    dispatchMessage(message) {
        this.getAriaLiveElement()?.updateMessage(this.regionName, message, this.assertive);
    }
    set message(msg) {
        this.dispatchMessage(msg);
    }
    hostUpdate() {
        this.getAriaLiveElement()?.registerRegion(this.regionName, this.assertive);
    }
    constructor(host, regionName, assertive = false){
        this.host = host;
        this.regionName = regionName;
        this.assertive = assertive;
        this.host.addController(this);
    }
}
class FocusTargetController {
    registerFocusCallback(callback) {
        this.publicOnFocusCallbacks.push(callback);
    }
    clearFocusCallbacks() {
        this.internalOnFocusCallback?.();
        while(this.publicOnFocusCallbacks.length)this.publicOnFocusCallbacks.pop()?.();
    }
    async setTarget(el) {
        if (!el) return;
        this.element = el;
        if (this.doFocusOnNextTarget) {
            this.doFocusOnNextTarget = false;
            await this.focus();
        }
    }
    async focus() {
        await defer();
        this.element?.focus();
        this.clearFocusCallbacks();
    }
    focusAfterSearch() {
        this.lastSearchId = this.bindings.store.getUniqueIDFromEngine(this.bindings.engine);
        this.doFocusAfterSearch = true;
        return new Promise((resolve)=>{
            this.internalOnFocusCallback = resolve;
        });
    }
    focusOnNextTarget() {
        this.doFocusOnNextTarget = true;
        return new Promise((resolve)=>{
            this.internalOnFocusCallback = resolve;
        });
    }
    disableForCurrentSearch() {
        if (this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId) this.doFocusAfterSearch = false;
    }
    async hostUpdated() {
        if (this.doFocusAfterSearch && this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId) {
            this.doFocusAfterSearch = false;
            if (this.element) {
                const el = this.element;
                await defer().then(()=>{
                    el.focus();
                    this.clearFocusCallbacks();
                });
            }
        }
    }
    constructor(host, bindings){
        this.host = host;
        this.publicOnFocusCallbacks = [];
        this.doFocusAfterSearch = false;
        this.doFocusOnNextTarget = false;
        this.bindings = bindings;
        this.host.addController(this);
    }
}
function isFocusable(element) {
    if ('-1' === element.getAttribute('tabindex')) return false;
    if (element.hasAttribute('tabindex')) return true;
    if ('true' === element.getAttribute('contentEditable')) return true;
    switch(element.tagName){
        case 'A':
        case 'AREA':
            return element.hasAttribute('href');
        case 'INPUT':
        case 'SELECT':
        case 'TEXTAREA':
        case 'BUTTON':
            return !element.hasAttribute('disabled');
        case 'IFRAME':
            return true;
        default:
            return false;
    }
}
function* getFocusableDescendants(element) {
    if (isFocusable(element)) yield element;
    let children = Array.from(element.children);
    if (element instanceof HTMLSlotElement) children = element.assignedElements();
    else if (element.shadowRoot) children.push(...Array.from(element.shadowRoot.children));
    for (const child of children)yield* getFocusableDescendants(child);
}
function getFirstFocusableDescendant(element) {
    return getFocusableDescendants(element).next().value ?? null;
}
export { AriaLiveRegionController, FocusTargetController, getFirstFocusableDescendant };
