// discover_main.js — Community overlap, shared interests, network graph
// Phase 5: Discover page — "What connects my community members?"

//
// ==================== COMMUNITIES TAB ====================
//

var discAllCommunities = [];
var discFilteredCommunities = [];
var discCommPage = 1;
var discCommLimit = 50;
var discSelectedCommunitySlug = null;
var discCachedProfiles = null;
var discTotalMemberCount = 0;

var discCommFilterState = {
    sortBy: 'shared_count_desc',
    searchTerm: '',
    minSharedMembers: 2
};

async function loadCommunitiesTab() {
    var data;
    try {
        var rawRes = await fetch('/api/communities/global');
        if (!rawRes.ok) {
            document.getElementById('disc-comm-list').innerHTML =
                '<div class="disc-empty">Error loading communities.</div>';
            return;
        }
        data = await rawRes.json();
    } catch (e) {
        document.getElementById('disc-comm-list').innerHTML =
            '<div class="disc-empty">Error loading communities.</div>';
        return;
    }

    discAllCommunities = Array.isArray(data) ? data : (data.communities || data.items || []);

    // Get total member count for percentage calculation
    try {
        var memberRes = await fetch('/api/user/filter', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ page: 1, limit: 1 })
        });
        if (memberRes.ok) {
            var memberData = await memberRes.json();
            discTotalMemberCount = memberData.total || memberData.count || 0;
        }
    } catch (e) {
        // Non-critical — percentage just won't show
    }

    applyDiscCommFilter();
}

function applyDiscCommFilter() {
    var results = discAllCommunities.slice();
    var search = discCommFilterState.searchTerm.toLowerCase();
    var minShared = discCommFilterState.minSharedMembers || 0;

    if (search) {
        results = results.filter(function(c) {
            var name = (c.name || '').toLowerCase();
            var slug = (c.slug || '').toLowerCase();
            return name.includes(search) || slug.includes(search);
        });
    }

    if (minShared > 0) {
        results = results.filter(function(c) {
            return (c.shared_user_count || 0) >= minShared;
        });
    }

    switch (discCommFilterState.sortBy) {
        case 'shared_count_desc':
            results.sort(function(a, b) { return (b.shared_user_count || 0) - (a.shared_user_count || 0); });
            break;
        case 'shared_count_asc':
            results.sort(function(a, b) { return (a.shared_user_count || 0) - (b.shared_user_count || 0); });
            break;
        case 'name_asc':
            results.sort(function(a, b) { return (a.name || a.slug || '').localeCompare(b.name || b.slug || ''); });
            break;
        case 'name_desc':
            results.sort(function(a, b) { return (b.name || b.slug || '').localeCompare(a.name || a.slug || ''); });
            break;
    }

    discFilteredCommunities = results;
    discCommPage = 1;
    document.getElementById('disc-comm-count').innerText = discFilteredCommunities.length.toString();
    renderCommunitiesList();
}

function renderCommunitiesList() {
    var listEl = document.getElementById('disc-comm-list');
    if (!listEl) return;

    var offset = (discCommPage - 1) * discCommLimit;
    var pageCommunities = discFilteredCommunities.slice(offset, offset + discCommLimit);

    var html = '';
    for (var i = 0; i < pageCommunities.length; i++) {
        html += renderDiscCommunityCard(pageCommunities[i]);
    }

    html += renderDiscCommPagination();

    if (discFilteredCommunities.length === 0) {
        html = '<div class="disc-empty">No communities found matching filters.</div>';
    }

    listEl.innerHTML = html;
}

