///////////////////////////////////////////////////////////////////////////////
// columns.js
// Self-contained column picker sidebar panel for the domain portfolio tool.
// Dependencies (globals): domainColumns, buildColumnDefsFromDomainColumns,
//                         DEFAULT_REQUESTED_COLUMNS, DataTableView,
//                         get_config_data, set_config_data, API_BASE_URL
///////////////////////////////////////////////////////////////////////////////

(function () {

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

    const PANEL_ID           = "cp-sidebar";
    const OVERLAY_ID         = "cp-overlay";
    const PANEL_OPEN_CLS     = "cp-open";
    const COLUMN_CONFIG_KEY  = "portfolio_tool_domain_columns";

    // Columns excluded from the panel entirely (mandatory + ctrl)
    const EXCLUDED_COLS = new Set(["dt_ser_num", "cb", "r_h_disp", "domain"]);

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

    // Working copy while the panel is open — discarded on cancel
    let _workingOrder = [];   // [{ colName, label, visible }]
    let _dragSrcIndex = null;
    let _filterText   = "";   // current search string

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

    window.initColumnPanel = function () {
        _buildDOM();
        _injectFooterButton();
        _attachEscapeKey();
    };

    ///////////////////////////////////////////////////////////////////////////
    // Escape key — close panel
    ///////////////////////////////////////////////////////////////////////////

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

    ///////////////////////////////////////////////////////////////////////////
    // Save column config to server
    ///////////////////////////////////////////////////////////////////////////

    async function saveColumnConfig(requestedColumns) {
        try {
            await set_config_data(COLUMN_CONFIG_KEY, requestedColumns, API_BASE_URL);
            console.log("[columns] Column config saved:", requestedColumns);
        } catch (err) {
            console.error("[columns] Failed to save column config:", err);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Load column config from server
    // Returns requestedColumns array, or null if not found / error
    ///////////////////////////////////////////////////////////////////////////

    window.loadColumnConfig = async function () {
        try {
            const raw = await get_config_data(COLUMN_CONFIG_KEY, API_BASE_URL);

            // get_config_data returns the raw stored string — parse if needed
            let parsed = raw;
            if (typeof raw === "string") {
                parsed = JSON.parse(raw);
            }

            // Validate: must be a non-empty array
            if (Array.isArray(parsed) && parsed.length > 0) {
                console.log("[columns] Loaded column config:", parsed);
                return parsed;
            }

            console.log("[columns] Saved config empty or invalid, using defaults.");
            return null;

        } catch (err) {
            // Key not found or parse error — fall back to defaults silently
            console.log("[columns] No saved column config found, using defaults.", err);
            return null;
        }
    };

    ///////////////////////////////////////////////////////////////////////////
    // DOM construction — runs once
    ///////////////////////////////////////////////////////////////////////////

    function _buildDOM() {
        // Overlay
        const overlay = document.createElement("div");
        overlay.id = OVERLAY_ID;
        overlay.addEventListener("click", closeColumnPanel);
        document.body.appendChild(overlay);

        // Sidebar panel
        const panel = document.createElement("div");
        panel.id = PANEL_ID;
        panel.innerHTML = `
            <div class="cp-header">
                <span class="cp-title">Columns</span>
                <button class="cp-close-btn" title="Close" aria-label="Close panel">&#x2715;</button>
            </div>
            <div class="cp-subheader">
                Drag rows to reorder &bull; Toggle to show/hide
            </div>
            <div class="cp-search-wrap">
                <input type="text" class="cp-search-input" id="cp-search" placeholder="Find column…" autocomplete="off">
                <button class="cp-search-clear" id="cp-search-clear" title="Clear" style="display:none">&#x2715;</button>
            </div>
            <div class="cp-list-wrap">
                <ul class="cp-list" id="cp-list"></ul>
            </div>
            <div class="cp-footer">
                <button class="cp-btn cp-btn-cancel">Cancel</button>
                <button class="cp-btn cp-btn-apply">Apply</button>
            </div>
        `;
        document.body.appendChild(panel);

        // Wire static buttons
        panel.querySelector(".cp-close-btn").addEventListener("click", closeColumnPanel);
        panel.querySelector(".cp-btn-cancel").addEventListener("click", closeColumnPanel);
        panel.querySelector(".cp-btn-apply").addEventListener("click", _applyColumns);

        // Wire filter input
        const searchInput = panel.querySelector("#cp-search");
        const searchClear = panel.querySelector("#cp-search-clear");

        searchInput.addEventListener("input", function () {
            _filterText = this.value.trim().toLowerCase();
            searchClear.style.display = _filterText ? "flex" : "none";
            _applyFilter();
        });

        searchClear.addEventListener("click", function () {
            searchInput.value = "";
            _filterText = "";
            this.style.display = "none";
            searchInput.focus();
            _applyFilter();
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // Footer button injection
    ///////////////////////////////////////////////////////////////////////////

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

        const btn = document.createElement("button");
        btn.className   = "btn btn-sm btn-secondary cp-trigger-btn";
        btn.id          = "cp-open-btn";
        btn.textContent = "⚙ Columns";
        btn.addEventListener("click", openColumnPanel);
        footer.appendChild(btn);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Open / close
    ///////////////////////////////////////////////////////////////////////////

    window.openColumnPanel = function () {
        _populateWorkingOrder();
        _filterText = "";
        const searchInput = document.getElementById("cp-search");
        const searchClear = document.getElementById("cp-search-clear");
        if (searchInput) { searchInput.value = ""; }
        if (searchClear) { searchClear.style.display = "none"; }
        _renderList();

        document.getElementById(OVERLAY_ID).classList.add(PANEL_OPEN_CLS);
        document.getElementById(PANEL_ID).classList.add(PANEL_OPEN_CLS);

        // Focus search input after slide-in
        setTimeout(() => { if (searchInput) searchInput.focus(); }, 300);
    };

    window.closeColumnPanel = function () {
        document.getElementById(OVERLAY_ID).classList.remove(PANEL_OPEN_CLS);
        document.getElementById(PANEL_ID).classList.remove(PANEL_OPEN_CLS);
        _workingOrder = [];
        _dragSrcIndex = null;
        _filterText   = "";
    };

    ///////////////////////////////////////////////////////////////////////////
    // Build working order from current active DataTableView columns
    ///////////////////////////////////////////////////////////////////////////

function _populateWorkingOrder() {
		_workingOrder = [];

		const dt         = DataTableView.getTable("dp-table");
		const activeCols = dt ? dt.options.columns : [];

		const NS_BACKING = new Set([
			"d.ns1","d.ns2","d.ns3","d.ns4",
			"d.ns5","d.ns6","d.ns7","d.ns8"
		]);
		const isNsSummaryActive = activeCols.some(
			c => c.name === "ns_summary" && c.hidden !== true
		);

		const activeNames        = new Set();
		const activeVirtualNames = new Set();

		activeCols.forEach(col => {
			if (col.hidden === true) return;

			const bare = col.name.startsWith("d.") ? col.name.substring(2) : col.name;
			if (EXCLUDED_COLS.has(bare)) return;

			if (col.virtual) {
				if (col.always_on) return;
				activeVirtualNames.add(col.name);
				_workingOrder.push({
					colName  : col.name,
					label    : col.label || col.name,
					visible  : true,
					isVirtual: true,
				});
				return;
			}

			const meta = domainColumns[bare];
			if (!meta) return;
			meta.gridview = meta?.gridview === 1 || meta?.gridview === "1" ? 1 : 0;
			if (meta.gridview !== 1) return;
			activeNames.add(bare);
			_workingOrder.push({
				colName  : bare,
				label    : meta.label || bare,
				visible  : true,
				isVirtual: false,
			});
		});

		Object.entries(domainColumns).forEach(([bare, meta]) => {
			meta.gridview = meta?.gridview === 1 || meta?.gridview === "1" ? 1 : 0;
			if (EXCLUDED_COLS.has(bare))  return;
			if (activeNames.has(bare))    return;
			if (meta.gridview !== 1)      return;
			// When ns_summary is active, hidden ns backing columns land here —
			// they appear in the picker as unchecked (not user-visible)
			_workingOrder.push({
				colName  : bare,
				label    : meta.label || bare,
				visible  : false,
				isVirtual: false,
			});
		});

		const knownUserVirtuals = (typeof KNOWN_USER_VIRTUAL_COLUMNS !== "undefined")
			? KNOWN_USER_VIRTUAL_COLUMNS
			: [{ name: "member_of", label: "Member Of" }];

		knownUserVirtuals.forEach(def => {
			if (activeVirtualNames.has(def.name)) return;
			_workingOrder.push({
				colName  : def.name,
				label    : def.label || def.name,
				visible  : false,
				isVirtual: true,
			});
		});
	}
	
    ///////////////////////////////////////////////////////////////////////////
    // Render the list from _workingOrder
    ///////////////////////////////////////////////////////////////////////////

    function _renderList() {
        const ul = document.getElementById("cp-list");
        ul.innerHTML = "";

        const isFiltering = _filterText.length > 0;

        _workingOrder.forEach((item, idx) => {
            const li = document.createElement("li");
            li.className     = "cp-item" + (item.visible ? " cp-visible" : "");
            li.draggable     = !isFiltering; // disable drag while filtering
            li.dataset.index = idx;

            if (isFiltering) {
                li.classList.add("cp-no-drag");
            }

            li.innerHTML = `
                <span class="cp-drag-handle" title="${isFiltering ? 'Clear filter to reorder' : 'Drag to reorder'}">&#8597;</span>
                <label class="cp-item-label">
                    <input type="checkbox" class="cp-checkbox" ${item.visible ? "checked" : ""}>
                    <span class="cp-col-names">
                        <span class="cp-col-label">${_escHtml(item.label)}</span>
                        <span class="cp-col-key">${_escHtml(item.colName)}</span>
                    </span>
                </label>
            `;

            // Checkbox toggle
            li.querySelector(".cp-checkbox").addEventListener("change", function () {
                _workingOrder[idx].visible = this.checked;
                li.classList.toggle("cp-visible", this.checked);
            });

            // Drag events — only attached when not filtering
            if (!isFiltering) {
                li.addEventListener("dragstart", _onDragStart);
                li.addEventListener("dragover",  _onDragOver);
                li.addEventListener("dragleave", _onDragLeave);
                li.addEventListener("drop",      _onDrop);
                li.addEventListener("dragend",   _onDragEnd);
            }

            ul.appendChild(li);
        });

        // Apply visibility filter after rendering
        if (isFiltering) {
            _applyFilter();
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Filter — show/hide items without touching _workingOrder
    ///////////////////////////////////////////////////////////////////////////

    function _applyFilter() {
        const items = document.querySelectorAll("#cp-list .cp-item");
        const isFiltering = _filterText.length > 0;

        items.forEach(li => {
            const idx   = parseInt(li.dataset.index, 10);
            const item  = _workingOrder[idx];
            const label = (item.label   || "").toLowerCase();
            const key   = (item.colName || "").toLowerCase();

            const matches = !isFiltering || label.includes(_filterText) || key.includes(_filterText);
            li.style.display  = matches ? "" : "none";

            // Update draggable state in case filter was toggled without re-render
            li.draggable = !isFiltering;
            li.classList.toggle("cp-no-drag", isFiltering);
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // Drag-and-drop reordering
    ///////////////////////////////////////////////////////////////////////////

    function _onDragStart(e) {
        _dragSrcIndex = parseInt(this.dataset.index, 10);
        this.classList.add("cp-dragging");
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("text/plain", _dragSrcIndex);
    }

    function _onDragOver(e) {
        e.preventDefault();
        e.dataTransfer.dropEffect = "move";
        document.querySelectorAll(".cp-item").forEach(el => el.classList.remove("cp-drag-over"));
        this.classList.add("cp-drag-over");
    }

    function _onDragLeave() {
        this.classList.remove("cp-drag-over");
    }

    function _onDrop(e) {
        e.preventDefault();
        const targetIndex = parseInt(this.dataset.index, 10);
        if (_dragSrcIndex === null || _dragSrcIndex === targetIndex) return;

        const moved = _workingOrder.splice(_dragSrcIndex, 1)[0];
        _workingOrder.splice(targetIndex, 0, moved);

        _dragSrcIndex = null;
        _renderList(); // re-render with corrected indices
    }

    function _onDragEnd() {
        document.querySelectorAll(".cp-item").forEach(el => {
            el.classList.remove("cp-dragging", "cp-drag-over");
        });
        _dragSrcIndex = null;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Apply — build column defs and push to DataTableView
    ///////////////////////////////////////////////////////////////////////////

    function _applyColumns() {
        // Always prepend ctrl
        const requestedColumns = ["ctrl"];

        _workingOrder
            .filter(item => item.visible)
            .forEach(item => {
                if (item.isVirtual) {
                    // Virtual columns use bare name (e.g. "member_of")
                    requestedColumns.push(item.colName);
                } else {
                    // Regular API columns use "d." prefix
                    requestedColumns.push(`d.${item.colName}`);
                }
            });

        // Build full column defs and apply to live table
        const newColDefs = buildColumnDefsFromDomainColumns(domainColumns, requestedColumns);
		// Apply editable fields so editing works after column picker changes
		if (typeof applyEditableFields === 'function') {
			applyEditableFields(newColDefs, domainColumns);
		}
        const dt = DataTableView.getTable("dp-table");
        if (dt) {
            dt.setColumns(newColDefs);
        }

        // Persist to server
        saveColumnConfig(requestedColumns);

        closeColumnPanel();
    }

    ///////////////////////////////////////////////////////////////////////////
    // Utility
    ///////////////////////////////////////////////////////////////////////////

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

})();