// people_list.js — People list logic (replaces members_members.js for the People page)
// Loads /api/user/filter and renders person cards with churn indicators.

// ========== State ==========

var people = [];
var peopleProfiles = new Map();
var peopleActivityCounts = {};
var peoplePage = 1;
const peoplePageSize = 200;
var selectedSkoolId = null;
var peopleChurnScores = new Map(); // skool_id -> score
var peopleMemberInterests = new Map(); // skool_id -> [{category, weight, count}]
var peopleLabelsLoaded = false;
var peopleFilterOpen = false;
var peopleLabelFilter = ''; // client-side label/category filter (from ?label= URL param)

var peopleFilterState = {
    sortBy: 'last_active_desc',
    searchTerm: '',
    filters: {
        last_active: { from: 1, to: 3 },
        joined: { from: 1, to: 1825 },
        member_role: ['member'],
        community_slugs: null,
        community_owner: null,
        hardcore_status: null,
        mrr_status: null,
        liftoff_status: null,
        diamond_status: null,
        fire_status: null,
        goat_status: null,
        star_status: null,
        activity_period: { from: 1, to: 1825 },
        count_posts: { from: 0, to: 1000 },
        count_comments: { from: 0, to: 1000 },
        count_likes: { from: 0, to: 1000 }
    }
};

// ========== Filter persistence (sessionStorage) ==========

function savePeopleFilters() {
    try {
        sessionStorage.setItem('pp-filter-state', JSON.stringify({
            sortBy: peopleFilterState.sortBy,
            searchTerm: peopleFilterState.searchTerm,
            filters: peopleFilterState.filters
        }));
    } catch (e) { /* ignore quota errors */ }
}

function restorePeopleFilters() {
    try {
        const saved = sessionStorage.getItem('pp-filter-state');
        if (!saved) return false;
        const parsed = JSON.parse(saved);
        if (parsed.sortBy) peopleFilterState.sortBy = parsed.sortBy;
        if (parsed.searchTerm != null) peopleFilterState.searchTerm = parsed.searchTerm;
        if (parsed.filters) peopleFilterState.filters = parsed.filters;
        return true;
    } catch (e) { return false; }
}

// ========== Filter helpers (mirror members.html logic) ==========

const peopleDaysSliderSteps = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,30,60,90,120,150,180,365,730,1095,1460,1825];
const peopleCountSliderSteps = [0,1,2,3,4,5,6,7,8,9,10,15,20,25,30,40,50,75,100,150,200,300,500,1000];

function peopleDaysToStepIndex(days) {
    let best = 0;
    for (let i = 0; i < peopleDaysSliderSteps.length; i++) {
        if (Math.abs(peopleDaysSliderSteps[i] - days) <= Math.abs(peopleDaysSliderSteps[best] - days)) best = i;
    }
    return best;
}

function peopleCountToStepIndex(count) {
    let best = 0;
    for (let i = 0; i < peopleCountSliderSteps.length; i++) {
        if (Math.abs(peopleCountSliderSteps[i] - count) <= Math.abs(peopleCountSliderSteps[best] - count)) best = i;
    }
    return best;
}

function peopleDaysAgoToDate(days) {
    let d = new Date();
    d.setHours(0, 0, 0, 0);
    d.setDate(d.getDate() - (days - 1));
    return d;
}

function peopleDateToISO(date) {
    return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0');
}

function peopleISOToDays(isoStr) {
    const parts = isoStr.split('-');
    const date = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
    const now = new Date();
    now.setHours(0, 0, 0, 0);
    date.setHours(0, 0, 0, 0);
    return Math.max(1, Math.round((now - date) / (1000 * 60 * 60 * 24)) + 1);
}

const peopleTodayISO = peopleDateToISO(new Date());
const peopleOldestISO = peopleDateToISO(peopleDaysAgoToDate(1825));

// ========== Sort & Filter API ==========

function setPeopleSort(value) {
    peopleFilterState.sortBy = value;
    loadPeople();
}

function applyPeopleFilters() {
    peoplePage = 1;
    savePeopleFilters();
    loadPeople();
}

function clearPeopleFilters() {
    sessionStorage.removeItem('pp-filter-state');
    peopleFilterState.sortBy = 'last_active_desc';
    peopleFilterState.searchTerm = '';
    peopleFilterState.filters = {
        last_active: { from: 1, to: 3 },
        joined: { from: 1, to: 1825 },
        member_role: ['member'],
        community_slugs: null,
        community_owner: null,
        hardcore_status: null,
        mrr_status: null,
        liftoff_status: null,
        diamond_status: null,
        fire_status: null,
        goat_status: null,
        star_status: null,
        activity_period: { from: 1, to: 1825 },
        count_posts: { from: 0, to: 1000 },
        count_comments: { from: 0, to: 1000 },
        count_likes: { from: 0, to: 1000 }
    };
    const searchInput = document.getElementById('people-search');
    if (searchInput) searchInput.value = '';
    const sortSelect = document.getElementById('people-sort');
    if (sortSelect) sortSelect.value = 'last_active_desc';
    renderPeopleFilterPanel();
    loadPeople();
}

// ========== Range filter helpers ==========

function setPeopleRangeValue(key, side, days) {
    if (!peopleFilterState.filters[key]) return;
    days = Math.max(1, Math.min(1825, days));
    if (side === 'from') {
        peopleFilterState.filters[key].from = days;
        if (peopleFilterState.filters[key].to < days) peopleFilterState.filters[key].to = days;
    } else {
        peopleFilterState.filters[key].to = days;
        if (peopleFilterState.filters[key].from > days) peopleFilterState.filters[key].from = days;
    }
}