function renderDiscCommunityCard(c) {
    var name = lib.escapeHtml(c.name || c.slug);
    var slug = lib.escapeHtml(c.slug);
    var sharedCount = c.shared_user_count || 0;
    var isSelected = c.slug === discSelectedCommunitySlug;

    // Parse description from about_data
    var description = '';
    var totalMembers = '';
    if (c.about_data) {
        try {
            var about = JSON.parse(c.about_data);
            if (about.description) description = about.description;
            if (about.memberCount) totalMembers = about.memberCount.toLocaleString() + ' total members';
        } catch(e) {}
    }

    var pctHtml = '';
    if (discTotalMemberCount > 0) {
        var pct = ((sharedCount / discTotalMemberCount) * 100).toFixed(1);
        pctHtml = ' (' + pct + '% of yours)';
    }

    var descHtml = description
        ? '<div class="disc-community-card__desc">' + lib.escapeHtml(description.substring(0, 150)) + '</div>'
        : '';

    var totalHtml = totalMembers
        ? '<span style="margin-left: var(--space-2);">' + totalMembers + '</span>'
        : '';

    return '<div class="disc-community-card' + (isSelected ? ' disc-community-card--selected' : '') + '"'
        + ' onclick="selectDiscCommunity(\'' + slug.replace(/'/g, "\\'") + '\')">'
        + '<div class="disc-community-card__name">' + name + '</div>'
        + descHtml
        + '<div class="disc-community-card__stats">'
        + '<span class="disc-community-card__badge">' + sharedCount + ' shared members' + pctHtml + '</span>'
        + totalHtml
        + '</div>'
        + '</div>';
}

function renderDiscCommPagination() {
    var total = discFilteredCommunities.length;
    var pages = Math.ceil(total / discCommLimit);
    if (pages <= 1) return '';
    var start = (discCommPage - 1) * discCommLimit + 1;
    var end = Math.min(discCommPage * discCommLimit, total);

    var html = '<div class="disc-pagination">';
    html += '<span>' + start + '-' + end + ' of ' + total + '</span>';
    html += '<button onclick="setDiscCommPage(1)"' + (discCommPage === 1 ? ' disabled' : '') + '>&laquo;</button>';
    html += '<button onclick="setDiscCommPage(' + (discCommPage - 1) + ')"' + (discCommPage === 1 ? ' disabled' : '') + '>&lsaquo;</button>';

    var pStart = Math.max(1, discCommPage - 2);
    var pEnd = Math.min(pages, discCommPage + 2);
    for (var i = pStart; i <= pEnd; i++) {
        html += '<button onclick="setDiscCommPage(' + i + ')"' + (i === discCommPage ? ' class="is-current"' : '') + '>' + i + '</button>';
    }

    html += '<button onclick="setDiscCommPage(' + (discCommPage + 1) + ')"' + (discCommPage === pages ? ' disabled' : '') + '>&rsaquo;</button>';
    html += '<button onclick="setDiscCommPage(' + pages + ')"' + (discCommPage === pages ? ' disabled' : '') + '>&raquo;</button>';
    html += '</div>';
    return html;
}

function setDiscCommPage(page) {
    discCommPage = page;
    renderCommunitiesList();
    document.getElementById('disc-comm-list').scrollTop = 0;
}

async function selectDiscCommunity(slug) {
    discSelectedCommunitySlug = slug;
    renderCommunitiesList(); // Re-render to highlight selected card

    var detailEl = document.getElementById('disc-comm-detail');
    if (!detailEl) return;

    detailEl.innerHTML = '<div class="disc-comm-detail-placeholder"><div style="font-size:0.9rem;color:var(--color-text-secondary);">Loading members...</div></div>';

    // Load profiles once and cache
    if (!discCachedProfiles) {
        try {
            var res = await get('/api/profile');
            if (res.ok) {
                discCachedProfiles = res.data;
            } else {
                detailEl.innerHTML = '<div class="disc-comm-detail-placeholder"><div style="font-size:0.9rem;color:var(--color-error);">Error loading profiles.</div></div>';
                return;
            }
        } catch (e) {
            detailEl.innerHTML = '<div class="disc-comm-detail-placeholder"><div style="font-size:0.9rem;color:var(--color-error);">Error loading profiles.</div></div>';
            return;
        }
    }

    // Find members who belong to BOTH the current community and the selected community
    var matchingMembers = [];
    var mySlug = (typeof currentCommunity !== 'undefined') ? currentCommunity : '';
    for (var i = 0; i < discCachedProfiles.length; i++) {
        var p = discCachedProfiles[i];
        if (!p.groups_member_of) continue;
        try {
            var groups = JSON.parse(p.groups_member_of);
            if (Array.isArray(groups)) {
                var inSelected = false;
                var inCurrent = !mySlug; // if no current slug, skip this check
                for (var j = 0; j < groups.length; j++) {
                    if (groups[j] === slug) inSelected = true;
                    if (groups[j] === mySlug) inCurrent = true;
                }
                if (inSelected && inCurrent) matchingMembers.push(p);
            }
        } catch(e) {}
    }

    var communityInfo = discAllCommunities.find(function(c) { return c.slug === slug; });
    var communityName = communityInfo ? (communityInfo.name || slug) : slug;

    var html = '<h3 style="margin-top:0;font-size:1.1rem;">' + lib.escapeHtml(communityName) + '</h3>';
    html += '<p style="font-size:0.85rem;color:var(--color-text-secondary);margin-bottom:var(--space-3);">'
        + matchingMembers.length + ' shared members</p>';

    if (matchingMembers.length === 0) {
        html += '<p style="color:var(--color-text-secondary);font-size:0.85rem;">No matching members found (profiles may not be fetched yet).</p>';
    } else {
        for (var k = 0; k < matchingMembers.length; k++) {
            var m = matchingMembers[k];
            var memberName = [m.first_name, m.last_name].filter(Boolean).join(' ') || m.name || m.skool_id;
            var imgHtml = '<img class="disc-comm-member-item__avatar" src="/api/image/' + m.skool_id + '" onerror="this.style.display=\'none\'">';
            html += '<div class="disc-comm-member-item">'
                + imgHtml
                + '<a class="disc-comm-member-item__name" href="/people.html?id=' + encodeURIComponent(m.skool_id) + '">'
                + lib.escapeHtml(memberName)
                + '</a>'
                + '</div>';
        }
    }

    detailEl.innerHTML = html;
}


//
// ==================== INTERESTS TAB ====================
//

var discInterestsLoaded = false;
var discAllLabels = [];
var discInterestSearchTerm = '';

async function loadInterestsTab() {
    discInterestsLoaded = true;
    var contentEl = document.getElementById('disc-interests-content');
    if (!contentEl) return;

    contentEl.innerHTML = '<div class="disc-interests-empty"><div style="font-size:0.9rem;">Loading interests...</div></div>';

    var data;
    try {
        var rawRes = await fetch('/api/community-labels');
        if (!rawRes.ok) {
            contentEl.innerHTML = '<div class="disc-interests-empty"><div style="font-size:0.9rem;color:var(--color-error);">Error loading interests.</div></div>';
            return;
        }
        data = await rawRes.json();
    } catch (e) {
        contentEl.innerHTML = '<div class="disc-interests-empty"><div style="font-size:0.9rem;color:var(--color-error);">Error loading interests.</div></div>';
        return;
    }

    discAllLabels = data.labels || [];
    renderInterestsContent();
}

function filterInterests(term) {
    discInterestSearchTerm = (term || '').toLowerCase();
    renderInterestsContent();
}

function renderInterestsContent() {
    var contentEl = document.getElementById('disc-interests-content');
    if (!contentEl) return;

    // Aggregate tags and categories across all community labels
    var tagCounts = {};   // tag -> count of communities that have this tag
    var catCounts = {};   // category -> count of communities

    for (var i = 0; i < discAllLabels.length; i++) {
        var label = discAllLabels[i];
        var tags = label.tags || [];
        var cats = label.categories || [];

        for (var t = 0; t < tags.length; t++) {
            var tag = tags[t];
            if (typeof tag === 'string' && tag.trim()) {
                var tagKey = tag.trim().toLowerCase();
                tagCounts[tagKey] = (tagCounts[tagKey] || 0) + 1;
            }
        }

        for (var c = 0; c < cats.length; c++) {
            var cat = cats[c];
            if (typeof cat === 'string' && cat.trim()) {
                var catKey = cat.trim().toLowerCase();
                catCounts[catKey] = (catCounts[catKey] || 0) + 1;
            }
        }
    }

    // Convert to sorted arrays
    var tagList = Object.keys(tagCounts).map(function(k) { return { name: k, count: tagCounts[k] }; });
    var catList = Object.keys(catCounts).map(function(k) { return { name: k, count: catCounts[k] }; });

    tagList.sort(function(a, b) { return b.count - a.count; });
    catList.sort(function(a, b) { return b.count - a.count; });

    // Apply search filter
    if (discInterestSearchTerm) {
        tagList = tagList.filter(function(t) { return t.name.includes(discInterestSearchTerm); });
        catList = catList.filter(function(c) { return c.name.includes(discInterestSearchTerm); });
    }

    var totalTags = tagList.length + catList.length;
    document.getElementById('disc-int-count').innerText = totalTags.toString();

    if (totalTags === 0 && discAllLabels.length === 0) {
        contentEl.innerHTML = '<div class="disc-interests-empty">'
            + '<div style="font-size:2.5rem;opacity:0.4;">&#127793;</div>'
            + '<div style="font-size:0.9rem;">No interest data available yet.</div>'
            + '<div style="font-size:0.85rem;color:var(--color-text-disabled);max-width:350px;">Interests are derived from community overlap analysis. Make sure community labels have been fetched.</div>'
            + '</div>';
        return;
    }

    var html = '';

    // Categories section
    if (catList.length > 0) {
        html += '<div class="disc-interests-section">';
        html += '<div class="disc-interests-section__title">Categories</div>';
        html += '<div class="disc-interests-grid">';
        for (var ci = 0; ci < catList.length; ci++) {
            html += renderInterestTag(catList[ci].name, catList[ci].count);
        }
        html += '</div></div>';
    }

    // Tags section
    if (tagList.length > 0) {
        html += '<div class="disc-interests-section">';
        html += '<div class="disc-interests-section__title">Tags</div>';
        html += '<div class="disc-interests-grid">';
        for (var ti = 0; ti < tagList.length; ti++) {
            html += renderInterestTag(tagList[ti].name, tagList[ti].count);
        }
        html += '</div></div>';
    }

    if (html === '') {
        html = '<div class="disc-interests-empty"><div style="font-size:0.9rem;">No interests match your search.</div></div>';
    }

    contentEl.innerHTML = html;
}

function renderInterestTag(name, count) {
    var displayName = lib.escapeHtml(name);
    var countLabel = count + ' ' + (count === 1 ? 'community' : 'communities');
    // Click navigates to people page with label filter
    return '<span class="disc-interest-tag" onclick="navigateToInterest(\'' + displayName.replace(/'/g, "\\'") + '\')" title="Click to view members with this interest">'
        + '#' + displayName
        + '<span class="disc-interest-tag__count">' + countLabel + '</span>'
        + '</span>';
}

function navigateToInterest(interest) {
    // Navigate to people page — the interest will be shown in the URL
    // People page can use this to pre-filter by label
    window.location.href = '/people.html?label=' + encodeURIComponent(interest);
}


//
// ==================== NETWORK TAB ====================
//

var discNetworkLoaded = false;
var discNetwork = null;
var discNetworkShowAll = false;
var discNetworkAllData = null; // cached graph data
var discNetworkHiddenRoles = {}; // roles currently hidden: { 'group-admin': true, ... }
var discNetworkVisData = null; // { allVisNodes, allVisEdges, nodeRoleMap }

async function loadNetworkTab() {
    discNetworkLoaded = true;
    var container = document.getElementById('disc-network-content');
    if (!container) return;

    container.innerHTML = '<div class="disc-network-empty"><div style="font-size:0.9rem;">Loading interaction data...</div></div>';

    // First get all member skool_ids
    var skoolIds = [];
    try {
        var memberRes = await fetch('/api/user/filter', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ page: 1, limit: 5000 })
        });
        if (memberRes.ok) {
            var memberData = await memberRes.json();
            var members = memberData.users || memberData.data || memberData.members || [];
            skoolIds = members.map(function(m) { return m.skool_id; });
        }
    } catch (e) {
        container.innerHTML = '<div class="disc-network-empty"><div style="font-size:0.9rem;color:var(--color-error);">Error loading members.</div></div>';
        return;
    }

    if (skoolIds.length === 0) {
        container.innerHTML = '<div class="disc-network-empty"><div style="font-size:2.5rem;opacity:0.4;">&#128279;</div><div style="font-size:0.9rem;">No members found.</div></div>';
        return;
    }

    // Load graph data
    var graphData;
    try {
        var graphRes = await post('/api/graph/interactions', {
            skool_ids: skoolIds,
            community: currentCommunity
        });
        if (!graphRes.ok) {
            container.innerHTML = '<div class="disc-network-empty"><div style="font-size:0.9rem;color:var(--color-error);">Error loading graph data.</div></div>';
            return;
        }
        graphData = graphRes.data;
    } catch (e) {
        container.innerHTML = '<div class="disc-network-empty"><div style="font-size:0.9rem;color:var(--color-error);">Error loading graph data.</div></div>';
        return;
    }

    // Cache the full data for toggle
    discNetworkAllData = graphData;
    discNetworkShowAll = false;

    renderNetworkGraph(container, graphData, false);
}

