///////////////////////////////////////////////////////////////////////////////
// domainActions.js
// Handles footer action buttons for the domain portfolio tool.
// Dependencies (globals): API_BASE_URL, DataTableView, _categories,
//                         _domainMembershipMap
///////////////////////////////////////////////////////////////////////////////

(function () {

    ///////////////////////////////////////////////////////////////////////////
    // Category name placeholders
    ///////////////////////////////////////////////////////////////////////////

    function getSoldCategoryName()    { return "Sold Domains";    }
    function getIgnoredCategoryName() { return "Ignored Domains"; }
    function getDeletedCategoryName() { return "Deleted Domains"; }

    ///////////////////////////////////////////////////////////////////////////
    // Constants
    ///////////////////////////////////////////////////////////////////////////

    const ATG_PANEL_ID   = "atg-sidebar";
    const ATG_OVERLAY_ID = "atg-overlay";
    const ATG_OPEN_CLS   = "cp-open"; // reuse column panel open class

    // cid <= 1 is "All Domains" — cannot remove from it
    const ALL_DOMAINS_CID = 1;

    ///////////////////////////////////////////////////////////////////////////
    // State
    ///////////////////////////////////////////////////////////////////////////

    let _soldCid    = null;
    let _ignoredCid = null;
    let _deletedCid = null;

    let _maxDownloadRows = 5000; // fetched from API on init; fallback default

    ///////////////////////////////////////////////////////////////////////////
    // Public init — called from portfolio.js initApp()
    ///////////////////////////////////////////////////////////////////////////

    window.initDomainActions = function () {
        _buildAddToGroupPanel();
        _wireButtons();
        _attachEscapeKey();
        _fetchMaxDownloadRows();
        _checkScheduledReports();
        _updateDownloadIcons();
    };

    function _updateDownloadIcons() {
        const mode = (typeof ColorMode !== "undefined") ? ColorMode.getMode() : "light";
        ["btn-download-all", "btn-scheduled-reports"].forEach(function(id) {
            const img = document.querySelector("#" + id + " img");
            if (img && img.src) {
                img.src = img.src.replace(/\/(light|dark)\//, "/" + mode + "/");
            }
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // Called from portfolio.js after categories are loaded
    ///////////////////////////////////////////////////////////////////////////

    window.initDomainActionCategories = function (categories) {
        const soldName    = getSoldCategoryName().toLowerCase();
        const ignoredName = getIgnoredCategoryName().toLowerCase();
        const deletedName = getDeletedCategoryName().toLowerCase();

        categories.forEach(cat => {
            const name = (cat.name || "").toLowerCase();
            if (name === soldName)    _soldCid    = cat.id;
            if (name === ignoredName) _ignoredCid = cat.id;
            if (name === deletedName) _deletedCid = cat.id;
        });

        _setButtonVisible("btn-mark-sold",    _soldCid    !== null);
        _setButtonVisible("btn-add-ignore",   _ignoredCid !== null);
        _setButtonVisible("btn-mark-deleted", _deletedCid !== null);
    };

    ///////////////////////////////////////////////////////////////////////////
    // Build Add To Group sidebar panel DOM — runs once
    ///////////////////////////////////////////////////////////////////////////

    function _buildAddToGroupPanel() {
        // Overlay
        const overlay = document.createElement("div");
        overlay.id = ATG_OVERLAY_ID;
        overlay.addEventListener("click", _closeAddToGroupPanel);
        document.body.appendChild(overlay);

        // Panel — reuses cp-* classes so it inherits column panel styles
        const panel = document.createElement("div");
        panel.id = ATG_PANEL_ID;
        panel.innerHTML = `
            <div class="cp-header">
                <span class="cp-title">Add To Group</span>
                <button class="cp-close-btn" title="Close" aria-label="Close panel">&#x2715;</button>
            </div>
            <div class="cp-subheader">
                Select one or more categories to add the selected domains to
            </div>
            <div class="atg-domain-summary" id="atg-domain-summary"></div>
            <div class="cp-list-wrap">
                <ul class="cp-list" id="atg-list"></ul>
            </div>
            <div class="cp-footer">
                <button class="cp-btn cp-btn-cancel" id="atg-cancel">Cancel</button>
                <button class="cp-btn cp-btn-apply" id="atg-apply">Apply</button>
            </div>
        `;
        document.body.appendChild(panel);

        panel.querySelector(".cp-close-btn").addEventListener("click", _closeAddToGroupPanel);
        panel.querySelector("#atg-cancel").addEventListener("click", _closeAddToGroupPanel);
        panel.querySelector("#atg-apply").addEventListener("click", _applyAddToGroup);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Open Add To Group panel
    ///////////////////////////////////////////////////////////////////////////

    async function _openAddToGroupPanel() {
        const ids = _getSelectedIds();
        if (!ids.length) return;

        // Use _domainMembershipMap as primary; re-fetch if map has no data
        // for any of the selected domains
        let membershipMap = _domainMembershipMap;
        const mapHasData  = ids.some(id => membershipMap.has(String(id)));

        if (!mapHasData) {
            membershipMap = await _fetchMembershipMap(ids);
        }

        // Build set of cids that ALL selected domains belong to
        // Only exclude a category if every selected domain is already in it
        const cidMemberCount = {};
        ids.forEach(id => {
            const cids = membershipMap.get(String(id)) || [];
            cids.forEach(cid => {
                cidMemberCount[String(cid)] = (cidMemberCount[String(cid)] || 0) + 1;
            });
        });

        // A category is excluded only if ALL selected domains are already in it
        const excludedCids = new Set(
            Object.entries(cidMemberCount)
                .filter(([, count]) => count === ids.length)
                .map(([cid]) => cid)
        );

        // Filter available categories — exclude already-member categories and All Domains
        const cats      = window._categories || [];
        const available = cats.filter(cat =>
            !excludedCids.has(String(cat.id)) &&
            parseInt(cat.id, 10) > ALL_DOMAINS_CID
        );

        _renderAddToGroupList(available);
        _renderDomainSummary(ids);

        document.getElementById(ATG_OVERLAY_ID).classList.add(ATG_OPEN_CLS);
        document.getElementById(ATG_PANEL_ID).classList.add(ATG_OPEN_CLS);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Render domain summary in Add To Group panel
    ///////////////////////////////////////////////////////////////////////////

    function _renderDomainSummary(ids) {
        const el = document.getElementById("atg-domain-summary");
        if (!el) return;

        const summary = _getSelectedDomainSummary(ids);
        el.innerHTML  = `
            <div class="atg-summary-count">
                This will affect <strong>${ids.length}</strong> domain(s):
            </div>
            <div class="atg-summary-names">${_escHtml(summary)}</div>
        `;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Render category list
    ///////////////////////////////////////////////////////////////////////////

    function _renderAddToGroupList(categories) {
        const ul = document.getElementById("atg-list");
        ul.innerHTML = "";

        if (!categories.length) {
            ul.innerHTML = '<li class="atg-empty">All selected domains are already in every category.</li>';
            return;
        }

        categories.forEach(cat => {
            const li = document.createElement("li");
            li.className     = "cp-item atg-cat-item";
            li.dataset.catId = cat.id;

            const iconHtml = cat.caticon
                ? `<img class="atg-cat-icon" src="lib/layouts/images/icons/categories/light/${cat.caticon}" alt="">`
                : `<span class="atg-cat-icon-placeholder"></span>`;

            li.innerHTML = `
                <label class="cp-item-label">
                    <input type="checkbox" class="cp-checkbox atg-checkbox">
                    ${iconHtml}
                    <span class="cp-col-names">
                        <span class="cp-col-label">${_escHtml(cat.name || "")}</span>
                    </span>
                </label>
            `;

            ul.appendChild(li);
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // Close Add To Group panel
    ///////////////////////////////////////////////////////////////////////////

    function _closeAddToGroupPanel() {
        const overlay = document.getElementById(ATG_OVERLAY_ID);
        const panel   = document.getElementById(ATG_PANEL_ID);
        if (overlay) overlay.classList.remove(ATG_OPEN_CLS);
        if (panel)   panel.classList.remove(ATG_OPEN_CLS);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Apply Add To Group
    ///////////////////////////////////////////////////////////////////////////

    async function _applyAddToGroup() {
        const ids = _getSelectedIds();
        if (!ids.length) {
            await SedUI.alert({ title: 'No Selection', message: 'No domains selected.', type: 'info' });
            return;
        }

        const checkedCids = Array.from(
            document.querySelectorAll("#atg-list .atg-checkbox:checked")
        ).map(cb => cb.closest(".atg-cat-item").dataset.catId);

        if (!checkedCids.length) {
            await SedUI.alert({ title: 'No Category', message: 'Please select at least one category.', type: 'info' });
            return;
        }

        const catNames = checkedCids.map(cid => {
            const cat = (window._categories || []).find(c => String(c.id) === String(cid));
            return cat ? cat.name : cid;
        }).join(", ");

        const confirmed = await SedUI.confirm({
            title        : 'Add To Group',
            message      : `This will affect ${ids.length} domain(s):\n${_getSelectedDomainSummary(ids)}\n\nCategories: ${catNames}`,
            confirmLabel : 'Add',
            confirmClass : 'btn-primary',
            cancelLabel  : 'Cancel',
        });
        if (!confirmed) return;

        try {
            const params = new URLSearchParams({
                c    : "set",
                t    : "category",
                oper : "addto",
                cid  : checkedCids.join(","),
                id   : ids.join(",")
            });

            const response = await fetch(API_BASE_URL, {
                method     : "POST",
                credentials: "same-origin",
                headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                body       : params.toString()
            });

            const json = await response.json();

            if (json.status === "ok") {
                console.log(`[domainActions] Add To Group: ${json.added ?? ids.length} domain(s) added.`);
                _closeAddToGroupPanel();
                _reloadTable();
            } else {
                console.error("[domainActions] Add To Group failed:", json.error);
                await SedUI.alert({ title: 'Action Failed', message: json.error || 'Unknown error', type: 'error' });
            }

        } catch (err) {
            console.error("[domainActions] Add To Group error:", err);
            await SedUI.alert({ title: 'Error', message: 'An error occurred. Please try again.', type: 'error' });
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Remove From Group
    ///////////////////////////////////////////////////////////////////////////

    async function _handleRemoveFromGroup() {
        const ids = _getSelectedIds();
        if (!ids.length) return;

        const activeBtn  = document.querySelector(".btn-cat-selector.btn-active");
        const cid        = activeBtn ? parseInt(activeBtn.getAttribute("data-cid"), 10) : 0;
        const activeName = activeBtn ? activeBtn.textContent.trim() : "this category";
        if (!cid || cid <= ALL_DOMAINS_CID) return;

        const confirmed = await SedUI.confirm({
            title        : `Remove From "${activeName}"`,
            message      : `This will affect ${ids.length} domain(s):\n${_getSelectedDomainSummary(ids)}`,
            confirmLabel : 'Remove',
            confirmClass : 'btn-danger',
            cancelLabel  : 'Cancel',
        });
        if (!confirmed) return;

        try {
            const params = new URLSearchParams({
                c    : "set",
                t    : "category",
                oper : "delfrom",
                cid  : cid,
                id   : ids.join(",")
            });

            const response = await fetch(API_BASE_URL, {
                method     : "POST",
                credentials: "same-origin",
                headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                body       : params.toString()
            });

            const json = await response.json();

            if (json.status === "ok") {
                console.log("[domainActions] Remove From Group: done.");
                _reloadTable();
            } else {
                console.error("[domainActions] Remove From Group failed:", json.error);
                await SedUI.alert({ title: 'Action Failed', message: json.error || 'Unknown error', type: 'error' });
            }

        } catch (err) {
            console.error("[domainActions] Remove From Group error:", err);
            await SedUI.alert({ title: 'Error', message: 'An error occurred. Please try again.', type: 'error' });
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Shared handler for sold / ignore actions
    ///////////////////////////////////////////////////////////////////////////

    async function _handleCategoryAction(cid, actionLabel) {
        const ids = _getSelectedIds();
        if (!ids.length) {
            await SedUI.alert({ title: 'No Selection', message: 'No domains selected.', type: 'info' });
            return;
        }

        const confirmed = await SedUI.confirm({
            title        : actionLabel,
            message      : `This will affect ${ids.length} domain(s):\n${_getSelectedDomainSummary(ids)}`,
            confirmLabel : 'Confirm',
            confirmClass : 'btn-primary',
            cancelLabel  : 'Cancel',
        });
        if (!confirmed) return;

        try {
            const params = new URLSearchParams({
                c    : "set",
                t    : "category",
                oper : "addto",
                cid  : cid,
                id   : ids.join(",")
            });

            const response = await fetch(API_BASE_URL, {
                method     : "POST",
                credentials: "same-origin",
                headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                body       : params.toString()
            });

            const json = await response.json();

            if (json.status === "ok") {
                console.log(`[domainActions] ${actionLabel}: ${json.added ?? ids.length} domain(s) added.`);
                _reloadTable();
            } else {
                console.error(`[domainActions] ${actionLabel} failed:`, json.error);
                await SedUI.alert({ title: 'Action Failed', message: json.error || 'Unknown error', type: 'error' });
            }

        } catch (err) {
            console.error(`[domainActions] ${actionLabel} error:`, err);
            await SedUI.alert({ title: 'Error', message: 'An error occurred. Please try again.', type: 'error' });
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Fetch membership map from API (fallback)
    ///////////////////////////////////////////////////////////////////////////

    async function _fetchMembershipMap(ids) {
        const map = new Map();
        try {
            const params = new URLSearchParams({
                c   : "get",
                t   : "domain",
                oper: "catmap",
                id  : ids.join(",")
            });

            const res  = await fetch(API_BASE_URL + "?" + params.toString(), {
                credentials: "same-origin"
            });
            const json = await res.json();

            if (json.status === "ok" && json.data) {
                Object.entries(json.data).forEach(([domainId, cids]) => {
                    map.set(String(domainId), cids);
                });
            }
        } catch (err) {
            console.error("[domainActions] Failed to fetch membership map:", err);
        }
        return map;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Wire button click handlers
    ///////////////////////////////////////////////////////////////////////////

    function _wireButtons() {
        const btnAddGroup    = document.getElementById("btn-add-group");
        const btnRemoveGroup = document.getElementById("btn-remove-group");
        const btnSold        = document.getElementById("btn-mark-sold");
        const btnIgnore      = document.getElementById("btn-add-ignore");
        const btnDeleted     = document.getElementById("btn-mark-deleted");

        if (btnAddGroup)    btnAddGroup.addEventListener("click",    () => _openAddToGroupPanel());
        if (btnRemoveGroup) btnRemoveGroup.addEventListener("click", () => _handleRemoveFromGroup());
        if (btnSold)        btnSold.addEventListener("click",        () => _handleCategoryAction(_soldCid,    `Mark as Sold (${getSoldCategoryName()})`));
        if (btnIgnore)      btnIgnore.addEventListener("click",      () => _handleCategoryAction(_ignoredCid, `Add to Ignore List (${getIgnoredCategoryName()})`));
        if (btnDeleted)     btnDeleted.addEventListener("click",     () => _handleCategoryAction(_deletedCid, `Mark as Deleted (${getDeletedCategoryName()})`));

        const btnDelete = document.getElementById("btn-delete-permanent");
        if (btnDelete)  btnDelete.addEventListener("click", () => _handleDeletePermanent());

        const btnDownloadAll       = document.getElementById("btn-download-all");
        const btnScheduledReports  = document.getElementById("btn-scheduled-reports");
        if (btnDownloadAll)      btnDownloadAll.addEventListener("click",      () => _downloadAll());
        if (btnScheduledReports) btnScheduledReports.addEventListener("click", () => _openScheduledReportsDialog());
    }

    ///////////////////////////////////////////////////////////////////////////
    // Delete permanently
    ///////////////////////////////////////////////////////////////////////////

    async function _handleDeletePermanent() {
        const ids = _getSelectedIds();
        if (!ids.length) return;

        const confirmed = await SedUI.confirm({
            title        : 'Delete Permanently',
            message      : `This cannot be undone.\n\nThis will permanently delete ${ids.length} domain(s):\n${_getSelectedDomainSummary(ids)}`,
            confirmLabel : 'Delete Permanently',
            confirmClass : 'btn-danger',
            cancelLabel  : 'Cancel',
            icon         : 'warning',
        });
        if (!confirmed) return;

        try {
            const params = new URLSearchParams({
                c    : "grid",
                t    : "domain",
                oper : "del",
                id   : ids.join(",")
            });

            const response = await fetch(API_BASE_URL, {
                method     : "POST",
                credentials: "same-origin",
                headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                body       : params.toString()
            });

            const json = await response.json();

            if (json.status === "ok") {
                console.log(`[domainActions] Deleted ${ids.length} domain(s) permanently.`);
                _reloadTable();
            } else {
                console.error("[domainActions] Delete failed:", json.error);
                await SedUI.alert({ title: 'Delete Failed', message: json.error || 'Unknown error', type: 'error' });
            }

        } catch (err) {
            console.error("[domainActions] Delete error:", err);
            await SedUI.alert({ title: 'Error', message: 'An error occurred. Please try again.', type: 'error' });
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Download — fetch admin threshold
    ///////////////////////////////////////////////////////////////////////////

    async function _fetchMaxDownloadRows() {
        try {
            const params = new URLSearchParams({
                c   : "admin",
                t   : "options",
                oper: "get",
                max_download_rows: ""
            });
            const res  = await fetch(API_BASE_URL + "?" + params.toString(), {
                credentials: "same-origin"
            });
            const json = await res.json();
            if (json.status === "ok" && parseInt(json.max_download_rows) > 0) {
                _maxDownloadRows = parseInt(json.max_download_rows);
            }
        } catch (err) {
            console.warn("[domainActions] Could not fetch max_download_rows, using default:", _maxDownloadRows);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Download all rows — direct or scheduled based on row count
    ///////////////////////////////////////////////////////////////////////////

    async function _downloadAll() {
        const dt = DataTableView.getTable("dp-table");
        if (!dt) return;

        const total = parseInt(dt.options.totalrows || 0);

        if (total > _maxDownloadRows) {
            await _scheduleDownload(dt);
        } else {
            _directDownload(dt);
        }
    }

    function _buildDownloadParams(dt, bgproc) {
        const cid    = parseInt(dt.options.cid  || 0);
        const aqid   = parseInt(dt.options.aqid || 0);
        const sidx   = dt.options.sortname  || "d.domain";
        const sord   = dt.options.sortorder || "asc";

        // query/params live in source.params, not dt.options directly
        const srcParams = (dt.options.source && dt.options.source.params) || {};
        const rawQuery  = srcParams.query  || "";
        const rawParams = srcParams.params || "";

        // Skip [in_domainlist] — the download API does not support this token.
        // If the curated list is the only active filter, omit query entirely.
        const query  = rawQuery.includes("[in_domainlist]") ? "" : rawQuery;
        const params = query ? rawParams : "";

        const date     = new Date().toISOString().slice(0, 10);
        const filename = "portfolio-export-" + date + ".csv";

        let datastr = "c=download&t=domain";
        datastr += "&colmode=all";
        datastr += "&type=csv";

        // Pass current grid columns so server puts them first (d.domain ends up first).
        // Strip UI-only columns that have no place in a CSV export.
        const _skipCols = new Set(["d.r_h_disp", "s.r_h_disp", "r_h_disp", "ctrl", "subgrid"]);
        const rawColumns = dt.getColumnNames ? dt.getColumnNames() : "";
        const columns = rawColumns.split(",").filter(c => !_skipCols.has(c.trim())).join(",");
        if (columns) datastr += "&columns=" + encodeURIComponent(columns);
        datastr += "&sidx="     + encodeURIComponent(sidx);
        datastr += "&sord="     + encodeURIComponent(sord);
        datastr += "&filename=" + encodeURIComponent(filename);

        if (cid > 1) {
            datastr += "&cid=" + encodeURIComponent(cid);
        } else if (aqid > 0) {
            datastr += "&aqid=" + encodeURIComponent(aqid);
        }

        if (query) {
            datastr += "&query="  + encodeURIComponent(query);
            datastr += "&params=" + encodeURIComponent(params);
        }

        if (bgproc) datastr += "&bgproc=1";

        return datastr;
    }

    function _directDownload(dt) {
        const datastr  = _buildDownloadParams(dt, false);
        const posturl  = API_BASE_URL + "?";
        const wrap     = document.getElementById("csvdownload");
        if (!wrap) return;
        const form = document.createElement("form");
        form.id     = "exportform";
        form.action = posturl + datastr;
        form.method = "post";
        wrap.appendChild(form);
        form.submit();
        form.remove();
    }

    async function _scheduleDownload(dt) {
        try {
            const datastr = _buildDownloadParams(dt, true);
            const res  = await fetch(API_BASE_URL, {
                method     : "POST",
                credentials: "same-origin",
                headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                body       : datastr
            });
            const json = await res.json();

            if (json.status === "ok") {
                await SedUI.alert({
                    title  : "Download Scheduled",
                    message: "A background process has been set to generate your report.\n\nUse the Scheduled Reports button when it is ready to download.",
                    type   : "info"
                });
                _checkScheduledReports();
            } else {
                await SedUI.alert({ title: "Schedule Failed", message: json.error || "Could not schedule the download.", type: "error" });
            }
        } catch (err) {
            console.error("[domainActions] Schedule download error:", err);
            await SedUI.alert({ title: "Error", message: "An error occurred. Please try again.", type: "error" });
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Scheduled reports — check availability and open dialog
    ///////////////////////////////////////////////////////////////////////////

    async function _checkScheduledReports() {
        try {
            const res  = await fetch(API_BASE_URL + "?c=download&t=csvdata&oper=get", {
                credentials: "same-origin"
            });
            const json = await res.json();
            const btn  = document.getElementById("btn-scheduled-reports");
            if (!btn) return;
            const hasReports = json.status === "ok" && Array.isArray(json.data) && json.data.length > 0;
            btn.style.display = hasReports ? "" : "none";
        } catch (err) {
            console.warn("[domainActions] Could not check scheduled reports:", err);
        }
    }

    async function _openScheduledReportsDialog() {
        try {
            const res  = await fetch(API_BASE_URL + "?c=download&t=csvdata&oper=get", {
                credentials: "same-origin"
            });
            const json = await res.json();
            if (json.status !== "ok" || !json.data.length) {
                await SedUI.alert({ title: "Scheduled Reports", message: "No scheduled reports available.", type: "info" });
                _checkScheduledReports();
                return;
            }
            _buildScheduledReportsDialog(json.data);
        } catch (err) {
            await SedUI.alert({ title: "Error", message: "Could not load scheduled reports.", type: "error" });
        }
    }

    function _buildScheduledReportsDialog(reports) {
        // Remove previous instance
        const existing = document.getElementById("sched-reports-modal");
        if (existing) existing.remove();

        const modal = document.createElement("div");
        modal.id        = "sched-reports-modal";
        modal.className = "sui-overlay";
        modal.style.display  = "flex";
        modal.style.zIndex   = "2500"; // below SedUI (3000) so confirm dialogs appear on top

        const rows = reports.map(r => {
            const desc = r.description ? ` <span class="sched-report-desc">[${_escHtml(r.description)}]</span>` : "";
            return `<tr>
                <td class="sched-report-name">${_escHtml(r.name)}${desc}</td>
                <td class="sched-report-actions">
                    <button class="btn btn-sm btn-primary sched-download-btn"  data-id="${r.id}">Download</button>
                    <button class="btn btn-sm btn-danger  sched-delete-btn"    data-id="${r.id}">Delete</button>
                </td>
            </tr>`;
        }).join("");

        modal.innerHTML = `
            <div class="sui-dialog sched-reports-dialog">
                <div class="sui-dialog-header">
                    <span class="sui-dialog-title">Scheduled Reports</span>
                </div>
                <div class="sui-dialog-body sched-reports-body">
                    <table class="sched-reports-table">
                        <tbody>${rows}</tbody>
                    </table>
                </div>
                <div class="sui-dialog-footer">
                    <button class="btn btn-sm btn-secondary" id="sched-reports-close">Close</button>
                </div>
            </div>`;

        document.body.appendChild(modal);

        modal.querySelector("#sched-reports-close").addEventListener("click", () => _closeScheduledReportsDialog());
        modal.addEventListener("click", e => { if (e.target === modal) _closeScheduledReportsDialog(); });

        // Download buttons
        modal.querySelectorAll(".sched-download-btn").forEach(btn => {
            btn.addEventListener("click", function() {
                const id      = this.getAttribute("data-id");
                const posturl = API_BASE_URL + "?";
                const datastr = "c=download&t=csvdata&oper=get&mode=csv&id=" + encodeURIComponent(id);
                const wrap    = document.getElementById("csvdownload");
                if (!wrap) return;
                const form = document.createElement("form");
                form.action = posturl + datastr;
                form.method = "post";
                wrap.appendChild(form);
                form.submit();
                form.remove();
            });
        });

        // Delete buttons
        modal.querySelectorAll(".sched-delete-btn").forEach(btn => {
            btn.addEventListener("click", async function() {
                const id = this.getAttribute("data-id");
                const ok = await SedUI.confirm({
                    title        : "Delete Report",
                    message      : "Delete this scheduled report?",
                    confirmLabel : "Delete",
                    confirmClass : "btn-danger",
                    cancelLabel  : "Cancel"
                });
                if (!ok) return;
                try {
                    const res  = await fetch(API_BASE_URL, {
                        method     : "POST",
                        credentials: "same-origin",
                        headers    : { "Content-Type": "application/x-www-form-urlencoded" },
                        body       : "c=download&t=csvdata&oper=del&id=" + encodeURIComponent(id)
                    });
                    const json = await res.json();
                    if (json.status === "ok") {
                        _closeScheduledReportsDialog();
                        _checkScheduledReports();
                    } else {
                        await SedUI.alert({ title: "Error", message: json.error || "Could not delete report.", type: "error" });
                    }
                } catch (err) {
                    await SedUI.alert({ title: "Error", message: "An error occurred.", type: "error" });
                }
            });
        });

        document.addEventListener("keydown", function onKey(e) {
            if (e.key === "Escape") {
                document.removeEventListener("keydown", onKey);
                _closeScheduledReportsDialog();
            }
        });
    }

    function _closeScheduledReportsDialog() {
        const modal = document.getElementById("sched-reports-modal");
        if (modal) modal.remove();
    }

    ///////////////////////////////////////////////////////////////////////////
    // Selection + button state polling
    ///////////////////////////////////////////////////////////////////////////

    function _updateButtonStates() {
        const ids          = _getSelectedIds();
        const hasSelection = ids.length > 0;

        _setButtonEnabled("btn-add-group",        hasSelection);
        _setButtonEnabled("btn-mark-sold",        hasSelection);
        _setButtonEnabled("btn-add-ignore",       hasSelection);
        _setButtonEnabled("btn-mark-deleted",     hasSelection);
        _setButtonEnabled("btn-delete-permanent", hasSelection);

        // Remove From Group — also disabled when active category is All Domains
        const activeBtn  = document.querySelector(".btn-cat-selector.btn-active");
        const activeCid  = activeBtn ? parseInt(activeBtn.getAttribute("data-cid"), 10) : 0;
        const activeName = activeBtn ? activeBtn.textContent.trim() : "";
        const canRemove  = hasSelection && activeCid > ALL_DOMAINS_CID;

        // Update button label to always reflect which category will be affected
        const btnRemove = document.getElementById("btn-remove-group");
        if (btnRemove) {
            btnRemove.textContent = (activeCid > ALL_DOMAINS_CID && activeName)
                ? `Remove From "${activeName}"`
                : "Remove From Group";
            btnRemove.style.cursor = canRemove ? "" : "not-allowed";
        }

        _setButtonEnabled("btn-remove-group", canRemove);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Escape key — close Add To Group panel if open
    ///////////////////////////////////////////////////////////////////////////

    function _attachEscapeKey() {
        document.addEventListener("keydown", function (e) {
            if (e.key === "Escape") {
                const panel = document.getElementById(ATG_PANEL_ID);
                if (panel && panel.classList.contains(ATG_OPEN_CLS)) {
                    _closeAddToGroupPanel();
                }
            }
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // Utilities
    ///////////////////////////////////////////////////////////////////////////

    function _getSelectedIds() {
        const dt = DataTableView.getTable("dp-table");
        if (!dt) return [];
        return dt.getSelectedIds();
    }

    // Returns a human-readable summary of selected domains for confirm dialogs
    // e.g. "example.com, foo.com, bar.com and 12 more"
    function _getSelectedDomainSummary(ids, maxShown = 5) {
        const dt = DataTableView.getTable("dp-table");
        if (!dt || !dt.options.data) return `${ids.length} domain(s)`;

        const names = [];
        ids.forEach(id => {
            const row = dt.options.data.find(r => String(r.id) === String(id));
            if (row) {
                const name = row["d.domain"] || row["domain"] || null;
                if (name) names.push(name);
            }
        });

        if (!names.length) return `${ids.length} domain(s)`;

        const shown    = names.slice(0, maxShown);
        const remaining = names.length - shown.length;
        let summary    = shown.join(", ");
        if (remaining > 0) summary += ` and ${remaining} more`;
        return summary;
    }

    function _reloadTable() {
        const dt = DataTableView.getTable("dp-table");
        if (dt) dt.reload();
    }

    function _setButtonVisible(id, visible) {
        const btn = document.getElementById(id);
        if (!btn) return;
        btn.style.display = visible ? "" : "none";
    }

    function _setButtonEnabled(id, enabled) {
        const btn = document.getElementById(id);
        if (!btn) return;
        btn.disabled = !enabled;
    }

    function _escHtml(str) {
        return String(str)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;");
    }

    setInterval(_updateButtonStates, 500);

})();