// retoor <retoor@molodetz.nl>

import { Http } from "./Http.js";

export function escapeHtml(value) {
    const span = document.createElement("span");
    span.textContent = value == null ? "" : String(value);
    return span.innerHTML;
}

export function wireBootToggle(form) {
    const select = form.elements.boot_language;
    const update = () => {
        const script = form.querySelector('[data-boot="script"]');
        if (script) script.style.display = select.value === "none" ? "none" : "";
    };
    select.addEventListener("change", update);
    update();
}

const SEARCH_DEBOUNCE_MS = 200;

export function wireSearch(form, kind, url, hiddenName, valueOf, labelOf) {
    const input = form.querySelector(`[data-search="${kind}"]`);
    const list = form.querySelector(`[data-suggest="${kind}"]`);
    const hidden = form.elements[hiddenName];
    if (!input || !list || !hidden) return;

    if (!list.id) list.id = `cm-suggest-${kind}`;
    list.setAttribute("role", "listbox");
    input.setAttribute("role", "combobox");
    input.setAttribute("aria-autocomplete", "list");
    input.setAttribute("aria-controls", list.id);
    input.setAttribute("aria-expanded", "false");

    let timer = null;
    let active = -1;

    const options = () => Array.from(list.querySelectorAll("li[data-value]"));

    const close = () => {
        list.innerHTML = "";
        active = -1;
        input.setAttribute("aria-expanded", "false");
        input.removeAttribute("aria-activedescendant");
    };

    const setActive = (index) => {
        const opts = options();
        active = index;
        opts.forEach((li, i) => {
            const on = i === index;
            li.classList.toggle("cm-suggest-active", on);
            li.setAttribute("aria-selected", on ? "true" : "false");
            if (on) {
                input.setAttribute("aria-activedescendant", li.id);
                li.scrollIntoView({ block: "nearest" });
            }
        });
        if (index < 0) input.removeAttribute("aria-activedescendant");
    };

    const choose = (item) => {
        if (!item) return;
        hidden.value = item.dataset.value;
        input.value = item.dataset.label;
        close();
    };

    const render = (results) => {
        list.innerHTML = results.map((r, i) =>
            `<li id="${list.id}-opt-${i}" role="option" aria-selected="false" `
            + `data-value="${escapeHtml(valueOf(r))}" data-label="${escapeHtml(labelOf(r))}">`
            + `${escapeHtml(labelOf(r))}</li>`
        ).join("");
        active = -1;
        input.setAttribute("aria-expanded", results.length ? "true" : "false");
        input.removeAttribute("aria-activedescendant");
    };

    input.addEventListener("input", () => {
        hidden.value = "";
        clearTimeout(timer);
        const q = input.value.trim();
        if (!q) { close(); return; }
        timer = setTimeout(async () => {
            try {
                const data = await Http.getJson(`${url}?q=${encodeURIComponent(q)}`);
                render(data.results || []);
            } catch (err) {
                close();
            }
        }, SEARCH_DEBOUNCE_MS);
    });

    input.addEventListener("keydown", (e) => {
        const opts = options();
        const count = opts.length;
        if (!count) return;
        const last = count - 1;
        switch (e.key) {
            case "ArrowDown":
                e.preventDefault();
                setActive(active < last ? active + 1 : 0);
                break;
            case "ArrowUp":
                e.preventDefault();
                setActive(active > 0 ? active - 1 : last);
                break;
            case "Home":
                e.preventDefault();
                setActive(0);
                break;
            case "End":
                e.preventDefault();
                setActive(last);
                break;
            case "Enter":
                e.preventDefault();
                choose(active >= 0 ? opts[active] : opts[0]);
                break;
            case "Escape":
                e.preventDefault();
                close();
                break;
            case "Tab":
                if (e.shiftKey) {
                    if (active > 0) { e.preventDefault(); setActive(active - 1); }
                    else close();
                } else if (active < last) {
                    e.preventDefault();
                    setActive(active + 1);
                } else {
                    choose(opts[active]);
                }
                break;
            default:
                break;
        }
    });

    list.addEventListener("mousedown", (e) => e.preventDefault());
    list.addEventListener("click", (e) => {
        const item = e.target.closest("li[data-value]");
        choose(item);
    });
    input.addEventListener("blur", () => {
        window.setTimeout(close, 150);
    });
}