function setPeopleCountValue(key, side, count) {
    if (!peopleFilterState.filters[key]) return;
    count = Math.max(0, Math.min(1000, count));
    if (side === 'from') {
        peopleFilterState.filters[key].from = count;
        if (peopleFilterState.filters[key].to < count) peopleFilterState.filters[key].to = count;
    } else {
        peopleFilterState.filters[key].to = count;
        if (peopleFilterState.filters[key].from > count) peopleFilterState.filters[key].from = count;
    }
}

function handlePeopleDateInput(key, side, isoValue) {
    if (!isoValue) return;
    const days = peopleISOToDays(isoValue);
    if (days < 1) return;
    setPeopleRangeValue(key, side, Math.min(1825, days));
    syncPeopleRangeUI(key);
}

function handlePeopleDualSliderInput(key, side, stepIdx) {
    const days = peopleDaysSliderSteps[stepIdx];
    const elem = document.getElementById('pf_input_' + key + '_' + side);
    if (elem) elem.value = peopleDateToISO(peopleDaysAgoToDate(days));
}

function handlePeopleDualSliderChange(key, side, stepIdx) {
    const days = peopleDaysSliderSteps[stepIdx];
    setPeopleRangeValue(key, side, days);
    syncPeopleRangeUI(key);
}

function handlePeopleCountSliderInput(key, side, stepIdx) {
    const count = peopleCountSliderSteps[stepIdx];
    const elem = document.getElementById('pf_input_' + key + '_' + side);
    if (elem) elem.innerText = count;
}

function handlePeopleCountSliderChange(key, side, stepIdx) {
    const count = peopleCountSliderSteps[stepIdx];
    setPeopleCountValue(key, side, count);
    syncPeopleCountUI(key);
}

function syncPeopleRangeUI(key) {
    const f = peopleFilterState.filters[key];
    if (!f) return;
    const fromSlider = document.getElementById('pf_slider_' + key + '_from');
    const toSlider = document.getElementById('pf_slider_' + key + '_to');
    if (fromSlider) fromSlider.value = peopleDaysToStepIndex(f.from);
    if (toSlider) toSlider.value = peopleDaysToStepIndex(f.to);
    const fromDisplay = document.getElementById('pf_input_' + key + '_from');
    const toDisplay = document.getElementById('pf_input_' + key + '_to');
    if (fromDisplay) fromDisplay.value = peopleDateToISO(peopleDaysAgoToDate(f.from));
    if (toDisplay) toDisplay.value = peopleDateToISO(peopleDaysAgoToDate(f.to));
}

function syncPeopleCountUI(key) {
    const f = peopleFilterState.filters[key];
    if (!f) return;
    const fromSlider = document.getElementById('pf_slider_' + key + '_from');
    const toSlider = document.getElementById('pf_slider_' + key + '_to');
    if (fromSlider) fromSlider.value = peopleCountToStepIndex(f.from);
    if (toSlider) toSlider.value = peopleCountToStepIndex(f.to);
    const fromDisplay = document.getElementById('pf_input_' + key + '_from');
    const toDisplay = document.getElementById('pf_input_' + key + '_to');
    if (fromDisplay) fromDisplay.innerText = f.from;
    if (toDisplay) toDisplay.innerText = f.to;
}

// ========== Toggle helpers ==========

function togglePeopleRole(role) {
    if (!Array.isArray(peopleFilterState.filters.member_role)) return;
    const idx = peopleFilterState.filters.member_role.indexOf(role);
    if (idx >= 0) peopleFilterState.filters.member_role.splice(idx, 1);
    else peopleFilterState.filters.member_role.push(role);
}

function togglePeopleCommunityOwner(type) {
    if (!Array.isArray(peopleFilterState.filters.community_owner)) return;
    const idx = peopleFilterState.filters.community_owner.indexOf(type);
    if (idx >= 0) peopleFilterState.filters.community_owner.splice(idx, 1);
    else peopleFilterState.filters.community_owner.push(type);
}

function togglePeopleStatus(key) {
    if (peopleFilterState.filters[key] === null) peopleFilterState.filters[key] = true;
    else peopleFilterState.filters[key] = !peopleFilterState.filters[key];
}

var peopleCachedCommunityList = null;

function togglePeopleCommunitySlug(slug) {
    if (!Array.isArray(peopleFilterState.filters.community_slugs)) return;
    const idx = peopleFilterState.filters.community_slugs.indexOf(slug);
    if (idx >= 0) peopleFilterState.filters.community_slugs.splice(idx, 1);
    else peopleFilterState.filters.community_slugs.push(slug);
}

async function loadPeopleCommunityList() {
    if (peopleCachedCommunityList) {
        renderPeopleFilterPanel();
        return;
    }
    try {
        const res = await fetch('/api/crud/othercommunity');
        if (res.ok) {
            peopleCachedCommunityList = await res.json();
            renderPeopleFilterPanel();
        }
    } catch (e) {
        console.error('[People] Failed to load community list:', e);
    }
}

function enablePeopleFilter(key) {
    if (key === 'community_slugs') {
        peopleFilterState.filters[key] = [];
        loadPeopleCommunityList();
    } else if (key === 'community_owner') {
        peopleFilterState.filters[key] = [];
    }
    renderPeopleFilterPanel();
}

// ========== Quicklinks (reuse entity) ==========

var peopleQuicklinks = [];
var activePeopleQuicklinkId = null;

async function loadPeopleQuicklinks() {
    try {
        const all = await Quicklink.all();
        const cc = await ConfigEntry.get("current_community");
        peopleQuicklinks = all
            .filter(q => !q.community_slug || q.community_slug === cc)
            .sort((a, b) => a.sort_order - b.sort_order);
        renderPeopleFilterPanel();
    } catch (e) {
        console.warn('[People] Failed to load quicklinks:', e);
    }
}

function onPeopleQuicklinkChange(value) {
    if (value === '__default__') {
        activePeopleQuicklinkId = null;
        clearPeopleFilters();
        return;
    }
    const id = parseInt(value);
    if (isNaN(id)) return;
    applyPeopleQuicklink(id);
}

