///////////////////////////////////////////////////////////////////////////////
// Portfolio filters
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Filter bar toggle button
// Called after initColumnPanel() so the Columns button already exists.
// Inserts a flex spacer + filter toggle immediately before the Columns button
// so both sit together at the right of the footer.
// Silently skipped if #domain-portfolio-filters is absent (bar disabled).
///////////////////////////////////////////////////////////////////////////////

function initFilterToggleButton() {
    const footer  = document.getElementById("domain-portfolio-footer");
    const filters = document.getElementById("domain-portfolio-filters");
    if (!footer || !filters) return;

    const mode = (typeof ColorMode !== "undefined") ? ColorMode.getMode() : "light";

    const btn         = document.createElement("button");
    btn.id            = "btn-toggle-filters";
    btn.className     = "btn btn-sm btn-secondary pf-filter-toggle-btn";
    btn.title         = "Show / Hide Filters";

    const img     = document.createElement("img");
    img.src       = `lib/layouts/colors/${mode}/images/64/toolbar/filter.png`;
    img.className = "pf-filter-btn-icon";
    btn.appendChild(img);

    btn.addEventListener("click", _toggleFilterBar);

    // Insert spacer + filter toggle just before the Columns button
    const columnsBtn = document.getElementById("cp-open-btn");
    if (columnsBtn) {
        const spacer = document.createElement("div");
        spacer.style.cssText = "flex: 1 1 auto;";
        footer.insertBefore(spacer, columnsBtn);
        footer.insertBefore(btn, columnsBtn);
    } else {
        const spacer = document.createElement("div");
        spacer.style.cssText = "flex: 1 1 auto;";
        footer.appendChild(spacer);
        footer.appendChild(btn);
    }

    // Panels start visible — add active class by default, remove if restoring hidden state
    btn.classList.add("pf-filter-toggle-active");
    if (localStorage.getItem("portfolio_filters_hidden") === "1") {
        const filters = document.getElementById("domain-portfolio-filters");
        if (filters) filters.style.display = "none";
        btn.classList.remove("pf-filter-toggle-active");
    }
}

function _toggleFilterBar() {
    const filters = document.getElementById("domain-portfolio-filters");
    const btn     = document.getElementById("btn-toggle-filters");
    if (!filters) return;

    const isVisible = filters.style.display !== "none";
    filters.style.display = isVisible ? "none" : "";
    localStorage.setItem("portfolio_filters_hidden", isVisible ? "1" : "0");
    // active = now visible = !isVisible
    if (btn) btn.classList.toggle("pf-filter-toggle-active", !isVisible);
}
// State is private; portfolio.js interacts via setCuratedList/clearCuratedList.
///////////////////////////////////////////////////////////////////////////////

let _curatedDomainList = null; // pipe-separated string e.g. "a.com|b.net" or null

function getCuratedDomainCount() {
    if (!_curatedDomainList) return 0;
    return _curatedDomainList.split('|').length;
}

function setCuratedList(rawText) {
    const domains = (rawText || '')
        .split(/\r?\n|\r/)
        .map(d => d.trim().toLowerCase())
        .filter(d => d.length > 0);

    if (domains.length === 0) {
        clearCuratedList();
        return;
    }

    _curatedDomainList = domains.join('|');

    // Set cid/aqid to All Domains base so category doesn't restrict the list
    const dt   = DataTableView.getTable("dp-table");
    const aqid = typeof getDomainsOfInterestAqid === "function" ? getDomainsOfInterestAqid() : 0;
    if (dt) {
        dt.options.cid  = aqid > 0 ? 0 : 1;
        dt.options.aqid = aqid > 0 ? aqid : 0;
    }

    // Deactivate category buttons — curated list is now the base
    document.querySelectorAll(".btn-cat-selector").forEach(b => b.classList.remove("btn-active"));
    document.getElementById("cat-overflow-more")?.classList.remove("btn-active");

    // Update button label
    if (typeof _updateCuratedListBtnLabel === "function") _updateCuratedListBtnLabel();

    applyFilters();
}

function clearCuratedList() {
    if (!_curatedDomainList) return;
    _curatedDomainList = null;
    if (typeof _updateCuratedListBtnLabel === "function") _updateCuratedListBtnLabel();
    // Note: caller (categoryButtonClicked) handles reload via its own dt.reload()
    // If cleared without a category click, rebuild the query
    applyFilters();
}

///////////////////////////////////////////////////////////////////////////////
// Curated list dialog
///////////////////////////////////////////////////////////////////////////////

