(() => {
    const menuController = window.WarpBoxUI?.bindMenuBar?.() || {
        close() {
            document.querySelectorAll(".menu-item.is-open").forEach((item) => {
                item.classList.remove("is-open");
                item.querySelector(".menu-button")?.setAttribute("aria-expanded", "false");
            });
        }
    };

    const toastTarget = document.getElementById("toast");
    const dataNode = document.getElementById("boxes-data");
    const tableBody = document.getElementById("boxes-table-body");
    const emptyState = document.getElementById("boxes-empty-state");
    const searchInput = document.getElementById("boxes-search");
    const statusFilter = document.getElementById("boxes-status-filter");
    const flagFilter = document.getElementById("boxes-flag-filter");
    const sortFilter = document.getElementById("boxes-sort");
    const pageSizeFilter = document.getElementById("boxes-page-size");
    const selectAll = document.getElementById("boxes-select-all");
    const prevPageButton = document.getElementById("boxes-prev-page");
    const nextPageButton = document.getElementById("boxes-next-page");
    const pageLabel = document.getElementById("boxes-page-label");
    const rangeLabel = document.getElementById("boxes-range-label");
    const selectedLabel = document.getElementById("boxes-selected-label");
    const footerSummary = document.getElementById("boxes-footer-summary");
    const detailFileList = document.getElementById("detail-file-list");

    if (!dataNode || !tableBody || !searchInput || !detailFileList) return;

    const statEls = {
        total: document.querySelector("[data-stat-total]"),
        ready: document.querySelector("[data-stat-ready]"),
        uploading: document.querySelector("[data-stat-uploading]"),
        expired: document.querySelector("[data-stat-expired]")
    };

    const detailEls = {
        boxId: document.getElementById("detail-box-id"),
        status: document.getElementById("detail-status"),
        created: document.getElementById("detail-created"),
        expires: document.getElementById("detail-expires"),
        retention: document.getElementById("detail-retention"),
        files: document.getElementById("detail-files"),
        size: document.getElementById("detail-size"),
        flags: document.getElementById("detail-flags"),
        open: document.getElementById("detail-open"),
        zip: document.getElementById("detail-zip")
    };

    function showToast(message, type = "info", duration = 2200) {
        if (window.WarpBoxUI) {
            window.WarpBoxUI.toast(message, type, { target: toastTarget, duration });
            return;
        }
        if (!toastTarget) return;
        toastTarget.textContent = message;
        toastTarget.classList.add("is-visible");
        window.setTimeout(() => toastTarget.classList.remove("is-visible"), duration);
    }

    function parseData() {
        try {
            return JSON.parse(dataNode.textContent || "[]");
        } catch (_) {
            return [];
        }
    }

    const state = {
        boxes: parseData(),
        selected: new Set(),
        activeId: null,
        page: 1
    };

    function pageSize() {
        return Number(pageSizeFilter.value || 10);
    }

    function allBoxes() {
        return state.boxes.slice();
    }

    function sortBoxes(boxes) {
        const sorted = boxes.slice();
        switch (sortFilter.value) {
            case "name":
                sorted.sort((a, b) => a.id.localeCompare(b.id));
                break;
            case "largest":
                sorted.sort((a, b) => compareSizeLabel(a.total_size_label, b.total_size_label));
                break;
            case "expires":
                sorted.sort((a, b) => compareExpiry(a.expires_at_iso, b.expires_at_iso));
                break;
            default:
                sorted.sort((a, b) => (b.created_at_iso || "").localeCompare(a.created_at_iso || ""));
        }
        return sorted;
    }

    function compareSizeLabel(left, right) {
        return sizeLabelToBytes(right) - sizeLabelToBytes(left);
    }

    function sizeLabelToBytes(label) {
        const match = String(label || "").trim().match(/^([\d.]+)\s*([KMGT]?i?B|B)$/i);
        if (!match) return 0;
        const value = Number(match[1]);
        const unit = match[2].toUpperCase();
        const map = { B: 1, KIB: 1024, MIB: 1024 ** 2, GIB: 1024 ** 3, TIB: 1024 ** 4 };
        return value * (map[unit] || 1);
    }

    function compareExpiry(left, right) {
        if (!left && !right) return 0;
        if (!left) return 1;
        if (!right) return -1;
        return left.localeCompare(right);
    }

    function filteredBoxes() {
        const query = searchInput.value.trim().toLowerCase();
        const status = statusFilter.value;
        const flag = flagFilter.value;

        return sortBoxes(allBoxes().filter((box) => {
            const matchesSearch = !query || String(box.search_text || "").includes(query);
            const matchesStatus = status === "all" || box.status === status;
            const matchesFlag = flag === "all" || (box.flags || []).includes(flag);
            return matchesSearch && matchesStatus && matchesFlag;
        }));
    }

    function pagedBoxes(boxes) {
        const size = pageSize();
        const pages = Math.max(1, Math.ceil(boxes.length / size));
        if (state.page > pages) state.page = pages;
        if (state.page < 1) state.page = 1;
        const start = (state.page - 1) * size;
        return {
            items: boxes.slice(start, start + size),
            start,
            pages
        };
    }

    function selectedBoxes() {
        return allBoxes().filter((box) => state.selected.has(box.id));
    }

    function currentActiveBox() {
        const boxes = allBoxes();
        return boxes.find((box) => box.id === state.activeId) || null;
    }

    function ensureActiveBox(filtered) {
        if (filtered.length === 0) {
            state.activeId = null;
            return null;
        }
        if (!filtered.some((box) => box.id === state.activeId)) {
            state.activeId = filtered[0].id;
        }
        return filtered.find((box) => box.id === state.activeId) || filtered[0];
    }

    function renderSummary(filtered) {
        const total = filtered.length;
        const ready = filtered.filter((box) => box.status === "ready").length;
        const uploading = filtered.filter((box) => box.status === "uploading").length;
        const expired = filtered.filter((box) => box.status === "expired" || box.status === "consumed").length;
        statEls.total.textContent = String(total);
        statEls.ready.textContent = String(ready);
        statEls.uploading.textContent = String(uploading);
        statEls.expired.textContent = String(expired);
        footerSummary.textContent = `${allBoxes().length} boxes loaded`;
        selectedLabel.textContent = `Selected: ${state.selected.size}`;
    }

    function renderTable() {
        const filtered = filteredBoxes();
        const active = ensureActiveBox(filtered);
        const page = pagedBoxes(filtered);

        tableBody.innerHTML = "";
        page.items.forEach((box) => tableBody.appendChild(buildRow(box)));
        emptyState.hidden = page.items.length !== 0;

        const startIndex = filtered.length ? page.start + 1 : 0;
        const endIndex = page.start + page.items.length;
        rangeLabel.textContent = `Showing ${startIndex}-${endIndex} of ${filtered.length}`;
        pageLabel.textContent = `Page ${state.page} / ${page.pages}`;
        prevPageButton.disabled = state.page <= 1;
        nextPageButton.disabled = state.page >= page.pages;
        selectAll.checked = page.items.length > 0 && page.items.every((box) => state.selected.has(box.id));

        renderSummary(filtered);
        renderDetails(active);
    }

    function buildRow(box) {
        const row = document.createElement("tr");
        if (box.id === state.activeId) row.classList.add("is-selected");

        row.innerHTML = `
            <td><input type="checkbox" class="boxes-row-check"${state.selected.has(box.id) ? " checked" : ""}></td>
            <td title="${escapeAttr(box.id)}">${box.id}</td>
            <td><span class="boxes-status-pill ${box.status}">${box.status_label}</span></td>
            <td>${box.complete_files}/${box.file_count}</td>
            <td>${box.total_size_label}</td>
            <td>${box.retention_label || "Not set"}</td>
            <td>${box.expires_at_label || "Not set"}</td>
            <td><div class="boxes-flags-cell">${renderFlags(box.flags)}</div></td>
            <td><div class="boxes-action-cell">${renderRowActions(box)}</div></td>
        `;

        row.addEventListener("click", (event) => {
            if (event.target.closest("button") || event.target.closest("a") || event.target.closest("input")) return;
            state.activeId = box.id;
            renderTable();
        });

        row.querySelector(".boxes-row-check")?.addEventListener("change", (event) => {
            if (event.target.checked) {
                state.selected.add(box.id);
            } else {
                state.selected.delete(box.id);
            }
            selectedLabel.textContent = `Selected: ${state.selected.size}`;
            syncSelectAllForPage();
        });

        row.querySelector('[data-row-action="focus"]')?.addEventListener("click", () => {
            state.activeId = box.id;
            renderTable();
        });

        return row;
    }

    function renderFlags(flags) {
        if (!flags || !flags.length) return '<span class="boxes-flag">none</span>';
        return flags.map((flag) => `<span class="boxes-flag">${escapeHtml(flag)}</span>`).join("");
    }

    function renderRowActions(box) {
        const parts = [
            `<a class="win98-button boxes-row-button" href="${escapeAttr(box.open_url)}" target="_blank" rel="noreferrer">Open</a>`,
            `<button class="win98-button boxes-row-button" type="button" data-row-action="focus">View</button>`
        ];
        if (box.zip_available && box.zip_url) {
            parts.push(`<a class="win98-button boxes-row-button" href="${escapeAttr(box.zip_url)}" target="_blank" rel="noreferrer">ZIP</a>`);
        }
        return parts.join("");
    }

    function renderDetails(box) {
        if (!box) {
            detailEls.boxId.textContent = "-";
            detailEls.status.textContent = "-";
            detailEls.created.textContent = "-";
            detailEls.expires.textContent = "-";
            detailEls.retention.textContent = "-";
            detailEls.files.textContent = "-";
            detailEls.size.textContent = "-";
            detailEls.flags.textContent = "-";
            detailEls.open.href = "#";
            detailEls.zip.href = "#";
            detailEls.zip.setAttribute("aria-disabled", "true");
            detailFileList.innerHTML = '<div class="boxes-file-card">No box selected.</div>';
            return;
        }

        detailEls.boxId.textContent = box.id;
        detailEls.status.textContent = box.status_label;
        detailEls.created.textContent = box.created_at_label || "Not set";
        detailEls.expires.textContent = box.expires_at_label || "Not set";
        detailEls.retention.textContent = box.retention_label || "Not set";
        detailEls.files.textContent = `${box.complete_files}/${box.file_count} complete`;
        detailEls.size.textContent = box.total_size_label;
        detailEls.flags.textContent = (box.flags || []).join(", ") || "none";
        detailEls.open.href = box.open_url || "#";

        if (box.zip_available && box.zip_url) {
            detailEls.zip.href = box.zip_url;
            detailEls.zip.removeAttribute("aria-disabled");
            detailEls.zip.style.pointerEvents = "";
            detailEls.zip.style.opacity = "";
        } else {
            detailEls.zip.href = "#";
            detailEls.zip.setAttribute("aria-disabled", "true");
            detailEls.zip.style.pointerEvents = "none";
            detailEls.zip.style.opacity = ".55";
        }

        renderFiles(box.files || []);
    }

    function renderFiles(files) {
        if (!files.length) {
            detailFileList.innerHTML = '<div class="boxes-file-card">No file inventory available for this box.</div>';
            return;
        }

        detailFileList.innerHTML = files.map((file) => `
            <div class="boxes-file-card">
                <div class="boxes-file-row">
                    <div class="boxes-file-name" title="${escapeAttr(file.name)}">${escapeHtml(file.name)}</div>
                    <span class="boxes-status-pill ${escapeAttr(file.status || "legacy")}">${escapeHtml(file.status_label || file.status || "Unknown")}</span>
                </div>
                <div class="boxes-file-meta">
                    <span>${escapeHtml(file.size_label || "0 B")}</span>
                    <span>${escapeHtml(file.mime_type || "application/octet-stream")}</span>
                    ${file.is_complete && file.download_path ? `<a class="boxes-file-link" href="${escapeAttr(file.download_path)}" target="_blank" rel="noreferrer">download</a>` : "<span>pending</span>"}
                </div>
            </div>
        `).join("");
    }

    function syncSelectAllForPage() {
        const filtered = filteredBoxes();
        const page = pagedBoxes(filtered);
        selectAll.checked = page.items.length > 0 && page.items.every((box) => state.selected.has(box.id));
        selectedLabel.textContent = `Selected: ${state.selected.size}`;
    }

    function clearFilters() {
        searchInput.value = "";
        statusFilter.value = "all";
        flagFilter.value = "all";
        sortFilter.value = "newest";
        pageSizeFilter.value = "10";
        state.page = 1;
        renderTable();
    }

    async function runBulkAction(action, ids, deltaSeconds = 0) {
        if (!ids.length) {
            showToast("Select one or more boxes first", "warning");
            return;
        }

        try {
            const response = await fetch("/admin/boxes/actions", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ action, box_ids: ids, delta_seconds: deltaSeconds })
            });
            const payload = await response.json();
            if (!response.ok) {
                const message = payload.error || payload.message || "Action failed";
                const warning = Array.isArray(payload.warnings) && payload.warnings.length ? ` (${payload.warnings[0]})` : "";
                showToast(`${message}${warning}`, "error", 3200);
                return;
            }

            state.boxes = Array.isArray(payload.boxes) ? payload.boxes : state.boxes;
            state.selected.clear();
            if (state.activeId && !state.boxes.some((box) => box.id === state.activeId)) {
                state.activeId = null;
            }
            renderTable();

            let message = payload.message || "Action complete";
            if (Array.isArray(payload.warnings) && payload.warnings.length) {
                message += ` (${payload.warnings.length} warning${payload.warnings.length === 1 ? "" : "s"})`;
            }
            showToast(message, Array.isArray(payload.warnings) && payload.warnings.length ? "warning" : "success", 2800);
        } catch (_) {
            showToast("Network error while updating boxes", "error", 3200);
        }
    }

    function selectedIDsOrActive() {
        if (state.selected.size) return Array.from(state.selected);
        const active = currentActiveBox();
        return active ? [active.id] : [];
    }

    async function runCommand(command) {
        switch (command) {
            case "refresh":
                window.location.reload();
                return;
            case "export":
                exportVisibleCSV();
                showToast("Visible boxes exported");
                return;
            case "status-ready":
                statusFilter.value = "ready";
                state.page = 1;
                renderTable();
                return;
            case "status-expired":
                statusFilter.value = "expired";
                state.page = 1;
                renderTable();
                return;
            case "clear-filters":
                clearFilters();
                showToast("Filters cleared");
                return;
            case "expire":
            case "active-expire":
                await runBulkAction("expire", selectedIDsOrActive());
                return;
            case "extend-day":
            case "active-extend-day":
                await runBulkAction("bump", selectedIDsOrActive(), 24 * 60 * 60);
                return;
            case "extend-week":
            case "active-extend-week":
                await runBulkAction("bump", selectedIDsOrActive(), 7 * 24 * 60 * 60);
                return;
            case "delete":
            case "active-delete":
                if (!window.confirm("Delete selected boxes? This removes stored files.")) return;
                await runBulkAction("delete", selectedIDsOrActive());
                return;
            case "help-scope":
                showToast("Ownership filter waits for account + box owner data in backend", "info", 3400);
                return;
            case "help-flags":
                showToast("Flags: protected, one-time, zip off, legacy, consumed", "info", 3200);
                return;
            default:
                showToast(`Unknown command: ${command}`, "warning");
        }
    }

    function exportVisibleCSV() {
        const rows = filteredBoxes().map((box) => ([
            box.id,
            box.status_label,
            box.file_count,
            box.total_size_label,
            box.retention_label,
            box.expires_at_label,
            (box.flags || []).join("|")
        ]));
        const csv = [
            ["box_id", "status", "files", "size", "retention", "expires", "flags"],
            ...rows
        ].map((row) => row.map(csvCell).join(",")).join("\n");

        const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
        const url = URL.createObjectURL(blob);
        const anchor = document.createElement("a");
        anchor.href = url;
        anchor.download = "warpbox-boxes.csv";
        anchor.click();
        URL.revokeObjectURL(url);
    }

    function csvCell(value) {
        const text = String(value ?? "");
        if (/[",\n]/.test(text)) return `"${text.replaceAll('"', '""')}"`;
        return text;
    }

    function escapeHtml(value) {
        return String(value ?? "")
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll('"', "&quot;");
    }

    function escapeAttr(value) {
        return escapeHtml(value).replaceAll("'", "&#39;");
    }

    [searchInput, statusFilter, flagFilter, sortFilter].forEach((control) => {
        control.addEventListener(control.tagName === "INPUT" ? "input" : "change", () => {
            state.page = 1;
            renderTable();
        });
    });

    pageSizeFilter.addEventListener("change", () => {
        state.page = 1;
        renderTable();
    });

    selectAll?.addEventListener("change", () => {
        const filtered = filteredBoxes();
        const page = pagedBoxes(filtered);
        page.items.forEach((box) => {
            if (selectAll.checked) state.selected.add(box.id);
            else state.selected.delete(box.id);
        });
        renderTable();
    });

    prevPageButton?.addEventListener("click", () => {
        state.page -= 1;
        renderTable();
    });

    nextPageButton?.addEventListener("click", () => {
        state.page += 1;
        renderTable();
    });

    document.querySelectorAll("[data-command]").forEach((button) => {
        button.addEventListener("click", async () => {
            menuController.close();
            await runCommand(button.dataset.command);
        });
    });

    document.addEventListener("keydown", async (event) => {
        if (event.key === "Escape") menuController.close();
        if (event.key === "F5") {
            event.preventDefault();
            await runCommand("refresh");
        }
    });

    if (state.boxes.length > 0) {
        state.activeId = state.boxes[0].id;
    }
    renderTable();
})();