async function renderNetworkGraph(container, graphData, showAll) {
    var allNodes = graphData.nodes || [];
    var likeEdges = graphData.like_edges || [];
    var commentEdges = graphData.comment_edges || [];

    // Build set of connected node IDs (nodes that have at least one edge)
    var connectedIds = {};
    likeEdges.forEach(function(e) { connectedIds[e.source] = true; connectedIds[e.target] = true; });
    commentEdges.forEach(function(e) { connectedIds[e.source] = true; connectedIds[e.target] = true; });

    var connectedNodes = allNodes.filter(function(n) { return connectedIds[n.id]; });
    var isolatedCount = allNodes.length - connectedNodes.length;
    var nodes = showAll ? allNodes : connectedNodes;

    if (nodes.length === 0) {
        container.innerHTML = '<div class="disc-network-empty"><div style="font-size:2.5rem;opacity:0.4;">&#128279;</div><div style="font-size:0.9rem;">No interaction data found.</div></div>';
        return;
    }

    // Check if profile images should be hidden
    var hideVal = await ConfigEntry.get("hide_profile_images");
    var hideImages = hideVal === "1";

    container.innerHTML = '';

    // Stats bar + toggle button
    var topBar = document.createElement('div');
    topBar.style.cssText = 'position:absolute;top:8px;left:10px;display:flex;align-items:center;gap:10px;z-index:10;';

    var stats = document.createElement('small');
    stats.style.cssText = 'background:var(--color-bg-primary);padding:4px 8px;border-radius:var(--radius-sm);font-size:11px;';
    stats.textContent = nodes.length + ' Members | ' + likeEdges.length + ' Likes | ' + commentEdges.length + ' Comments';
    topBar.appendChild(stats);

    if (isolatedCount > 0) {
        var toggleBtn = document.createElement('button');
        toggleBtn.className = 'btn btn--sm btn--ghost';
        toggleBtn.style.cssText = 'font-size:11px;padding:3px 8px;';
        toggleBtn.textContent = showAll
            ? 'Hide ' + isolatedCount + ' unconnected'
            : 'Show all (+' + isolatedCount + ' unconnected)';
        toggleBtn.onclick = function() {
            discNetworkShowAll = !discNetworkShowAll;
            renderNetworkGraph(container, discNetworkAllData, discNetworkShowAll);
        };
        topBar.appendChild(toggleBtn);
    }

    // Role filter buttons
    var roleCounts = {};
    nodes.forEach(function(n) {
        var r = n.role || 'member';
        roleCounts[r] = (roleCounts[r] || 0) + 1;
    });

    var roleFilters = [
        { key: 'owner', label: 'Owner', color: '#9b59b6' },
        { key: 'group-admin', label: 'Admins', color: '#e74c3c' },
        { key: 'group-moderator', label: 'Moderators', color: '#f39c12' }
    ];
    roleFilters.forEach(function(rf) {
        var count = roleCounts[rf.key] || 0;
        if (count === 0) return;
        var btn = document.createElement('button');
        btn.className = 'btn btn--sm';
        var isHidden = discNetworkHiddenRoles[rf.key];
        btn.style.cssText = 'font-size:11px;padding:3px 8px;border:1px solid ' + rf.color + ';'
            + (isHidden
                ? 'background:transparent;color:var(--color-text-secondary);opacity:0.5;'
                : 'background:' + rf.color + '22;color:' + rf.color + ';');
        btn.textContent = (isHidden ? 'Show ' : 'Hide ') + rf.label + ' (' + count + ')';
        btn.onclick = function() {
            discNetworkHiddenRoles[rf.key] = !discNetworkHiddenRoles[rf.key];
            applyNetworkRoleFilter();
            // Update button appearance
            var hidden = discNetworkHiddenRoles[rf.key];
            btn.style.cssText = 'font-size:11px;padding:3px 8px;border:1px solid ' + rf.color + ';'
                + (hidden
                    ? 'background:transparent;color:var(--color-text-secondary);opacity:0.5;'
                    : 'background:' + rf.color + '22;color:' + rf.color + ';');
            btn.textContent = (hidden ? 'Show ' : 'Hide ') + rf.label + ' (' + count + ')';
        };
        topBar.appendChild(btn);
    });

    // Legend
    var legend = buildDiscNetworkLegend();

    // Graph container
    var graphWrap = document.createElement('div');
    graphWrap.className = 'disc-network-graph';
    graphWrap.style.cssText = 'position:relative;';
    graphWrap.appendChild(topBar);
    graphWrap.appendChild(legend);

    var graphDiv = document.createElement('div');
    graphDiv.style.cssText = 'width:100%;height:100%;';
    graphWrap.appendChild(graphDiv);

    container.appendChild(graphWrap);

    // Build vis nodes and edges, store role mapping for filtering
    var nodeRoleMap = {};
    var allVisNodes = nodes.map(function(n) {
        var role = n.role || 'member';
        nodeRoleMap[n.id] = role;
        var node = {
            id: n.id,
            label: n.name,
            size: 40,
            title: '@' + n.name + '\nRole: ' + role,
            group: role,
            borderWidth: 2
        };
        if (hideImages || !n.id) {
            node.shape = 'dot';
        } else {
            node.shape = 'circularImage';
            node.image = '/api/image/' + n.id;
        }
        return node;
    });

    var allVisEdges = likeEdges.map(function(e, i) {
        return {
            id: 'like-' + i,
            from: e.source, to: e.target, value: e.weight,
            color: { color: '#e74c3c', highlight: '#c0392b' },
            title: e.weight + ' Like' + (e.weight > 1 ? 's' : ''),
            arrows: 'to'
        };
    }).concat(commentEdges.map(function(e, i) {
        return {
            id: 'comment-' + i,
            from: e.source, to: e.target, value: e.weight,
            color: { color: '#3498db', highlight: '#2980b9' },
            title: e.weight + ' Comment' + (e.weight > 1 ? 's' : ''),
            arrows: 'to'
        };
    }));

    // Store for role filtering
    discNetworkVisData = {
        allVisNodes: allVisNodes,
        allVisEdges: allVisEdges,
        nodeRoleMap: nodeRoleMap
    };

    // Apply current role filters
    var filteredNodes = allVisNodes.filter(function(n) { return !discNetworkHiddenRoles[nodeRoleMap[n.id]]; });
    var visibleIds = {};
    filteredNodes.forEach(function(n) { visibleIds[n.id] = true; });
    var filteredEdges = allVisEdges.filter(function(e) { return visibleIds[e.from] && visibleIds[e.to]; });

    var nodesDS = new vis.DataSet(filteredNodes);
    var edgesDS = new vis.DataSet(filteredEdges);

    // Render vis.js network
    discNetwork = new vis.Network(graphDiv, { nodes: nodesDS, edges: edgesDS }, {
        nodes: {
            font: { size: 12 },
            scaling: { min: 20, max: 40 }
        },
        edges: {
            smooth: { type: 'continuous' },
            scaling: { min: 1, max: 8 }
        },
        physics: {
            enabled: true,
            stabilization: { iterations: 150, fit: true },
            barnesHut: {
                gravitationalConstant: -8000,
                springLength: 400,
                springConstant: 0.02,
                avoidOverlap: 0.8
            }
        },
        groups: {
            'owner': { color: { border: '#9b59b6', background: '#f4ecf7' } },
            'group-admin': { color: { border: '#e74c3c', background: '#fadbd8' } },
            'group-moderator': { color: { border: '#f39c12', background: '#fef5e7' } },
            'member': { color: { border: '#3498db', background: '#ebf5fb' } }
        }
    });

    // Stabilize once then disable physics and save positions
    discNetwork.once('stabilizationIterationsDone', function() {
        discNetwork.setOptions({ physics: { enabled: false } });
        // Save positions of all nodes so filtering can restore them
        discNetworkVisData.savedPositions = discNetwork.getPositions();
    });

    // Click on node → navigate to people profile
    discNetwork.on('click', function(params) {
        if (params.nodes && params.nodes.length > 0) {
            var nodeId = params.nodes[0];
            window.location.href = '/people.html?id=' + encodeURIComponent(nodeId);
        }
    });
}