function applyPeopleQuicklink(id) {
    const ql = peopleQuicklinks.find(q => q.id === id);
    if (!ql) return;
    try {
        const saved = JSON.parse(ql.filter_state);
        peopleFilterState.sortBy = saved.sortBy || 'last_active_desc';
        peopleFilterState.searchTerm = saved.searchTerm || '';
        peopleFilterState.filters = saved.filters || {};
        const searchInput = document.getElementById('people-search');
        if (searchInput) searchInput.value = peopleFilterState.searchTerm;
        const sortSelect = document.getElementById('people-sort');
        if (sortSelect) sortSelect.value = peopleFilterState.sortBy;
        activePeopleQuicklinkId = id;
        renderPeopleFilterPanel();
        loadPeople();
    } catch (e) {
        console.error('[People] Failed to apply quicklink:', e);
    }
}

async function savePeopleQuicklink() {
    const name = prompt('Name for this saved filter:');
    if (!name || !name.trim()) return;
    const cc = await ConfigEntry.get("current_community");
    const ql = new Quicklink({
        name: name.trim(),
        community_slug: cc,
        filter_state: JSON.stringify(peopleFilterState),
        sort_order: peopleQuicklinks.length
    });
    const res = await ql.save();
    if (res.ok) {
        activePeopleQuicklinkId = ql.id;
        await loadPeopleQuicklinks();
    }
}

async function updatePeopleQuicklink() {
    if (!activePeopleQuicklinkId) return;
    const ql = peopleQuicklinks.find(q => q.id === activePeopleQuicklinkId);
    if (!ql) return;
    ql.filter_state = JSON.stringify(peopleFilterState);
    await ql.save();
    await loadPeopleQuicklinks();
}

async function deletePeopleQuicklink() {
    if (!activePeopleQuicklinkId) return;
    const ql = peopleQuicklinks.find(q => q.id === activePeopleQuicklinkId);
    if (!ql) return;
    if (!confirm('Delete saved filter "' + ql.name + '"?')) return;
    await ql.delete();
    activePeopleQuicklinkId = null;
    await loadPeopleQuicklinks();
}

async function renamePeopleQuicklink() {
    if (!activePeopleQuicklinkId) return;
    const ql = peopleQuicklinks.find(q => q.id === activePeopleQuicklinkId);
    if (!ql) return;
    const newName = prompt('New name:', ql.name);
    if (!newName || !newName.trim() || newName.trim() === ql.name) return;
    ql.name = newName.trim();
    await ql.save();
    await loadPeopleQuicklinks();
}

// ========== Filter panel rendering ==========

function toggleFilterPanel(forceOpen) {
    const panel = document.getElementById('people-filter-panel');
    if (!panel) return;
    if (forceOpen === true) {
        peopleFilterOpen = true;
    } else {
        peopleFilterOpen = !peopleFilterOpen;
    }
    panel.classList.toggle('is-open', peopleFilterOpen);
    ConfigEntry.set("people.filter_expanded", peopleFilterOpen.toString()).then();
    const btn = document.getElementById('people-filter-toggle');
    if (btn) btn.textContent = peopleFilterOpen ? 'Hide Filter' : 'Filter';
}

const peopleFilterHelpTexts = {
    quicklinks: 'Saved filter configurations',
    last_active: 'Last seen online in this date range',
    joined: 'Community join date range',
    member_role: 'Filter by community role',
    community_owner: 'Owns a Skool community',
    status: 'Skool profile badges',
    activity_period: 'Only count posts, comments and likes within this time range',
    count_posts: 'Total number of posts',
    count_comments: 'Total number of comments',
    count_likes: 'Total number of likes given',
};

// Track which filter sections are open (persisted in-memory)
var peopleFilterSectionsOpen = { quicklinks: true };

function togglePeopleFilterSection(key) {
    peopleFilterSectionsOpen[key] = !peopleFilterSectionsOpen[key];
    const row = document.querySelector('[data-filter-key="' + key + '"]');
    if (row) row.classList.toggle('is-open', !!peopleFilterSectionsOpen[key]);
}

function formatPeopleDateRangeSummary(v, d) {
    // Returns short readable summary for a days-based date range filter
    if (!v) return '';
    if (v.from === d.from && v.to === d.to) return '';
    // Format each bound as a short date (DD.MM)
    var fromDate = peopleDaysAgoToDate(v.from);
    var toDate = peopleDaysAgoToDate(v.to);
    var fd = String(fromDate.getDate()).padStart(2,'0') + '.' + String(fromDate.getMonth()+1).padStart(2,'0');
    var td = String(toDate.getDate()).padStart(2,'0') + '.' + String(toDate.getMonth()+1).padStart(2,'0');
    if (fd === td) return fd;
    return td + '–' + fd;
}

function getPeopleFilterSummary(key) {
    var f = peopleFilterState.filters;
    var defaults = { last_active: {from:1,to:3}, joined: {from:1,to:1825}, activity_period: {from:1,to:1825}, count_posts: {from:0,to:1000}, count_comments: {from:0,to:1000}, count_likes: {from:0,to:1000} };
    if (key === 'last_active' || key === 'joined') {
        return formatPeopleDateRangeSummary(f[key], defaults[key]);
    }
    if (key === 'activity_period') {
        // Combined: activity window + counts
        var parts = [];
        var ap = f.activity_period;
        var apSummary = formatPeopleDateRangeSummary(ap, defaults.activity_period);
        if (apSummary) parts.push(apSummary);
        ['count_posts','count_comments','count_likes'].forEach(function(ck) {
            var cv = f[ck], cd = defaults[ck];
            if (cv && !(cv.from === cd.from && cv.to === cd.to)) {
                var label = ck.split('_')[1][0].toUpperCase();
                parts.push(label + ':' + cv.from + '-' + cv.to);
            }
        });
        return parts.join(', ');
    }
    if (key === 'count_posts' || key === 'count_comments' || key === 'count_likes') {
        var d = defaults[key], v = f[key];
        if (!v) return '';
        if (v.from === d.from && v.to === d.to) return '';
        return v.from + '–' + v.to;
    }
    if (key === 'member_role') {
        if (!Array.isArray(f.member_role) || f.member_role.length === 4) return '';
        return f.member_role.length + '/4';
    }
    if (key === 'community_owner') {
        if (!Array.isArray(f.community_owner) || f.community_owner.length === 0) return '';
        return f.community_owner.map(function(v){ return v === 'owner' ? 'Yes' : 'No'; }).join(', ');
    }
    if (key === 'status') {
        var active = ['hardcore_status','mrr_status','liftoff_status','diamond_status','fire_status','goat_status','star_status'].filter(function(k){ return f[k] === true; });
        if (active.length === 0) return '';
        return active.length + ' active';
    }
    return '';
}

