// retoor <retoor@molodetz.nl>

export class ListNav {
    constructor(input, container, options) {
        this.input = input;
        this.container = container;
        this.itemSelector = options.itemSelector;
        this.activeClass = options.activeClass || "active";
        this.firstDefault = options.firstDefault !== false;
        this.chooseOnTab = options.chooseOnTab !== false;
        this.onChoose = options.onChoose;
        this.onEscape = options.onEscape || null;
        this.isOpen = options.isOpen || (() => this.items().length > 0);
        this.index = -1;

        if (!this.container.id) {
            this.container.id = `listnav-${Math.random().toString(36).slice(2)}`;
        }
        this.input.setAttribute("role", "combobox");
        this.input.setAttribute("aria-autocomplete", "list");
        this.input.setAttribute("aria-controls", this.container.id);
        this.input.setAttribute("aria-expanded", "false");

        this.input.addEventListener("keydown", (e) => this.onKeydown(e));
    }

    items() {
        return Array.from(this.container.querySelectorAll(this.itemSelector));
    }

    opened() {
        const items = this.items();
        items.forEach((el, i) => {
            if (!el.id) el.id = `${this.container.id}-opt-${i}`;
            el.setAttribute("role", "option");
            el.setAttribute("aria-selected", "false");
        });
        this.input.setAttribute("aria-expanded", items.length ? "true" : "false");
        this.setActive(this.firstDefault && items.length ? 0 : -1);
    }

    closed() {
        this.index = -1;
        this.input.setAttribute("aria-expanded", "false");
        this.input.removeAttribute("aria-activedescendant");
    }

    setActive(index) {
        const items = this.items();
        this.index = index;
        items.forEach((el, i) => {
            const on = i === index;
            el.classList.toggle(this.activeClass, on);
            el.setAttribute("aria-selected", on ? "true" : "false");
            if (on) {
                this.input.setAttribute("aria-activedescendant", el.id);
                el.scrollIntoView({ block: "nearest" });
            }
        });
        if (index < 0) this.input.removeAttribute("aria-activedescendant");
    }

    target() {
        const items = this.items();
        if (this.index >= 0 && items[this.index]) return items[this.index];
        return this.firstDefault ? items[0] || null : null;
    }

    choose(item) {
        if (item) this.onChoose(item);
    }

    onKeydown(e) {
        const items = this.items();
        if (!items.length || !this.isOpen()) return;
        const last = items.length - 1;

        if (e.key === "ArrowDown") {
            e.preventDefault();
            this.setActive(this.index < last ? this.index + 1 : 0);
        } else if (e.key === "ArrowUp") {
            e.preventDefault();
            this.setActive(this.index > 0 ? this.index - 1 : last);
        } else if (e.key === "Home") {
            e.preventDefault();
            this.setActive(0);
        } else if (e.key === "End") {
            e.preventDefault();
            this.setActive(last);
        } else if (e.key === "Enter") {
            const item = this.target();
            if (item) {
                e.preventDefault();
                this.choose(item);
            }
        } else if (e.key === "Tab" && this.chooseOnTab) {
            const item = this.target();
            if (item && !e.shiftKey) {
                e.preventDefault();
                this.choose(item);
            }
        } else if (e.key === "Escape") {
            e.preventDefault();
            if (this.onEscape) this.onEscape();
        }
    }
}