function applyNetworkRoleFilter() {
    if (!discNetwork || !discNetworkVisData) return;
    var d = discNetworkVisData;

    // Update saved positions from current visible nodes (user may have dragged them)
    if (!d.savedPositions) d.savedPositions = {};
    var currentPos = discNetwork.getPositions();
    for (var id in currentPos) {
        d.savedPositions[id] = currentPos[id];
    }

    var filteredNodes = d.allVisNodes.filter(function(n) { return !discNetworkHiddenRoles[d.nodeRoleMap[n.id]]; });
    var visibleIds = {};
    filteredNodes.forEach(function(n) { visibleIds[n.id] = true; });
    var filteredEdges = d.allVisEdges.filter(function(e) { return visibleIds[e.from] && visibleIds[e.to]; });

    // Bake saved positions into nodes so they don't move after setData
    filteredNodes = filteredNodes.map(function(n) {
        var pos = d.savedPositions[n.id];
        if (pos) {
            var copy = {};
            for (var k in n) copy[k] = n[k];
            copy.x = pos.x;
            copy.y = pos.y;
            return copy;
        }
        return n;
    });

    discNetwork.setData({
        nodes: new vis.DataSet(filteredNodes),
        edges: new vis.DataSet(filteredEdges)
    });
    // Ensure physics stays disabled so positions are preserved
    discNetwork.setOptions({ physics: { enabled: false } });
}