function isPeopleFilterActive(key) {
    return getPeopleFilterSummary(key) !== '';
}

function buildPeopleFilterRow(key, label, controlsHtml, opts) {
    opts = opts || {};
    var tip = peopleFilterHelpTexts[key] || '';
    var helpHtml = tip
        ? ' <span class="ck-help-icon">?<span class="ck-help-tip">' + tip + '</span></span>'
        : '';
    var isOpen = !!peopleFilterSectionsOpen[key];
    var openClass = isOpen ? ' is-open' : '';
    var summary = getPeopleFilterSummary(key);
    var activeDot = summary ? '<span class="filter-active-dot"></span>' : '';
    var summaryHtml = summary ? '<span class="filter-row-summary">' + summary + '</span>' : '';
    var chevron = '<span class="filter-row-chevron">&#9654;</span>';
    return '<div class="filter-row filter-row-enabled' + openClass + '" data-filter-key="' + key + '">' +
        '<div class="filter-row-header" onclick="togglePeopleFilterSection(\'' + key + '\')">' +
        activeDot + '<span class="filter-row-label">' + label + '</span>' + helpHtml + summaryHtml + chevron +
        '</div><div class="filter-row-controls">' + controlsHtml + '</div></div>';
}

var peopleDatePresetDefaults = { last_active: {from:1,to:3}, joined: {from:1,to:1825}, activity_period: {from:1,to:1825} };

function applyPeopleDatePreset(key, fromDays, toDays) {
    var f = peopleFilterState.filters[key];
    // Toggle off: if already matching this preset, reset to default
    if (f && f.from === fromDays && f.to === toDays) {
        var def = peopleDatePresetDefaults[key] || {from:1, to:1825};
        fromDays = def.from;
        toDays = def.to;
    }
    setPeopleRangeValue(key, 'from', fromDays);
    setPeopleRangeValue(key, 'to', toDays);
    syncPeopleRangeUI(key);
    renderPeopleFilterPanel();
}

function buildPeopleDatePresets(key) {
    var f = peopleFilterState.filters[key];
    if (!f) return '';
    var presets = [
        {label: '7d', from: 1, to: 7},
        {label: '30d', from: 1, to: 30},
        {label: '90d', from: 1, to: 90},
        {label: '1y', from: 1, to: 365},
        {label: 'All', from: 1, to: 1825}
    ];
    var html = '<div class="filter-presets">';
    for (var i = 0; i < presets.length; i++) {
        var p = presets[i];
        var active = (f.from === p.from && f.to === p.to) ? ' is-active' : '';
        html += '<button class="filter-preset-chip' + active + '" onclick="applyPeopleDatePreset(\'' + key + '\',' + p.from + ',' + p.to + ')">' + p.label + '</button>';
    }
    return html + '</div>';
}

function buildPeopleDualRangeSlider(key) {
    const f = peopleFilterState.filters[key];
    if (!f) return '';
    const fromIdx = peopleDaysToStepIndex(f.from);
    const toIdx = peopleDaysToStepIndex(f.to);
    const maxIdx = peopleDaysSliderSteps.length - 1;
    const fromDisplay = peopleDateToISO(peopleDaysAgoToDate(f.from));
    const toDisplay = peopleDateToISO(peopleDaysAgoToDate(f.to));
    return buildPeopleDatePresets(key) +
        '<div class="dual-range-wrap">' +
        '<div class="dual-range-labels">' +
        '<input type="date" class="dual-range-date-input" id="pf_input_' + key + '_from" value="' + fromDisplay + '" min="' + peopleOldestISO + '" max="' + peopleTodayISO + '" onchange="handlePeopleDateInput(\'' + key + '\',\'from\',this.value);this.blur();">' +
        '<span class="dual-range-unit">&mdash;</span>' +
        '<input type="date" class="dual-range-date-input" id="pf_input_' + key + '_to" value="' + toDisplay + '" min="' + peopleOldestISO + '" max="' + peopleTodayISO + '" onchange="handlePeopleDateInput(\'' + key + '\',\'to\',this.value);this.blur();">' +
        '</div>' +
        '<div class="dual-range-track">' +
        '<input type="range" id="pf_slider_' + key + '_from" min="0" max="' + maxIdx + '" value="' + fromIdx + '" oninput="handlePeopleDualSliderInput(\'' + key + '\',\'from\',this.value)" onchange="handlePeopleDualSliderChange(\'' + key + '\',\'from\',this.value)">' +
        '<input type="range" id="pf_slider_' + key + '_to" min="0" max="' + maxIdx + '" value="' + toIdx + '" oninput="handlePeopleDualSliderInput(\'' + key + '\',\'to\',this.value)" onchange="handlePeopleDualSliderChange(\'' + key + '\',\'to\',this.value)">' +
        '</div></div>';
}