function openCuratedListDialog() {
    _buildCuratedListDialog();

    const modal    = document.getElementById("curated-list-modal");
    const textarea = document.getElementById("curated-list-textarea");

    // Pre-fill with current list if active
    if (textarea) {
        textarea.value = _curatedDomainList
            ? _curatedDomainList.split('|').join('\n')
            : '';
    }

    if (modal) {
        modal.style.display = "flex";
        setTimeout(() => textarea?.focus(), 50);
    }
}

function _closeCuratedListDialog() {
    const modal = document.getElementById("curated-list-modal");
    if (modal) modal.style.display = "none";
}

function _buildCuratedListDialog() {
    if (document.getElementById("curated-list-modal")) return;

    const modal = document.createElement("div");
    modal.id        = "curated-list-modal";
    modal.className = "curated-modal-overlay";
    modal.innerHTML = `
        <div class="curated-modal">
            <div class="curated-modal-header">
                <span class="curated-modal-title">Curated Domain List</span>
                <button class="curated-modal-close" id="curated-modal-close-btn">&#x2715;</button>
            </div>
            <div class="curated-modal-body">
                <p class="curated-modal-hint">Paste one domain per line. The portfolio will show only these domains. Active filters continue to apply on top of this list.</p>
                <textarea id="curated-list-textarea"
                          class="curated-list-textarea"
                          placeholder="domain1.com&#10;domain2.net&#10;domain3.org"
                          spellcheck="false"></textarea>
            </div>
            <div class="curated-modal-footer">
                <button id="curated-list-clear-btn" class="btn btn-sm btn-danger">Clear List</button>
                <div style="flex:1"></div>
                <button id="curated-list-cancel-btn" class="btn btn-sm btn-secondary">Cancel</button>
                <button id="curated-list-apply-btn" class="btn btn-sm btn-primary">Apply</button>
            </div>
        </div>`;
    document.body.appendChild(modal);

    // Close on overlay click
    modal.addEventListener("click", function(e) {
        if (e.target === modal) _closeCuratedListDialog();
    });

    document.getElementById("curated-modal-close-btn").addEventListener("click", _closeCuratedListDialog);
    document.getElementById("curated-list-cancel-btn").addEventListener("click", _closeCuratedListDialog);

    document.getElementById("curated-list-clear-btn").addEventListener("click", function() {
        document.getElementById("curated-list-textarea").value = "";
        _closeCuratedListDialog();
        clearCuratedList();
        // Restore All Domains active state
        const allBtn = document.querySelector(".btn-cat-selector[data-cid='1']");
        if (allBtn) allBtn.classList.add("btn-active");
    });

    document.getElementById("curated-list-apply-btn").addEventListener("click", function() {
        const text = document.getElementById("curated-list-textarea").value;
        _closeCuratedListDialog();
        setCuratedList(text);
    });

    document.addEventListener("keydown", function(e) {
        const modal = document.getElementById("curated-list-modal");
        if (e.key === "Escape" && modal && modal.style.display !== "none")
            _closeCuratedListDialog();
    });
}

function initPortfolioFilters(domainColumns) {
    const expirySelect          = document.getElementById("pf-filter-expiry");
    const registrarExpirySelect = document.getElementById("pf-filter-registrar-expiry");

    if (expirySelect)
        expirySelect.addEventListener("change", applyFilters);

    if (registrarExpirySelect)
        registrarExpirySelect.addEventListener("change", applyFilters);

    (window.PF_FILTERS || []).forEach(function(f) {
        // Validate columns — flag any that don't exist in domainColumns
        f.missingColumns = _getMissingColumns(f.columns, domainColumns);

        if (f.missingColumns.length > 0) {
            _markFilterInvalid(f);
            // Still wire up events — filter will just produce no clause
        }

        if (f.type === 'text') {
            const el = document.getElementById(f.id);
            if (!el) return;
            let _timer = null;
            el.addEventListener("input", function() {
                clearTimeout(_timer);
                _timer = setTimeout(applyFilters, 400);
            });

        } else if (f.type === 'float' || f.type === 'int') {
            const minEl = document.getElementById(f.id + "-min");
            const maxEl = document.getElementById(f.id + "-max");
            let _timer  = null;
            const handler = function() {
                clearTimeout(_timer);
                _timer = setTimeout(applyFilters, 400);
            };
            if (minEl) minEl.addEventListener("input", handler);
            if (maxEl) maxEl.addEventListener("input", handler);

        } else if (f.type === 'boolean') {
            const el = document.getElementById(f.id);
            if (el) el.addEventListener("change", applyFilters);
        }
    });
}

///////////////////////////////////////////////////////////////////////////////
// Column validation
///////////////////////////////////////////////////////////////////////////////

function _getMissingColumns(columns, domainColumns) {
    if (!domainColumns || Object.keys(domainColumns).length === 0) return [];
    return columns.filter(function(col) {
        // Strip d. prefix before checking against domainColumns keys
        const key = col.startsWith("d.") ? col.substring(2) : col;
        return !(key in domainColumns);
    });
}