function buildDiscNetworkLegend() {
    var legend = document.createElement('div');
    legend.style.cssText = 'position:absolute;top:5px;right:10px;background:var(--color-bg-primary);'
        + 'padding:6px 10px;border-radius:var(--radius-sm);z-index:10;font-size:11px;line-height:1.6;'
        + 'border:1px solid var(--color-border);pointer-events:none;';

    var edgeLine = function(color, label) {
        return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:10px;">'
            + '<span style="display:inline-block;width:18px;height:3px;background:' + color + ';border-radius:1px;"></span>'
            + '<span>' + label + '</span></span>';
    };
    var nodeCircle = function(borderColor, bgColor, label) {
        return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:10px;">'
            + '<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + bgColor + ';border:2px solid ' + borderColor + ';"></span>'
            + '<span>' + label + '</span></span>';
    };

    legend.innerHTML = '<div style="margin-bottom:2px;font-weight:600;color:var(--color-text-disabled);">Edges</div>'
        + '<div>' + edgeLine('#e74c3c', 'Likes') + edgeLine('#3498db', 'Comments') + '</div>'
        + '<div style="margin-top:4px;margin-bottom:2px;font-weight:600;color:var(--color-text-disabled);">Nodes</div>'
        + '<div>' + nodeCircle('#9b59b6', '#f4ecf7', 'Owner') + nodeCircle('#e74c3c', '#fadbd8', 'Admin') + nodeCircle('#f39c12', '#fef5e7', 'Moderator') + nodeCircle('#3498db', '#ebf5fb', 'Member') + '</div>';

    return legend;
}