function buildPeopleCountSlider(key) {
    const f = peopleFilterState.filters[key];
    if (!f) return '';
    const fromIdx = peopleCountToStepIndex(f.from);
    const toIdx = peopleCountToStepIndex(f.to);
    const maxIdx = peopleCountSliderSteps.length - 1;
    return '<div class="dual-range-wrap" style="gap:4px;margin:2px 0;">' +
        '<div style="display:flex;align-items:center;gap:6px;">' +
        '<span class="dual-range-input" id="pf_input_' + key + '_from" style="min-width:30px;font-size:0.82em;">' + f.from + '</span>' +
        '<div class="dual-range-track" style="flex:1;">' +
        '<input type="range" id="pf_slider_' + key + '_from" min="0" max="' + maxIdx + '" value="' + fromIdx + '" oninput="handlePeopleCountSliderInput(\'' + key + '\',\'from\',this.value)" onchange="handlePeopleCountSliderChange(\'' + key + '\',\'from\',this.value)">' +
        '<input type="range" id="pf_slider_' + key + '_to" min="0" max="' + maxIdx + '" value="' + toIdx + '" oninput="handlePeopleCountSliderInput(\'' + key + '\',\'to\',this.value)" onchange="handlePeopleCountSliderChange(\'' + key + '\',\'to\',this.value)">' +
        '</div>' +
        '<span class="dual-range-input" id="pf_input_' + key + '_to" style="min-width:30px;font-size:0.82em;">' + f.to + '</span>' +
        '</div></div>';
}

function buildPeopleRoleCheckboxes() {
    if (!Array.isArray(peopleFilterState.filters.member_role)) peopleFilterState.filters.member_role = [];
    const roles = [{v: 'owner', l: 'Owner'}, {v: 'group-admin', l: 'Admin'}, {v: 'group-moderator', l: 'Mod'}, {v: 'member', l: 'Member'}];
    let html = '<div style="margin-top:4px;display:grid;grid-template-columns:1fr 1fr;gap:4px 12px;">';
    for (const role of roles) {
        const checked = peopleFilterState.filters.member_role.includes(role.v) ? 'checked' : '';
        html += '<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" ' + checked + ' onchange="togglePeopleRole(\'' + role.v + '\')"><span style="font-size:0.82em;">' + role.l + '</span></label>';
    }
    return html + '</div>';
}

function buildPeopleCommunityOwnerCheckboxes() {
    if (!Array.isArray(peopleFilterState.filters.community_owner)) peopleFilterState.filters.community_owner = [];
    const types = [{v: 'owner', l: 'Yes'}, {v: 'non-owner', l: 'No'}];
    let html = '<div style="margin-top:4px; display: flex; flex-direction: row; gap: 8px;">';
    for (const t of types) {
        const checked = peopleFilterState.filters.community_owner.includes(t.v) ? 'checked' : '';
        html += '<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" ' + checked + ' onchange="togglePeopleCommunityOwner(\'' + t.v + '\')"><span style="font-size:0.85em;">' + t.l + '</span></label>';
    }
    return html + '</div>';
}

function buildPeopleStatusCheckboxes() {
    const f = peopleFilterState.filters;
    const checks = [
        {key: 'hardcore_status', icon: '&#128293;'},
        {key: 'mrr_status', icon: '&#128081;'},
        {key: 'liftoff_status', icon: '&#128640;'},
        {key: 'diamond_status', icon: '&#128142;'},
        {key: 'fire_status', icon: '&#129399;'},
        {key: 'goat_status', icon: '&#128016;'},
        {key: 'star_status', icon: '&#11088;'},
    ];
    let html = '<div style="margin-top:4px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;">';
    for (const c of checks) {
        const checked = f[c.key] === true ? 'checked' : '';
        html += '<label style="display:flex;align-items:center;justify-content:center;gap:4px;cursor:pointer;padding:8px;border:1px solid var(--color-border);border-radius:4px;"><span style="font-size:1.2em;">' + c.icon + '</span><input type="checkbox" ' + checked + ' onchange="togglePeopleStatus(\'' + c.key + '\')"></label>';
    }
    return html + '</div>';
}