function _markFilterInvalid(f) {
    // Find the filter field container — for range inputs use the -min element
    const el = document.getElementById(f.id)
            || document.getElementById(f.id + "-min");
    if (!el) return;

    const field = el.closest(".pf-filter-field");
    if (!field) return;

    // Find or create the label — insert pill after it
    let label = field.querySelector(".pf-filter-label");
    if (!label) {
        // Single-field group with suppressed label — create one
        label = document.createElement("span");
        label.className = "pf-filter-label";
        field.insertBefore(label, field.firstChild);
    }

    const pill = document.createElement("span");
    pill.className   = "pf-error-pill";
    pill.textContent = "!";
    pill.title       = "Column not found: " + f.missingColumns.join(", ");

    label.appendChild(pill);

    // Disable the input(s) so the filter cannot be accidentally used
    [f.id, f.id + "-min", f.id + "-max"].forEach(function(id) {
        const input = document.getElementById(id);
        if (input) input.disabled = true;
    });
}

///////////////////////////////////////////////////////////////////////////////
// Build and apply all active filters
///////////////////////////////////////////////////////////////////////////////

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

    const today  = dayjs().format("YYYY-MM-DD");
    const parts  = [];
    const params = [];

    // Curated list — MUST be first param (index 0) for server-side [in_domainlist] binding
    if (_curatedDomainList) {
        parts.push("[in_domainlist]");
        params.push("(" + _curatedDomainList + ")");
    }

    const expiryVal          = document.getElementById("pf-filter-expiry")?.value          || "";
    const registrarExpiryVal = document.getElementById("pf-filter-registrar-expiry")?.value || "";

    _buildExpiryClause("d.registry_expiry",  expiryVal,          today, parts, params);
    _buildExpiryClause("d.registrar_expiry", registrarExpiryVal, today, parts, params);

    (window.PF_FILTERS || []).forEach(function(f) {
        // Skip filters with missing columns
        if (f.missingColumns && f.missingColumns.length > 0) return;

        if (f.type === 'text') {
            const val = (document.getElementById(f.id)?.value || "").trim();
            _buildSearchClause(f.columns, val, parts, params, f.match);

        } else if (f.type === 'float' || f.type === 'int') {
            const min = (document.getElementById(f.id + "-min")?.value || "").trim();
            const max = (document.getElementById(f.id + "-max")?.value || "").trim();
            _buildRangeClause(f.columns[0], min, max, parts, params);

        } else if (f.type === 'boolean') {
            const val = document.getElementById(f.id)?.value || "";
            _buildBooleanClause(f.columns[0], val, parts, params);
        }
    });

    dt.setQuery(parts.join(" AND "), params.join("\n"));
    dt.reload();
}

///////////////////////////////////////////////////////////////////////////////
// Clause builders
///////////////////////////////////////////////////////////////////////////////

function _buildExpiryClause(column, value, today, parts, params) {
    switch (value) {
        case "30":
        case "60":
        case "90":
        case "120":
        case "365":
            parts.push(`${column} GE ? AND ${column} LE ?`);
            params.push(today);
            params.push(dayjs().add(parseInt(value), "day").format("YYYY-MM-DD"));
            break;
        case "expired":
            parts.push(`${column} LT ?`);
            params.push(today);
            break;
        case "expired_unknown":
            parts.push(`(${column} LT ? OR ${column} IS NULL)`);
            params.push(today);
            break;
        case "unknown":
            parts.push(`${column} IS NULL`);
            break;
        default:
            break;
    }
}

function _buildSearchClause(columns, value, parts, params, matchType) {
    if (!value) return;
    const cols   = Array.isArray(columns) ? columns : [columns];
    const exact  = matchType === 'exact';
    const clause = cols.map(col => exact ? `${col} EQ ?` : `${col} LIKE ?`).join(" OR ");
    cols.forEach(() => params.push(exact ? value : "%" + value + "%"));
    if (cols.length > 1)
        parts.push(`(${clause})`);
    else
        parts.push(clause);
}

function _buildRangeClause(column, min, max, parts, params) {
    if (min !== "" && max !== "") {
        parts.push(`${column} GE ? AND ${column} LE ?`);
        params.push(min);
        params.push(max);
    } else if (min !== "") {
        parts.push(`${column} GE ?`);
        params.push(min);
    } else if (max !== "") {
        parts.push(`${column} LE ?`);
        params.push(max);
    }
}

function _buildBooleanClause(column, value, parts, params) {
    if (value === "") return;
    parts.push(`${column} EQ ?`);
    params.push(value);
}