/**
 * TreeNav — hierarchical sidebar navigation for Targets and Sources tabs.
 * Replaces flat sub-tab bars with a collapsible tree that groups related items.
 *
 * Config format:
 *   [
 *     { key, titleKey, icon?, children: [{ key, titleKey, icon?, count, subTab?, sectionKey? }] },
 *     { key, titleKey, icon?, count }   // standalone leaf (no children)
 *   ]
 */

import { t } from './i18n.js';

const STORAGE_KEY = 'tree_nav_collapsed';

function _getCollapsedMap() {
    try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); }
    catch { return {}; }
}

function _saveCollapsed(key, collapsed) {
    const map = _getCollapsedMap();
    if (collapsed) map[key] = true;
    else delete map[key];
    localStorage.setItem(STORAGE_KEY, JSON.stringify(map));
}

export class TreeNav {
    /**
     * @param {string} containerId - ID of the nav element to render into
     * @param {object} opts
     * @param {function} opts.onSelect - callback(leafKey, leafData) when a leaf is clicked
     */
    constructor(containerId, { onSelect }) {
        this.containerId = containerId;
        this.onSelect = onSelect;
        this._items = [];
        this._leafMap = new Map();   // key → leaf config
        this._activeLeaf = null;
        this._extraHtml = '';
        this._observerSuppressed = false;
    }

    /** Temporarily suppress scroll-spy (e.g. during programmatic scroll). */
    suppressObserver(ms = 800) {
        this._observerSuppressed = true;
        clearTimeout(this._suppressTimer);
        this._suppressTimer = setTimeout(() => { this._observerSuppressed = false; }, ms);
    }

    /**
     * Full re-render of the tree.
     * @param {Array} items - tree structure (groups and/or standalone leaves)
     * @param {string} activeLeafKey
     */
    update(items, activeLeafKey) {
        this._items = items;
        this._activeLeaf = activeLeafKey;
        this._buildLeafMap();
        this._render();
    }

    /** Update only the counts without full re-render. */
    updateCounts(countMap) {
        const container = document.getElementById(this.containerId);
        if (!container) return;
        for (const [key, count] of Object.entries(countMap)) {
            const el = container.querySelector(`[data-tree-leaf="${key}"] .tree-count`);
            if (el) el.textContent = count;
            // Also update in-memory
            const leaf = this._leafMap.get(key);
            if (leaf) leaf.count = count;
        }
        // Update group counts
        container.querySelectorAll('[data-tree-group]').forEach(groupEl => {
            let total = 0;
            groupEl.querySelectorAll('.tree-leaf .tree-count').forEach(cnt => {
                total += parseInt(cnt.textContent, 10) || 0;
            });
            const groupCount = groupEl.querySelector('.tree-group-count');
            if (groupCount) groupCount.textContent = total;
        });
    }

    /** Set extra HTML appended at the bottom (expand/collapse buttons, etc.) */
    setExtraHtml(html) {
        this._extraHtml = html;
    }

    /** Highlight a specific leaf. */
    setActive(leafKey) {
        this._activeLeaf = leafKey;
        const container = document.getElementById(this.containerId);
        if (!container) return;
        container.querySelectorAll('.tree-leaf').forEach(el =>
            el.classList.toggle('active', el.dataset.treeLeaf === leafKey)
        );
        // Also highlight standalone leaves
        container.querySelectorAll('.tree-standalone').forEach(el =>
            el.classList.toggle('active', el.dataset.treeLeaf === leafKey)
        );
    }

    /** Get leaf data for a key. */
    getLeaf(key) {
        return this._leafMap.get(key);
    }

    /** Find the first leaf key whose subTab matches. */
    getLeafForSubTab(subTab) {
        for (const [key, leaf] of this._leafMap) {
            if ((leaf.subTab || key) === subTab) return key;
        }
        return null;
    }

    _buildLeafMap() {
        this._leafMap.clear();
        for (const item of this._items) {
            if (item.children) {
                for (const child of item.children) {
                    this._leafMap.set(child.key, child);
                }
            } else {
                this._leafMap.set(item.key, item);
            }
        }
    }

    _render() {
        const container = document.getElementById(this.containerId);
        if (!container) return;

        const collapsed = _getCollapsedMap();

        const html = this._items.map(item => {
            if (item.children) {
                return this._renderGroup(item, collapsed);
            }
            return this._renderStandalone(item);
        }).join('');

        container.innerHTML = html +
            (this._extraHtml ? `<div class="tree-extra">${this._extraHtml}</div>` : '');
        this._bindEvents(container);
    }