function buildPeopleCommunitySelect() {
    if (!Array.isArray(peopleFilterState.filters.community_slugs)) {
        return '<div style="margin-top:4px;"><button onclick="enablePeopleFilter(\'community_slugs\')" style="padding:4px 8px;font-size:0.85em;">Activate Community Filter</button></div>';
    }
    if (!peopleCachedCommunityList) {
        return '<div class="community-check-list"><span class="loading-text">Loading communities...</span></div>';
    }
    let html = '<div class="community-check-list">';
    for (const c of peopleCachedCommunityList) {
        const slug = c.slug || '';
        const name = c.name || slug;
        if (!slug) continue;
        const checked = peopleFilterState.filters.community_slugs.includes(slug) ? 'checked' : '';
        html += '<label><input type="checkbox" ' + checked + ' onchange="togglePeopleCommunitySlug(\'' + slug.replace(/'/g, "\\'") + '\')">' + lib.escapeHtml(name) + '</label>';
    }
    if (peopleCachedCommunityList.length === 0) html += '<span class="loading-text">No communities found</span>';
    return html + '</div>';
}

function buildPeopleQuicklinkSelector() {
    let html = '<div style="display:flex;gap:4px;align-items:center;">';
    html += '<select id="people-quicklink-select" onchange="onPeopleQuicklinkChange(this.value)" style="flex:1;min-width:0;">';
    html += '<option value="__default__"' + (activePeopleQuicklinkId === null ? ' selected' : '') + '>Default</option>';
    for (const ql of peopleQuicklinks) {
        const sel = ql.id === activePeopleQuicklinkId ? ' selected' : '';
        html += '<option value="' + ql.id + '"' + sel + '>' + lib.escapeHtml(ql.name) + '</option>';
    }
    html += '</select>';
    html += '<button onclick="savePeopleQuicklink()" title="Save current filter as new saved filter" style="padding:2px 8px;font-size:1em;">+</button>';
    html += '</div>';
    if (activePeopleQuicklinkId !== null) {
        html += '<div style="display:flex;gap:4px;margin-top:4px;">';
        html += '<button onclick="updatePeopleQuicklink()" title="Update selected saved filter" style="flex:1;padding:4px 8px;font-size:0.85em;">Save</button>';
        html += '<button onclick="renamePeopleQuicklink()" title="Rename" style="flex:1;padding:4px 8px;font-size:0.85em;">Rename</button>';
        html += '<button onclick="deletePeopleQuicklink()" title="Delete" style="padding:4px 8px;font-size:0.85em;color:#c00;">X</button>';
        html += '</div>';
    }
    return html;
}

function renderPeopleFilterPanel() {
    const body = document.getElementById('people-filter-body');
    if (!body) return;
    let html = '';

    // Saved Filters (always open by default)
    html += buildPeopleFilterRow('quicklinks', 'Saved Filters', buildPeopleQuicklinkSelector());

    // Date ranges
    html += buildPeopleFilterRow('last_active', 'Online', buildPeopleDualRangeSlider('last_active'));
    html += buildPeopleFilterRow('joined', 'Joined', buildPeopleDualRangeSlider('joined'));

    // Activity group — activity period + counts together
    var activityContent = buildPeopleDualRangeSlider('activity_period');
    activityContent += '<div style="margin-top:18px;display:flex;flex-direction:column;gap:6px;">';
    var countKeys = [{key:'count_posts', label:'Posts'}, {key:'count_comments', label:'Comments'}, {key:'count_likes', label:'Likes'}];
    for (var i = 0; i < countKeys.length; i++) {
        var ck = countKeys[i];
        activityContent += '<div><span style="font-size:0.78em;font-weight:500;color:var(--color-text-secondary);">' + ck.label + '</span>' + buildPeopleCountSlider(ck.key) + '</div>';
    }
    activityContent += '</div>';
    html += buildPeopleFilterRow('activity_period', 'Activity', activityContent);

    // Role
    html += buildPeopleFilterRow('member_role', 'Role', buildPeopleRoleCheckboxes());

    // Community owner
    html += buildPeopleFilterRow('community_owner', 'Community Owner', buildPeopleCommunityOwnerCheckboxes());

    // Status badges
    html += buildPeopleFilterRow('status', 'Status', buildPeopleStatusCheckboxes());

    // Sticky Apply & Clear at bottom
    html += '<div class="filter-sticky-actions">';
    html += '<button class="filter-btn-apply" onclick="applyPeopleFilters()">Apply</button>';
    html += '<button class="filter-btn-clear" onclick="clearPeopleFilters()">Clear</button>';
    html += '</div>';

    body.innerHTML = html;
}

// ========== Filter Panel toggle ==========

// (toggleFilterPanel is defined above)

// ========== Core: Load People ==========

async function loadPeople() {
    const PAGE_SIZE = 100;
    let allUsers = [];
    let allProfiles = [];
    let allCounts = {};
    let totalUserCount = 0;
    let page = 1;
    let totalPages = 1;

    try {
        do {
            const rawRes = await fetch('/api/user/filter', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ ...peopleFilterState, page, pageSize: PAGE_SIZE })
            });
            if (!rawRes.ok) {
                lib.showSectionError('people-list-panel', 'People', 'loadPeople', 'people-filter');
                return;
            }
            const res = await rawRes.json();
            allUsers = allUsers.concat(res.users || []);
            for (const p of res.profiles || []) allProfiles.push(p);
            Object.assign(allCounts, res.counts || {});
            totalUserCount = res.total || 0;
            totalPages = res.total_pages || 1;
            page++;
        } while (page <= totalPages);
    } catch (e) {
        console.error('[People] Load failed:', e);
        lib.showSectionError('people-list-panel', 'People', 'loadPeople', 'people-filter');
        return;
    }

    people = allUsers;
    peopleProfiles = new Map();
    for (const p of allProfiles) peopleProfiles.set(p.skool_id, p);
    peopleActivityCounts = allCounts;
    peoplePage = 1;

    document.getElementById('peopleCount').textContent = people.length.toString();
    document.getElementById('peopleTotal').textContent = totalUserCount.toString();

    // Check for pre-selected person or label filter from URL
    const urlParams = new URLSearchParams(window.location.search);
    const urlId = urlParams.get('id');
    if (urlId && !selectedSkoolId) selectedSkoolId = urlId;
    const urlLabel = urlParams.get('label');
    if (urlLabel && !peopleLabelFilter) peopleLabelFilter = urlLabel;

    renderPeopleList();

    // Load churn scores in background
    loadPeopleChurnScores();

    // Load member interests in background
    if (!peopleLabelsLoaded) loadPeopleMemberInterests();

    // Load quicklinks in background
    loadPeopleQuicklinks();

    // Render filter panel
    renderPeopleFilterPanel();
}

// ========== Churn scores ==========

async function loadPeopleChurnScores() {
    try {
        peopleChurnScores.clear();
        let page = 1;
        const pageSize = 200;
        while (true) {
            const res = await fetch('/api/churn/members?page=' + page + '&pageSize=' + pageSize + '&sort=score_desc&tier=all');
            if (!res.ok) break;
            const data = await res.json();
            if (data.members) {
                for (const m of data.members) {
                    if (m.skool_id) peopleChurnScores.set(m.skool_id, m.churn_score);
                }
            }
            if (!data.members || data.members.length < pageSize) break;
            page++;
        }
        // Re-render to show churn scores
        if (peopleChurnScores.size > 0) renderPeopleList();
    } catch (e) {
        console.warn('[People] Failed to load churn scores:', e);
    }
}

// ========== Member interests ==========

