// retoor <retoor@molodetz.nl>

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

export class AppContextMenu extends Component {
    connectedCallback() {
        if (this._built) {
            return;
        }
        this._built = true;
        this.build();
        this.bindGlobal();
    }

    build() {
        const menu = document.createElement("div");
        menu.className = "context-menu";
        menu.setAttribute("role", "menu");
        this.appendChild(menu);
        this.menu = menu;
        menu.addEventListener("click", (e) => e.stopPropagation());
        menu.addEventListener("keydown", (e) => this.onKeydown(e));
    }

    onKeydown(e) {
        const items = [...this.menu.querySelectorAll(".context-menu-item:not([disabled])")];
        if (!items.length) return;
        const index = items.indexOf(document.activeElement);
        let next = null;
        if (e.key === "ArrowDown") next = index + 1;
        else if (e.key === "ArrowUp") next = index - 1;
        else if (e.key === "Home") next = 0;
        else if (e.key === "End") next = items.length - 1;
        if (next === null) return;
        e.preventDefault();
        items[(next + items.length) % items.length].focus();
    }

    bindGlobal() {
        document.addEventListener("click", (e) => {
            if (!this.menu.contains(e.target)) this.close();
        });
        document.addEventListener("scroll", () => this.close(), true);
        this._lastWidth = window.innerWidth;
        window.addEventListener("resize", () => {
            if (window.innerWidth !== this._lastWidth) {
                this._lastWidth = window.innerWidth;
                this.close();
            }
        });
        document.addEventListener("keydown", (e) => {
            if (e.key === "Escape") this.close();
        });
    }

    render(items) {
        this.menu.textContent = "";
        const list = document.createElement("ul");
        list.className = "context-menu-list";
        list.setAttribute("role", "none");
        items.forEach((item) => {
            const li = document.createElement("li");
            if (item.separator) {
                li.className = "context-menu-sep";
                li.setAttribute("role", "separator");
                list.appendChild(li);
                return;
            }
            li.setAttribute("role", "none");
            const btn = document.createElement("button");
            btn.type = "button";
            btn.className = "context-menu-item" + (item.danger ? " danger" : "");
            btn.setAttribute("role", "menuitem");
            btn.tabIndex = -1;
            btn.disabled = !!item.disabled;
            if (item.disabled) btn.setAttribute("aria-disabled", "true");
            const icon = item.icon ? `<span class="context-menu-icon" aria-hidden="true">${item.icon}</span>` : "";
            btn.innerHTML = icon + '<span class="context-menu-label"></span>';
            btn.querySelector(".context-menu-label").textContent = item.label;
            if (!item.disabled && item.onSelect) {
                btn.addEventListener("click", () => {
                    this.close();
                    item.onSelect();
                });
            }
            li.appendChild(btn);
            list.appendChild(li);
        });
        this.menu.appendChild(list);
    }

    open(x, y, items) {
        if (!items || !items.length) return;
        this.render(items);
        this.menu.classList.add("visible");
        const rect = this.menu.getBoundingClientRect();
        const vv = window.visualViewport;
        const safeW = vv ? vv.width : window.innerWidth;
        const safeH = vv ? vv.height : window.innerHeight;
        const offX = vv ? vv.offsetLeft : 0;
        const offY = vv ? vv.offsetTop : 0;
        let left = x;
        let top = y;
        if (left + rect.width > offX + safeW) left = offX + safeW - rect.width - 8;
        if (top + rect.height > offY + safeH) top = offY + safeH - rect.height - 8;
        this.menu.style.left = Math.max(offX + 8, left) + "px";
        this.menu.style.top = Math.max(offY + 8, top) + "px";
        const first = this.menu.querySelector(".context-menu-item:not([disabled])");
        if (first) setTimeout(() => first.focus(), 0);
    }

    close() {
        this.menu.classList.remove("visible");
    }

    suppressNextClick() {
        const handler = (e) => {
            e.preventDefault();
            e.stopPropagation();
        };
        document.addEventListener("click", handler, { capture: true, once: true });
    }

    attach(host, builder) {
        host.addEventListener("contextmenu", (e) => {
            const items = builder(e);
            if (!items || !items.length) return;
            e.preventDefault();
            this.open(e.clientX, e.clientY, items);
        });

        let timer = null;
        let startX = 0;
        let startY = 0;
        let fired = false;
        const clear = () => {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
        };
        host.addEventListener("touchstart", (e) => {
            if (e.touches.length !== 1) {
                clear();
                return;
            }
            const touch = e.touches[0];
            startX = touch.clientX;
            startY = touch.clientY;
            fired = false;
            timer = setTimeout(() => {
                fired = true;
                this.open(startX, startY, builder(e) || []);
            }, 500);
        }, { passive: true });
        host.addEventListener("touchmove", (e) => {
            const touch = e.touches[0];
            if (touch && (Math.abs(touch.clientX - startX) > 10 || Math.abs(touch.clientY - startY) > 10)) {
                clear();
            }
        }, { passive: true });
        host.addEventListener("touchend", () => {
            clear();
            if (fired) {
                fired = false;
                this.suppressNextClick();
            }
        });
    }
}

customElements.define("dp-context-menu", AppContextMenu);