    _renderGroup(group, collapsed) {
        const isCollapsed = !!collapsed[group.key];
        const groupCount = group.children.reduce((sum, c) => sum + (c.count || 0), 0);

        return `
        <div class="tree-group" data-tree-group="${group.key}">
            <div class="tree-group-header" data-tree-group-toggle="${group.key}">
                <span class="tree-chevron${isCollapsed ? '' : ' open'}">&#9654;</span>
                ${group.icon ? `<span class="tree-node-icon">${group.icon}</span>` : ''}
                <span class="tree-node-title" data-i18n="${group.titleKey}">${t(group.titleKey)}</span>
                <span class="tree-group-count">${groupCount}</span>
            </div>
            <div class="tree-children${isCollapsed ? ' collapsed' : ''}">
                ${group.children.map(leaf => `
                    <div class="tree-leaf${leaf.key === this._activeLeaf ? ' active' : ''}" data-tree-leaf="${leaf.key}">
                        ${leaf.icon ? `<span class="tree-node-icon">${leaf.icon}</span>` : ''}
                        <span class="tree-node-title" data-i18n="${leaf.titleKey}">${t(leaf.titleKey)}</span>
                        <span class="tree-count">${leaf.count ?? 0}</span>
                    </div>
                `).join('')}
            </div>
        </div>`;
    }

    _renderStandalone(leaf) {
        return `
        <div class="tree-standalone${leaf.key === this._activeLeaf ? ' active' : ''}" data-tree-leaf="${leaf.key}">
            ${leaf.icon ? `<span class="tree-node-icon">${leaf.icon}</span>` : ''}
            <span class="tree-node-title" data-i18n="${leaf.titleKey}">${t(leaf.titleKey)}</span>
            <span class="tree-count">${leaf.count ?? 0}</span>
        </div>`;
    }

    /**
     * Start observing card-section elements so the active tree leaf
     * follows whichever section is currently visible on screen.
     * @param {string} contentId - ID of the scrollable content container
     * @param {Object<string,string>} [sectionMap] - optional { data-card-section → leafKey } override
     */
    observeSections(contentId, sectionMap) {
        this.stopObserving();
        const content = document.getElementById(contentId);
        if (!content) return;

        // Build sectionKey → leafKey mapping
        const sectionToLeaf = new Map();
        if (sectionMap) {
            for (const [sk, lk] of Object.entries(sectionMap)) sectionToLeaf.set(sk, lk);
        } else {
            for (const [key, leaf] of this._leafMap) {
                sectionToLeaf.set(leaf.sectionKey || key, key);
            }
        }

        const stickyTop = parseInt(getComputedStyle(document.documentElement)
            .getPropertyValue('--sticky-top')) || 90;

        // Track which sections are currently intersecting
        const visible = new Set();

        this._observer = new IntersectionObserver((entries) => {
            for (const entry of entries) {
                if (entry.isIntersecting) visible.add(entry.target);
                else visible.delete(entry.target);
            }
            if (this._observerSuppressed) return;
            // Read fresh rects to find the topmost visible section
            let bestEl = null;
            let bestTop = Infinity;
            for (const el of visible) {
                const top = el.getBoundingClientRect().top;
                if (top < bestTop) { bestTop = top; bestEl = el; }
            }
            if (bestEl) {
                const sectionKey = bestEl.dataset.cardSection;
                const leafKey = sectionToLeaf.get(sectionKey);
                if (leafKey && leafKey !== this._activeLeaf) {
                    this._activeLeaf = leafKey;
                    this.setActive(leafKey);
                }
            }
        }, {
            rootMargin: `-${stickyTop}px 0px -40% 0px`,
            threshold: 0
        });

        content.querySelectorAll('[data-card-section]').forEach(section => {
            if (sectionToLeaf.has(section.dataset.cardSection)) {
                this._observer.observe(section);
            }
        });
    }

    /** Stop the scroll-spy observer. */
    stopObserving() {
        if (this._observer) {
            this._observer.disconnect();
            this._observer = null;
        }
    }

    _bindEvents(container) {
        // Group header toggle
        container.querySelectorAll('.tree-group-header').forEach(header => {
            header.addEventListener('click', () => {
                const key = header.dataset.treeGroupToggle;
                const children = header.nextElementSibling;
                if (!children) return;
                const isNowCollapsed = children.classList.toggle('collapsed');
                const chevron = header.querySelector('.tree-chevron');
                if (chevron) chevron.classList.toggle('open', !isNowCollapsed);
                _saveCollapsed(key, isNowCollapsed);
            });
        });

        // Leaf click (both grouped and standalone)
        container.querySelectorAll('[data-tree-leaf]').forEach(el => {
            el.addEventListener('click', (e) => {
                // Don't trigger on group header clicks
                if (el.closest('.tree-group-header')) return;
                const key = el.dataset.treeLeaf;
                this.setActive(key);
                this.suppressObserver();
                if (this.onSelect) this.onSelect(key, this._leafMap.get(key));
            });
        });
    }
}