const peopleCategoryHueMap = {
    'Health & Fitness': 120, 'Marketing & Sales': 30, 'Business & Entrepreneurship': 45,
    'Personal Development': 270, 'Technology & Software': 210, 'Creative Arts & Design': 330,
    'Education & Learning': 200, 'Finance & Investing': 60, 'Spirituality & Mindfulness': 280,
    'Relationships & Dating': 350, 'Career & Professional': 190, 'Real Estate': 80,
    'E-Commerce': 40, 'Content Creation': 300, 'Coaching & Consulting': 160, 'Other': 0,
};

async function loadPeopleMemberInterests() {
    try {
        const labelsRes = await fetch('/api/community-labels');
        if (!labelsRes.ok) return;
        const labelsData = await labelsRes.json();

        if (labelsData.stale) {
            fetch('/api/community-labels/refresh').then(r => {
                if (r.ok) loadPeopleMemberInterestsData();
            }).catch(() => {});
        }

        if (labelsData.count > 0) await loadPeopleMemberInterestsData();
        peopleLabelsLoaded = true;
    } catch (e) {
        console.warn('[People] Failed to load labels:', e);
    }
}

async function loadPeopleMemberInterestsData() {
    try {
        const skoolIds = people.map(m => m.skool_id).filter(Boolean);
        if (skoolIds.length === 0) return;

        const res = await fetch('/api/member-interests/batch', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ skool_ids: skoolIds })
        });
        if (!res.ok) return;
        const data = await res.json();

        peopleMemberInterests.clear();
        if (data.interests) {
            for (const [skoolId, interests] of Object.entries(data.interests)) {
                peopleMemberInterests.set(skoolId, interests);
            }
        }

        if (peopleMemberInterests.size > 0) renderPeopleList();
    } catch (e) {
        console.warn('[People] Failed to load member interests:', e);
    }
}

// ========== Render ==========

function formatMemberAge(ts) {
    if (!ts) return '';
    const now = Math.floor(Date.now() / 1000);
    const diff = now - ts;
    const days = Math.floor(diff / 86400);
    if (days < 1) return 'today';
    if (days < 30) return days + (days === 1 ? ' day' : ' days');
    const months = Math.floor(days / 30);
    if (months < 12) return months + (months === 1 ? ' month' : ' months');
    const years = Math.floor(months / 12);
    const remainingMonths = months % 12;
    if (remainingMonths === 0) return years + (years === 1 ? ' year' : ' years');
    return years + 'y ' + remainingMonths + 'mo';
}

function renderPersonCard(member) {
    const skoolId = member.skool_id || '';
    const name = lib.escapeHtml((member.first_name || '') + ' ' + (member.last_name || '')).trim() || lib.escapeHtml(member.name || skoolId || 'Unknown');
    const imgId = member.skool_id;
    const initial = (member.first_name || member.name || '?')[0].toUpperCase();
    const hue = Math.abs((skoolId || '').charCodeAt(0) * 137.5) % 360;
    const isSelected = selectedSkoolId === skoolId;

    // Avatar
    let imgHtml;
    if (imgId) {
        imgHtml = '<img src="/api/image/' + imgId + '" class="people-card__avatar" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\';" alt="">' +
            '<div class="people-card__avatar people-card__avatar--placeholder" style="display:none;background:hsl(' + hue + ',55%,45%);">' + initial + '</div>';
    } else {
        imgHtml = '<div class="people-card__avatar people-card__avatar--placeholder" style="background:hsl(' + hue + ',55%,45%);">' + initial + '</div>';
    }

    // Bio (from profile)
    const profile = peopleProfiles.get(skoolId);
    const bio = profile ? lib.escapeHtml(profile.bio || '') : '';
    const bioHtml = bio ? '<div class="people-card__bio">' + bio + '</div>' : '';

    // Membership age
    const memberAge = formatMemberAge(member.member_created_at);
    const memberAgeHtml = memberAge ? 'Member for ' + memberAge : '';

    // Activity counts
    const c = peopleActivityCounts[skoolId] || { posts: 0, comments: 0, likes: 0 };
    const statsHtml = c.posts + ' Posts &middot; ' + c.comments + ' Comments &middot; ' + c.likes + ' Likes';

    // Interest pills
    let labelsHtml = '';
    const interests = peopleMemberInterests.get(skoolId);
    if (interests && interests.length > 0) {
        labelsHtml = '<div class="people-card__labels">';
        const maxPills = 3;
        for (let i = 0; i < Math.min(interests.length, maxPills); i++) {
            const t = interests[i];
            const pillHue = peopleCategoryHueMap[t.category] ?? 0;
            labelsHtml += '<span class="people-card__label" style="background:hsl(' + pillHue + ',50%,25%);color:hsl(' + pillHue + ',80%,80%);border:1px solid hsl(' + pillHue + ',50%,35%);" title="' + lib.escapeHtml(t.category) + '">' + lib.escapeHtml(t.category) + '</span>';
        }
        labelsHtml += '</div>';
    }

    // Churn score
    const churnScore = peopleChurnScores.get(skoolId);
    const churnText = churnScore != null ? ' &middot; Churn ' + churnScore : '';

    return '<div class="people-card' + (isSelected ? ' is-selected' : '') + '" data-skool-id="' + skoolId + '" onclick="selectPerson(\'' + skoolId + '\')">' +
        '<div style="flex-shrink:0;">' + imgHtml + '</div>' +
        '<div class="people-card__body">' +
            '<div class="people-card__name">' + name + '</div>' +
            bioHtml +
            '<div class="people-card__meta">' + memberAgeHtml + '</div>' +
            '<div class="people-card__stats">' + statsHtml + churnText + '</div>' +
            labelsHtml +
        '</div>' +
    '</div>';
}

