(() => {

function query(selector, root) {
        return (root || document).querySelector(selector);
}

function ready(callback) {
        if (document.readyState === "loading") {
                document.addEventListener("DOMContentLoaded", callback, { once: true });
        } else {
                callback();
        }
}

function createAutocomplete(input) {
        const form = input.form;
        const results = document.createElement("div");
        const list = document.createElement("ul");
        let requestTimer = null;
        let controller = null;
        let items = [];
        let activeIndex = -1;
        let blurTimer = null;

        if (!form) return;

        results.className = "ac_results";
        results.hidden = true;
        results.setAttribute("role", "listbox");
        results.id = `${input.id}_results`;
        list.setAttribute("role", "presentation");
        results.appendChild(list);
        input.setAttribute("autocomplete", "off");
        input.setAttribute("aria-autocomplete", "list");
        input.setAttribute("aria-controls", results.id);
        input.setAttribute("aria-expanded", "false");
        form.appendChild(results);

        function syncResultsWidth() {
                results.style.width = `${input.offsetWidth}px`;
        }

        function hideResults() {
                results.hidden = true;
                input.setAttribute("aria-expanded", "false");
                input.removeAttribute("aria-activedescendant");
                activeIndex = -1;
                items = [];
                list.innerHTML = "";
        }

        function setActive(index) {
                if (!items.length) return;
                activeIndex = (index + items.length) % items.length;
                items.forEach((item, itemIndex) => {
                        item.element.classList.toggle("ac_over", itemIndex === activeIndex);
                });
                input.setAttribute(
                        "aria-activedescendant",
                        items[activeIndex].element.id,
                );
        }

        function selectItem(item) {
                input.value = item.values[1];
                window.location.href = item.values[3];
        }

        function renderItems(lines) {
                syncResultsWidth();
                list.innerHTML = "";
                items = lines.map((line, index) => {
                        const values = line.split(",");
                        const element = document.createElement("li");
                        const label = document.createElement("span");
                        const namespace = document.createElement("small");

                        element.id = `${results.id}_item_${index}`;
                        element.setAttribute("role", "option");
                        element.className = index % 2 === 0 ? "ac_even" : "ac_odd";
                        label.textContent = values[0];
                        element.appendChild(label);

                        if (values[1] !== "") {
                                namespace.textContent = `(${values[1]})`;
                                element.appendChild(document.createTextNode(" "));
                                element.appendChild(namespace);
                        }

                        element.addEventListener("mouseenter", () => {
                                setActive(index);
                        });
                        element.addEventListener("mousedown", (event) => {
                                event.preventDefault();
                                selectItem(items[index]);
                        });

                        list.appendChild(element);

                        return { element: element, values: values };
                });

                if (items.length) {
                        results.hidden = false;
                        input.setAttribute("aria-expanded", "true");
                        setActive(0);
                } else {
                        hideResults();
                }
        }

        function fetchResults(term) {
                if (controller) controller.abort();
                controller = new AbortController();
                input.classList.add("ac_loading");

                fetch(`${form.action}?q=${encodeURIComponent(term)}&_=${Date.now()}`, {
                        headers: {
                                "X-Requested-With": "XMLHttpRequest",
                        },
                        signal: controller.signal,
                })
                        .then((response) => response.text())
                        .then((text) => {
                                const lines = text
                                        .split("\n")
                                        .map((line) => line.trim())
                                        .filter(Boolean);

                                renderItems(lines);
                        })
                        .catch((error) => {
                                if (error.name !== "AbortError") hideResults();
                        })
                        .finally(() => {
                                input.classList.remove("ac_loading");
                        });
        }

        input.addEventListener("input", () => {
                clearTimeout(requestTimer);
                if (blurTimer) clearTimeout(blurTimer);

                if (!input.value.trim()) {
                        hideResults();
                        return;
                }

                requestTimer = setTimeout(() => {
                        fetchResults(input.value.trim());
                }, 200);
        });

        input.addEventListener("keydown", (event) => {
                if (
                        results.hidden &&
                        (event.key === "ArrowDown" || event.key === "ArrowUp")
                ) {
                        if (!input.value.trim()) return;
                        fetchResults(input.value.trim());
                        return;
                }

                if (event.key === "ArrowDown") {
                        event.preventDefault();
                        setActive(activeIndex + 1);
                } else if (event.key === "ArrowUp") {
                        event.preventDefault();
                        setActive(activeIndex - 1);
                } else if (event.key === "Enter") {
                        if (activeIndex >= 0 && items[activeIndex]) {
                                event.preventDefault();
                                selectItem(items[activeIndex]);
                        }
                } else if (event.key === "Escape") {
                        hideResults();
                }
        });

        input.addEventListener("blur", () => {
                blurTimer = setTimeout(hideResults, 150);
        });

        input.addEventListener("focus", () => {
                syncResultsWidth();
                if (items.length) {
                        results.hidden = false;
                        input.setAttribute("aria-expanded", "true");
                }
        });

        document.addEventListener("click", (event) => {
                if (!form.contains(event.target)) hideResults();
        });

        window.addEventListener("resize", syncResultsWidth);
        syncResultsWidth();
}

ready(() => {
        const input = query("#search_box");
        if (input) createAutocomplete(input);
});

})();