function renderPeopleList() {
    const panel = document.getElementById('people-list-panel');
    if (!panel) return;

    // Apply label filter if set
    let filteredPeople = people;
    if (peopleLabelFilter && peopleMemberInterests.size > 0) {
        const lf = peopleLabelFilter.toLowerCase();
        filteredPeople = people.filter(function(m) {
            const interests = peopleMemberInterests.get(m.skool_id);
            if (!interests) return false;
            return interests.some(function(t) {
                return t.category.toLowerCase() === lf || (t.tags && t.tags.some(function(tag) { return tag.toLowerCase() === lf; }));
            });
        });
    }

    let html = '';

    // Label filter banner
    if (peopleLabelFilter) {
        html += '<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:8px;background:var(--color-bg-secondary);border:1px solid var(--color-border);border-radius:var(--radius-m);font-size:0.85rem;">';
        html += '<span style="color:var(--color-text-secondary);">Filtered by interest:</span>';
        html += '<strong>' + lib.escapeHtml(peopleLabelFilter) + '</strong>';
        html += '<span style="color:var(--color-text-secondary);">(' + filteredPeople.length + ' members)</span>';
        html += '<button onclick="clearPeopleLabelFilter()" style="margin-left:auto;background:none;border:1px solid var(--color-border);border-radius:var(--radius-s);padding:2px 8px;color:var(--color-text-secondary);cursor:pointer;font-size:0.8rem;">&times; Clear</button>';
        html += '</div>';
    }

    const offset = (peoplePage - 1) * peoplePageSize;
    const pageMembers = filteredPeople.slice(offset, offset + peoplePageSize);

    for (const member of pageMembers) {
        html += renderPersonCard(member);
    }
    html += renderPeoplePaginationFor(filteredPeople.length);
    panel.innerHTML = html;
}

function clearPeopleLabelFilter() {
    peopleLabelFilter = '';
    peoplePage = 1;
    // Remove label param from URL without reload
    const url = new URL(window.location);
    url.searchParams.delete('label');
    history.replaceState(null, '', url);
    renderPeopleList();
}

function renderPeoplePaginationFor(total) {
    const pages = Math.ceil(total / peoplePageSize);
    if (pages <= 1) return '';

    const start = (peoplePage - 1) * peoplePageSize + 1;
    const end = Math.min(peoplePage * peoplePageSize, total);

    let html = '<div class="people-pagination">';
    html += '<span>' + start + '-' + end + ' of ' + total + '</span>';
    html += '<button onclick="setPeoplePage(1)"' + (peoplePage === 1 ? ' disabled' : '') + '>&laquo;</button>';
    html += '<button onclick="setPeoplePage(' + (peoplePage - 1) + ')"' + (peoplePage === 1 ? ' disabled' : '') + '>&lsaquo;</button>';

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

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

function setPeoplePage(page) {
    peoplePage = page;
    renderPeopleList();
    document.getElementById('people-list-panel').scrollTop = 0;
}

// ========== Person selection ==========

function selectPerson(skoolId) {
    selectedSkoolId = skoolId;

    // Update URL without reload
    const url = new URL(window.location);
    url.searchParams.set('id', skoolId);
    window.history.replaceState({}, '', url);

    // Mark selected in list
    document.querySelectorAll('.people-card').forEach(card => {
        card.classList.toggle('is-selected', card.dataset.skoolId === skoolId);
    });

    // Show Update Profile button
    const updateBtn = document.getElementById('updateProfileBtn');
    if (updateBtn) {
        updateBtn.style.display = '';
        updateBtn.disabled = false;
        updateBtn.textContent = 'Update Profile';
    }

    // Reset sub-module state for new person
    if (typeof PeopleProfilePosts !== 'undefined') PeopleProfilePosts.reset();
    if (typeof PeopleProfileConnections !== 'undefined') PeopleProfileConnections.reset();

    // Load full profile in right panel
    if (typeof PeopleProfile !== 'undefined') {
        PeopleProfile.loadProfile(skoolId);
    }
}

// ========== Export ==========

async function downloadPeopleCsv() {
    const skoolIds = people.map(m => m.skool_id);
    try {
        const res = await fetch('/api/user/export-csv', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ skool_ids: skoolIds })
        });
        if (!res.ok) { alert('Export failed'); return; }
        const blob = await res.blob();

        const browserDownload = () => {
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'people_export.zip';
            a.click();
            URL.revokeObjectURL(url);
        };

        if (window.__TAURI__ && window.__TAURI__.dialog && window.__TAURI__.fs) {
            try {
                const { save } = window.__TAURI__.dialog;
                const { writeFile } = window.__TAURI__.fs;
                const path = await save({
                    defaultPath: 'people_export.zip',
                    filters: [{ name: 'ZIP', extensions: ['zip'] }]
                });
                if (path) {
                    const buf = await blob.arrayBuffer();
                    await writeFile(path, new Uint8Array(buf));
                }
            } catch (e) {
                console.warn('Native export failed, falling back to browser download:', e);
                browserDownload();
            }
        } else {
            browserDownload();
        }
    } catch (e) {
        console.error('[People] Export failed:', e);
        alert('Export failed: ' + e.message);
    }
}

// ========== Update Profile ==========

async function updateCurrentProfile() {
    if (!selectedSkoolId) return;
    const btn = document.getElementById('updateProfileBtn');
    if (btn) {
        btn.disabled = true;
        btn.textContent = 'Queuing...';
    }

    try {
        const res = await fetch('/api/enqueue-profile-fetch', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({skool_id: selectedSkoolId})
        });
        const data = await res.json();

        if (btn) {
            btn.textContent = data.ok ? 'Queued' : (data.message || 'Error');
            setTimeout(() => { btn.textContent = 'Update Profile'; btn.disabled = false; }, 3000);
        }
    } catch (e) {
        console.error('[People] Update profile failed:', e);
        if (btn) { btn.textContent = 'Error'; btn.disabled = false; }
    }
}
