<script lang="ts">
import { untrack } from 'svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
import {
    load_jitsi_report,
    load_jitsi_report_from_cache
} from '$lib/ae_reports/reports_functions';
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
import { idaa_loc } from '$lib/stores/ae_idaa_stores';
import JitsiUrlBuilder from './ae_idaa_comp__jitsi_url_builder.svelte';

interface MeetingEvent {
    timestamp: string;
    action: string;
    details: { full_name?: string };
}

interface MeetingParticipant {
    displayName: string;
    role: string;
    novi_uuid?: string;
}

interface MeetingReport {
    meeting_id: string;
    room_name: string;
    start_time: string;
    final_duration: string;
    final_participant_count: number;
    final_participants: MeetingParticipant[];
    events: MeetingEvent[];
}

// Extends MeetingReport with exclusion-applied participant data
interface MeetingReportEnriched extends MeetingReport {
    real_participants: MeetingParticipant[];
    real_participant_count: number;
}

// --- Data state ---
let meetings_all = $state<MeetingReport[]>([]);
let meetings_loading = $state(true);
let meetings_error = $state<string | null>(null);
let meetings_load_started = $state(false);

function normalize_text(value: unknown): string {
    return typeof value === 'string' ? value.trim() : '';
}

function normalize_uuid(value: unknown): string {
    return normalize_text(value).toLowerCase();
}

function normalize_meeting_name(value: unknown): string {
    return normalize_text(value).toLowerCase();
}

function normalize_list(value: unknown): string[] {
    return Array.isArray(value)
        ? value
              .map((item) => normalize_text(item))
              .filter((item) => item.length > 0)
        : [];
}

function participant_identity(p: MeetingParticipant): string {
    const uuid = normalize_uuid(p.novi_uuid);
    if (uuid) return `uuid:${uuid}`;
    return `name:${normalize_meeting_name(p.displayName)}`;
}

function format_date_input_value(date: Date): string {
    const year = date.getFullYear();
    const month = `${date.getMonth() + 1}`.padStart(2, '0');
    const day = `${date.getDate()}`.padStart(2, '0');
    return `${year}-${month}-${day}`;
}

const today = new Date();
const default_filter_date_to = format_date_input_value(today);
const default_filter_date_from = format_date_input_value(
    new Date(today.getFullYear(), today.getMonth(), today.getDate() - 60)
);

$effect(() => {
    if (meetings_load_started) return;
    if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;

    const api_cfg = $ae_api;
    const account_id = $ae_loc.account_id;
    if (!api_cfg || !account_id) return;

    meetings_load_started = true;
    meetings_loading = true;
    meetings_error = null;

    untrack(() => {
        void (async () => {
            const cached = await load_jitsi_report_from_cache({
                account_id,
                log_lvl: 1
            });

            if (cached && cached.length > 0) {
                meetings_all = cached;
                meetings_loading = false;
                void load_jitsi_report({
                    api_cfg,
                    account_id,
                    log_lvl: 1
                })
                    .then((m: MeetingReport[]) => {
                        meetings_all = m ?? [];
                    })
                    .catch((err: unknown) => {
                        console.warn('Jitsi report background refresh failed.', err);
                    });
                return;
            }

            try {
                const fresh = await load_jitsi_report({
                    api_cfg,
                    account_id,
                    log_lvl: 1
                });
                meetings_all = fresh ?? [];
            } catch (err: unknown) {
                meetings_error = err instanceof Error ? err.message : String(err);
            } finally {
                meetings_loading = false;
            }
        })();
    });
});

// --- Staff/test exclusion ---
// UUID-based exclusion is the canonical path; the legacy name list stays as a fallback
// for older site config values until those are cleaned up.
let exclude_uuids = $derived<string[]>(
    normalize_list(
        ($ae_loc.site_cfg_json as Record<string, unknown>)
            ?.jitsi_exclude_uuids
    ).map((uuid) => uuid.toLowerCase())
);
const temp_excluded_names = ['Scott I.', 'Brie P.', 'Michelle V.'];
let exclude_names = $derived<string[]>(
    normalize_list(
        ($ae_loc.site_cfg_json as Record<string, unknown>)
            ?.jitsi_exclude_names
    ).map((name) => name.toLowerCase())
);
let known_meetings = $derived<string[]>(
    normalize_list(
        ($ae_loc.site_cfg_json as Record<string, unknown>)
            ?.jitsi_known_meetings
    ).map((name) => name.toLowerCase())
);
let exclude_uuid_set = $derived(new Set(exclude_uuids));
let exclude_name_set = $derived(new Set(exclude_names));
let temp_excluded_name_set = $derived(
    new Set(temp_excluded_names.map((name) => name.toLowerCase()))
);
let known_meeting_set = $derived(new Set(known_meetings));
let show_excluded_uuids = $state(false);
let show_all_meetings = $state(false);

// Apply exclusion to every meeting — produces the "real" participant list
// used by all downstream filters, stats, and display.
let meetings_enriched = $derived<MeetingReportEnriched[]>(
    meetings_all.map((m) => {
        // final_participants can be null/undefined if meta_json.participants was null in the API response
        const all_participants: MeetingParticipant[] = m.final_participants ?? [];
        const real_participants = all_participants.filter((p) => {
            if (show_excluded_uuids) return true;
            const participant_uuid = normalize_uuid(p.novi_uuid);
            if (participant_uuid && exclude_uuid_set.has(participant_uuid)) {
                return false;
            }

            const participant_name = normalize_meeting_name(p.displayName);
            if (
                exclude_name_set.has(participant_name) ||
                temp_excluded_name_set.has(participant_name)
            ) {
                return false;
            }

            return true;
        });
        return {
            ...m,
            real_participants,
            real_participant_count: real_participants.length
        };
    })
);

// --- View mode ---
let group_by_room = $state(true);

// --- Filter state ---
let filter_min_participants = $state(2);
let filter_room_name = $state('');
let filter_date_from = $state(default_filter_date_from);
let filter_date_to = $state(default_filter_date_to);

let filters_are_modified = $derived(
    filter_min_participants !== 2 ||
        (filter_room_name !== '' && $ae_loc.edit_mode) ||
        show_excluded_uuids ||
        show_all_meetings ||
        filter_date_from !== default_filter_date_from ||
        filter_date_to !== default_filter_date_to
);

function reset_filters() {
    filter_min_participants = 2;
    filter_room_name = '';
    show_excluded_uuids = false;
    show_all_meetings = false;
    filter_date_from = default_filter_date_from;
    filter_date_to = default_filter_date_to;
}

// --- Duration helpers ---
function parse_duration_seconds(d: string): number {
    if (!d) return 0;
    const parts = d.split(':').map(Number);
    return (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0);
}

function format_seconds(total: number): string {
    const h = Math.floor(total / 3600).toString().padStart(2, '0');
    const m = Math.floor((total % 3600) / 60).toString().padStart(2, '0');
    const s = Math.floor(total % 60).toString().padStart(2, '0');
    return `${h}:${m}:${s}`;
}

function compute_end_time(start_time: string, duration: string): string {
    if (!start_time || !duration) return '—';
    const start = new Date(start_time);
    const secs = parse_duration_seconds(duration);
    if (secs === 0) return '—';
    const end = new Date(start.getTime() + secs * 1000);
    return end.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}

// --- Derived: filtered meetings ---
// All counts and thresholds use real_participant_count (post-exclusion).
let meetings_filtered = $derived.by(() => {
    return meetings_enriched.filter((m) => {
        if (!show_all_meetings && known_meeting_set.size > 0) {
            if (!known_meeting_set.has(normalize_meeting_name(m.room_name))) {
                return false;
            }
        }
        if (m.real_participant_count < filter_min_participants) return false;
        if ($ae_loc.edit_mode && filter_room_name) {
            if (
                !m.room_name
                    ?.toLowerCase()
                    .includes(filter_room_name.toLowerCase())
            )
                return false;
        }
        if (filter_date_from) {
            if (Date.parse(m.start_time) < Date.parse(filter_date_from))
                return false;
        }
        if (filter_date_to) {
            if (
                Date.parse(m.start_time) >
                Date.parse(filter_date_to + 'T23:59:59.999')
            )
                return false;
        }
        return true;
    });
});

// --- Derived: grouped by room ---
// Rooms sorted by most-recent session desc; sessions within each room sorted desc.
let meetings_grouped = $derived.by<Map<string, MeetingReportEnriched[]>>(() => {
    const groups = new Map<string, MeetingReportEnriched[]>();
    for (const m of meetings_filtered) {
        const room = m.room_name ?? 'Unknown';
        if (!groups.has(room)) groups.set(room, []);
        groups.get(room)!.push(m);
    }
    for (const sessions of groups.values()) {
        sessions.sort(
            (a, b) =>
                new Date(b.start_time).getTime() -
                new Date(a.start_time).getTime()
        );
    }
    return new Map(
        [...groups.entries()].sort((a, b) => {
            const a_last = a[1].at(-1)?.start_time ?? '';
            const b_last = b[1].at(-1)?.start_time ?? '';
            return new Date(b_last).getTime() - new Date(a_last).getTime();
        })
    );
});

// --- Summary stats ---
let summary = $derived.by(() => {
    const count = meetings_filtered.length;
    const total_participants = meetings_filtered.reduce(
        (sum, m) => sum + m.real_participant_count,
        0
    );
    const total_secs = meetings_filtered.reduce(
        (sum, m) => sum + parse_duration_seconds(m.final_duration),
        0
    );
    const avg_secs = count > 0 ? Math.round(total_secs / count) : 0;
    return {
        count,
        total_participants,
        avg_duration: format_seconds(avg_secs),
        total_duration: format_seconds(total_secs)
    };
});

function room_stats(sessions: MeetingReportEnriched[]) {
    const total_secs = sessions.reduce(
        (sum, m) => sum + parse_duration_seconds(m.final_duration),
        0
    );
    const unique_names = new Set(
        sessions.flatMap((m) => m.real_participants.map((p) => participant_identity(p)))
    );
    return {
        meeting_count: sessions.length,
        total_duration: format_seconds(total_secs),
        unique_participant_count: unique_names.size
    };
}

// --- Accordion / room open state ---
let open_accordions = $state<Record<string, boolean>>({});
// Rooms default to open; only track explicitly-closed rooms.
let closed_rooms = $state<Record<string, boolean>>({});

function toggle_accordion(meeting_id: string) {
    open_accordions[meeting_id] = !open_accordions[meeting_id];
}

function toggle_room(room_name: string) {
    closed_rooms[room_name] = !closed_rooms[room_name];
}

function is_room_open(room_name: string): boolean {
    return !closed_rooms[room_name];
}

// --- URL Builder ---
let show_url_builder = $state(false);
let copied_participants_meeting_id = $state<string | null>(null);
let copied_participants_timeout: ReturnType<typeof setTimeout> | null = null;

function build_participant_copy_text(participants: MeetingParticipant[]): string {
    return participants
        .map((participant) =>
            participant.role === 'moderator'
                ? `Mod: ${participant.displayName}`
                : participant.displayName
        )
        .join('\n');
}

async function copy_participants(
    meeting_id: string,
    participants: MeetingParticipant[]
) {
    const text = build_participant_copy_text(participants);
    if (!text) return;

    try {
        await navigator.clipboard.writeText(text);
        copied_participants_meeting_id = meeting_id;
        if (copied_participants_timeout) {
            clearTimeout(copied_participants_timeout);
        }
        copied_participants_timeout = setTimeout(() => {
            if (copied_participants_meeting_id === meeting_id) {
                copied_participants_meeting_id = null;
            }
        }, 2000);
    } catch (err) {
        console.warn('Failed to copy participants to clipboard.', err);
    }
}

// --- Export ---
function download_file(content: string, filename: string, mime: string) {
    const blob = new Blob([content], { type: mime });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}

function export_csv() {
    const rows: string[][] = [
        [
            'Meeting ID',
            'Room Name',
            'Start Time',
            'Duration',
            'Participant Count',
            'Participants'
        ]
    ];
    for (const m of meetings_filtered) {
        rows.push([
            m.meeting_id ?? '',
            m.room_name ?? '',
            m.start_time ? new Date(m.start_time).toISOString() : '',
            m.final_duration ?? '',
            String(m.real_participant_count),
            m.real_participants.map((p) => p.displayName).join('; ')
        ]);
    }
    const csv = rows
        .map((r) =>
            r.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')
        )
        .join('\n');
    download_file(csv, 'jitsi_meeting_report.csv', 'text/csv;charset=utf-8;');
}

function export_json() {
    download_file(
        JSON.stringify(
            meetings_filtered.map((m) => ({
                ...m,
                final_participants: m.real_participants,
                final_participant_count: m.real_participant_count
            })),
            null,
            2
        ),
        'jitsi_meeting_report.json',
        'application/json'
    );
}
</script>

<svelte:head>
    <title>&AElig;: Jitsi Meeting Reports</title>
</svelte:head>

<div class="bg-surface-50-950 text-surface-950 dark:bg-surface-950 dark:text-surface-50 min-h-screen w-full max-w-5xl space-y-4 p-4">
    <!-- Page header: view toggle + export buttons -->
    <div class="flex flex-row flex-wrap items-center justify-between gap-2">
        <h1 class="!text-surface-950 dark:!text-surface-50 text-xl font-bold">
            Jitsi Meeting Reports
        </h1>
        <div class="flex items-center gap-2">
            <!-- View mode toggle -->
            <div class="border-surface-200-800 flex overflow-hidden rounded-lg border bg-surface-50-950 p-0.5 text-sm shadow-sm">
                <button
                    type="button"
                    onclick={() => (group_by_room = true)}
                    title="Group sessions by room"
                    aria-pressed={group_by_room}
                    class="flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium whitespace-nowrap transition-all duration-150 {group_by_room
                        ? '!bg-primary-400 !text-white !shadow-sm ring-1 ring-primary-700'
                        : '!bg-transparent !text-surface-700 hover:!bg-surface-100-900 hover:!text-surface-950 dark:!text-surface-200 dark:hover:!text-surface-50'}">
                    <span class="fas fa-layer-group text-xs" aria-hidden="true"></span>
                    <span>By Room</span>
                </button>
                <button
                    type="button"
                    onclick={() => (group_by_room = false)}
                    title="Show all sessions as a flat list"
                    aria-pressed={!group_by_room}
                    class="border-surface-200-800 flex items-center gap-1.5 rounded-md border-l px-3 py-1.5 text-xs font-medium whitespace-nowrap transition-all duration-150 {!group_by_room
                        ? '!bg-primary-400 !text-white !shadow-sm ring-1 ring-primary-700'
                        : '!bg-transparent !text-surface-700 hover:!bg-surface-100-900 hover:!text-surface-950 dark:!text-surface-200 dark:hover:!text-surface-50'}">
                    <span class="fas fa-list text-xs" aria-hidden="true"></span>
                    <span>Flat List</span>
                </button>
            </div>
            <button
                type="button"
                onclick={export_csv}
                disabled={meetings_filtered.length === 0}
                title="Export filtered meetings as CSV"
                class="btn btn-sm preset-tonal-surface border-surface-200-800 border disabled:opacity-40">
                <span class="fas fa-file-csv" aria-hidden="true"></span>
                <span class="ml-1 hidden sm:inline">CSV</span>
            </button>
            {#if $ae_loc.edit_mode}
            <button
                type="button"
                onclick={export_json}
                disabled={meetings_filtered.length === 0}
                title="Export filtered meetings as JSON"
                class="btn btn-sm preset-tonal-surface border-surface-200-800 border disabled:opacity-40">
                <span class="fas fa-file-code" aria-hidden="true"></span>
                <span class="ml-1 hidden sm:inline">JSON</span>
            </button>
            {/if}
        </div>
    </div>

    <!-- Jitsi URL Builder — edit mode only -->
    {#if $ae_loc.edit_mode && $ae_loc.trusted_access}
        <div
            class="bg-surface-100-900 border-surface-200-800 overflow-hidden rounded-xl border">
            <button
                type="button"
                onclick={() => (show_url_builder = !show_url_builder)}
                class="hover:bg-surface-200-800 flex w-full items-center justify-between gap-2 p-3 text-left transition-colors duration-200">
                <span class="flex items-center gap-2 text-sm font-semibold">
                    <span class="fas fa-tools" aria-hidden="true"></span>
                    Jitsi URL Builder
                </span>
                <span
                    class="fas text-xs opacity-60"
                    class:fa-chevron-down={!show_url_builder}
                    class:fa-chevron-up={show_url_builder}
                    aria-hidden="true"></span>
            </button>
            {#if show_url_builder}
                <div class="border-surface-200-800 border-t p-4">
                    <JitsiUrlBuilder />
                </div>
            {/if}
        </div>
    {/if}

    <!-- Filter bar -->
    <div
        class="bg-surface-100-900 border-surface-200-800 flex flex-row flex-wrap items-end gap-3 rounded-xl border p-3">
        {#if $ae_loc.edit_mode}
            <label class="flex cursor-pointer items-center gap-2 self-end pb-1.5">
                <input
                    type="checkbox"
                    bind:checked={show_excluded_uuids}
                    class="checkbox checkbox-sm" />
                <span class="text-sm font-medium whitespace-nowrap"
                    >Show excluded IDs</span>
            </label>
            <label class="flex cursor-pointer items-center gap-2 self-end pb-1.5">
                <input
                    type="checkbox"
                    bind:checked={show_all_meetings}
                    class="checkbox checkbox-sm" />
                <span class="text-sm font-medium whitespace-nowrap"
                    >Show all meetings</span>
            </label>
            <div class="bg-surface-200-800 hidden h-8 w-px self-end sm:block"></div>
        {/if}
        <div>
            <label
                for="filter_min_p"
                class="mb-1 block text-xs tracking-wide uppercase opacity-40">
                Min. Participants
            </label>
            <input
                type="number"
                id="filter_min_p"
                min="0"
                bind:value={filter_min_participants}
                class="border-surface-200-800 bg-surface-50-950 w-20 rounded border px-2 py-1" />
        </div>
        {#if $ae_loc.edit_mode}
            <div>
                <label
                    for="filter_room"
                    class="mb-1 block text-xs tracking-wide uppercase opacity-40">
                    Room Name
                </label>
                <input
                    type="text"
                    id="filter_room"
                    placeholder="Search rooms..."
                    bind:value={filter_room_name}
                    class="border-surface-200-800 bg-surface-50-950 rounded border px-2 py-1" />
            </div>
        {/if}
        <div>
            <label
                for="filter_date_from"
                class="mb-1 block text-xs tracking-wide uppercase opacity-40">
                From
            </label>
            <input
                type="date"
                id="filter_date_from"
                bind:value={filter_date_from}
                class="border-surface-200-800 bg-surface-50-950 rounded border px-2 py-1" />
        </div>
        <div>
            <label
                for="filter_date_to"
                class="mb-1 block text-xs tracking-wide uppercase opacity-40">
                To
            </label>
            <input
                type="date"
                id="filter_date_to"
                bind:value={filter_date_to}
                class="border-surface-200-800 bg-surface-50-950 rounded border px-2 py-1" />
        </div>
        {#if filters_are_modified}
            <button
                type="button"
                onclick={reset_filters}
                class="btn btn-sm preset-tonal-surface border-surface-200-800 self-end border"
                title="Reset all filters to defaults">
                <span class="fas fa-times" aria-hidden="true"></span>
                Reset
            </button>
        {/if}
    </div>

    {#if $ae_loc.edit_mode && (exclude_uuids.length > 0 || known_meetings.length > 0)}
        <div class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-3">
            <div class="mb-2 text-xs tracking-wide uppercase opacity-50">
                Active Exclusions
            </div>
            <div class="grid grid-cols-1 gap-2 md:grid-cols-2">
                <details class="bg-surface-50-950 rounded-lg border border-dashed border-opacity-30 p-2">
                    <summary class="cursor-pointer list-none font-semibold">
                        Excluded Novi UUIDs ({exclude_uuids.length})
                    </summary>
                    <div class="mt-2 space-y-1 text-xs font-mono">
                        {#each exclude_uuids as uuid (uuid)}
                            <div class="bg-surface-200-800 rounded px-2 py-1">
                                {uuid}
                            </div>
                        {/each}
                    </div>
                </details>
                <details class="bg-surface-50-950 rounded-lg border border-dashed border-opacity-30 p-2">
                    <summary class="cursor-pointer list-none font-semibold">
                        Known Meeting Names ({known_meetings.length})
                    </summary>
                    <div class="mt-2 space-y-1 text-xs font-mono">
                        {#each known_meetings as meeting_name (meeting_name)}
                            <div class="bg-surface-200-800 rounded px-2 py-1">
                                {meeting_name}
                            </div>
                        {/each}
                    </div>
                </details>
            </div>
        </div>
    {/if}

    {#if meetings_loading}
        <!-- Loading skeleton -->
        <div
            class="animate-pulse space-y-2"
            role="status"
            aria-live="polite"
            aria-label="Loading meeting reports">
            {#each [1, 2, 3, 4] as _, i (i)}
                <div class="bg-surface-200-800 h-14 w-full rounded-xl"></div>
            {/each}
        </div>
    {:else if meetings_error}
        <!-- Error state -->
        <div class="bg-error-100 border-error-300 rounded-xl border p-4">
            <div class="font-bold">Error Loading Reports</div>
            <p class="mt-1">
                An error occurred while fetching the meeting reports:
            </p>
            <pre
                class="mt-2 overflow-auto text-xs whitespace-pre-wrap">{meetings_error}</pre>
        </div>
    {:else}
        <!-- Summary stats -->
        {#if meetings_all.length > 0}
            <div class="grid grid-cols-2 gap-2 sm:grid-cols-4">
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-3 text-center">
                    <div class="text-2xl font-bold">{summary.count}</div>
                    <div class="text-xs tracking-wide uppercase opacity-40">
                        Meetings Shown
                    </div>
                </div>
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-3 text-center">
                    <div class="text-2xl font-bold">
                        {summary.total_participants}
                    </div>
                    <div class="text-xs tracking-wide uppercase opacity-40">
                        Total Participants
                    </div>
                </div>
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-3 text-center">
                    <div class="font-mono text-2xl font-bold">
                        {summary.avg_duration}
                    </div>
                    <div class="text-xs tracking-wide uppercase opacity-40">
                        Avg Duration
                    </div>
                </div>
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-3 text-center">
                    <div class="font-mono text-2xl font-bold">
                        {summary.total_duration}
                    </div>
                    <div class="text-xs tracking-wide uppercase opacity-40">
                        Total Duration
                    </div>
                </div>
            </div>
        {/if}

        <!-- ═══════════════════════════════════════════════════════════
             GROUPED BY ROOM VIEW
             ═══════════════════════════════════════════════════════════ -->
        {#if group_by_room}
            {#if meetings_grouped.size > 0}
                <div class="space-y-2">
                    {#each meetings_grouped as [room_name, sessions] (room_name)}
                        {@const stats = room_stats(sessions)}
                        <div
                            class="bg-surface-50-900 border-surface-200-800 overflow-hidden rounded-xl border">
                            <!-- Room header -->
                            <button
                                type="button"
                                onclick={() => toggle_room(room_name)}
                                class="hover:bg-surface-100-900 flex w-full items-center justify-between gap-2 p-3 text-left transition-colors duration-200">
                                <div class="min-w-0 flex-1">
                                    <div class="truncate font-semibold">
                                        {room_name}
                                    </div>
                                    <div
                                        class="mt-0.5 flex flex-wrap gap-3 text-xs opacity-60">
                                        <span>
                                            <span
                                                class="fas fa-calendar-alt mr-1"
                                                aria-hidden="true"></span>
                                            {stats.meeting_count}
                                            {stats.meeting_count === 1
                                                ? 'session'
                                                : 'sessions'}
                                        </span>
                                        <span title="Unique participants (by Novi UUID when available)">
                                            <span
                                                class="fas fa-users mr-1"
                                                aria-hidden="true"></span>
                                            {stats.unique_participant_count} unique
                                            {stats.unique_participant_count === 1
                                                ? 'participant'
                                                : 'participants'}
                                        </span>
                                        <span class="font-mono">
                                            <span
                                                class="fas fa-clock mr-1"
                                                aria-hidden="true"></span>
                                            {stats.total_duration} total
                                        </span>
                                    </div>
                                </div>
                                <span
                                    class="fas flex-none pl-2 text-xs opacity-60"
                                    class:fa-chevron-down={is_room_open(
                                        room_name
                                    )}
                                    class:fa-chevron-up={!is_room_open(
                                        room_name
                                    )}
                                    aria-hidden="true"></span>
                            </button>

                            <!-- Room session table -->
                            {#if is_room_open(room_name)}
                                <div
                                    class="border-surface-200-800 overflow-x-auto border-t">
                                    <table class="w-full text-sm">
                                        <thead>
                                            <tr
                                                class="bg-surface-100-900 border-surface-200-800 border-b">
                                                <th
                                                    class="text-left font-medium whitespace-nowrap opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    >Date</th>
                                                <th
                                                    class="text-left font-medium whitespace-nowrap opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    >Start</th>
                                                <th
                                                    class="text-left font-medium whitespace-nowrap opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    >End</th>
                                                <th
                                                    class="text-left font-medium whitespace-nowrap opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    >Duration</th>
                                                <th
                                                    class="text-center font-medium whitespace-nowrap opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    title="Participant count (after staff exclusion)">#</th>
                                                <th
                                                    class="text-left font-medium opacity-60"
                                                    style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                    >Participants</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            {#each sessions as m (m.meeting_id)}
                                                {@const mods = m.real_participants.filter((p) => p.role === 'moderator')}
                                                {@const others = m.real_participants.filter((p) => p.role !== 'moderator')}
                                                {@const all_names = m.real_participants.map((p) => `${p.displayName} (${p.role})`).join('\n')}
                                                <tr
                                                    class="border-surface-200-800 bg-surface-50-900 text-surface-950 hover:bg-surface-100-900 dark:bg-surface-900 dark:text-surface-50 border-b transition-colors duration-200">
                                                    <td
                                                        class="whitespace-nowrap"
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;">
                                                        {new Date(
                                                            m.start_time
                                                        ).toLocaleDateString()}
                                                    </td>
                                                    <td
                                                        class="whitespace-nowrap"
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;">
                                                        {new Date(
                                                            m.start_time
                                                        ).toLocaleTimeString(
                                                            [],
                                                            {
                                                                hour: '2-digit',
                                                                minute: '2-digit'
                                                            }
                                                        )}
                                                    </td>
                                                    <td
                                                        class="whitespace-nowrap"
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;">
                                                        {compute_end_time(m.start_time, m.final_duration)}
                                                    </td>
                                                    <td
                                                        class="font-mono whitespace-nowrap"
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;">
                                                        {m.final_duration}
                                                    </td>
                                                    <td
                                                        class="text-center"
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;"
                                                        title={all_names || 'No participants'}>
                                                        {m.real_participant_count}
                                                    </td>
                                                    <td
                                                        title={all_names || 'No participants'}
                                                        style="padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem;">
                                                        {#if m.real_participant_count === 0}
                                                            <span class="opacity-40">—</span>
                                                        {:else}
                                                            <div class="space-y-1 text-xs">
                                                                {#if mods.length > 0}
                                                                    <div class="whitespace-normal break-words">
                                                                        <span class="mr-1 opacity-40">Mod:</span>
                                                                        <span class="font-semibold">{mods.map((p) => p.displayName).join(', ')}</span>
                                                                    </div>
                                                                {/if}
                                                                {#if others.length > 0}
                                                                    <div class="whitespace-normal break-words opacity-80">
                                                                        {others.map((p) => p.displayName).join(', ')}
                                                                    </div>
                                                                {/if}
                                                                {#if $ae_loc.edit_mode}
                                                                    <button
                                                                        type="button"
                                                                        onclick={() =>
                                                                            copy_participants(
                                                                                m.meeting_id,
                                                                                m.real_participants
                                                                            )}
                                                                        class="inline-flex items-center gap-1 rounded border border-surface-200-800 bg-surface-100-900 px-2 py-1 text-xs font-medium transition-colors hover:bg-surface-200-800"
                                                                        title="Copy participants to clipboard">
                                                                        <span
                                                                            class="fas fa-copy"
                                                                            aria-hidden="true"></span>
                                                                        {copied_participants_meeting_id === m.meeting_id
                                                                            ? 'Copied'
                                                                            : 'Copy names'}
                                                                    </button>
                                                                {/if}
                                                            </div>
                                                        {/if}
                                                    </td>
                                                </tr>
                                            {/each}
                                        </tbody>
                                    </table>
                                </div>
                            {/if}
                        </div>
                    {/each}
                </div>
            {:else}
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-6 text-center">
                    {#if meetings_all.length > 0}
                        <div class="font-semibold">
                            No meetings match the current filters
                        </div>
                        <p class="mt-1 text-sm opacity-60">
                            Try adjusting the filters or clearing the date
                            range.
                        </p>
                        <button
                            type="button"
                            onclick={reset_filters}
                            class="btn btn-sm preset-tonal-surface border-surface-200-800 mt-3 border">
                            <span class="fas fa-times mr-1" aria-hidden="true"
                            ></span>
                            Reset Filters
                        </button>
                    {:else}
                        <div class="font-semibold">No Meeting Reports Found</div>
                        <p class="mt-1 text-sm opacity-60">
                            There are no Jitsi activity logs to display.
                        </p>
                    {/if}
                </div>
            {/if}

        <!-- ═══════════════════════════════════════════════════════════
             FLAT LIST VIEW
             ═══════════════════════════════════════════════════════════ -->
        {:else}
            {#if meetings_filtered.length > 0}
                <div class="space-y-2">
                    {#each meetings_filtered as meeting (meeting.meeting_id)}
                        <div
                            class="bg-surface-50-900 border-surface-200-800 overflow-hidden rounded-xl border">
                            <!-- Accordion header -->
                            <!-- svelte-ignore a11y_click_events_have_key_events -->
                            <!-- svelte-ignore a11y_no_static_element_interactions -->
                            <div
                                class="hover:bg-surface-100-900 cursor-pointer p-3 transition-colors duration-200"
                                onclick={() =>
                                    toggle_accordion(meeting.meeting_id)}>
                                <div class="flex items-center gap-2">
                                    <div class="min-w-0 flex-1">
                                        <div class="truncate font-semibold">
                                            {meeting.room_name}
                                        </div>
                                        <div class="text-sm opacity-60">
                                            {new Date(meeting.start_time).toLocaleString()}
                                            {#if meeting.final_duration && meeting.final_duration !== '00:00:00'}
                                                <span class="mx-1 opacity-50">→</span>{compute_end_time(meeting.start_time, meeting.final_duration)}
                                            {/if}
                                        </div>
                                    </div>
                                    <div
                                        class="hidden flex-none items-center gap-4 text-sm opacity-60 sm:flex">
                                        <span title="Duration">
                                            <span
                                                class="fas fa-clock mr-1"
                                                aria-hidden="true"></span>
                                            {meeting.final_duration}
                                        </span>
                                        <span title="Participant count">
                                            <span
                                                class="fas fa-users mr-1"
                                                aria-hidden="true"></span>
                                            {meeting.real_participant_count}
                                        </span>
                                    </div>
                                    <div class="flex-none pl-2">
                                        <span
                                            class="fas inline-block transition-transform duration-200"
                                            class:fa-chevron-down={!open_accordions[
                                                meeting.meeting_id
                                            ]}
                                            class:fa-chevron-up={open_accordions[
                                                meeting.meeting_id
                                            ]}
                                            aria-hidden="true"></span>
                                    </div>
                                </div>
                                <!-- Mobile stats row -->
                                <div
                                    class="mt-1 flex gap-4 text-sm opacity-60 sm:hidden">
                                    <span>
                                        <span
                                            class="fas fa-clock mr-1"
                                            aria-hidden="true"></span>
                                        {meeting.final_duration}
                                    </span>
                                    <span>
                                        <span
                                            class="fas fa-users mr-1"
                                            aria-hidden="true"></span>
                                        {meeting.real_participant_count}
                                        {meeting.real_participant_count === 1
                                            ? 'participant'
                                            : 'participants'}
                                    </span>
                                </div>
                            </div>

                            <!-- Accordion body -->
                            {#if open_accordions[meeting.meeting_id]}
                                <div
                                    class="border-surface-200-800 grid grid-cols-1 gap-4 border-t p-4 md:grid-cols-2">
                                    <!-- Event Timeline -->
                                    <div>
                                        <div
                                            class="mb-2 text-xs tracking-wide uppercase opacity-40">
                                            Event Timeline
                                        </div>
                                        {#if meeting.events && meeting.events.length > 0}
                                            <ul class="space-y-1">
                                                {#each meeting.events as event, i (i)}
                                                    <li
                                                        class="flex items-start gap-2 text-sm">
                                                        <span
                                                            class="mt-0.5 font-mono text-xs whitespace-nowrap opacity-60">
                                                            [{new Date(
                                                                event.timestamp
                                                            ).toLocaleTimeString()}]
                                                        </span>
                                                        <span>
                                                            <span
                                                                class="font-semibold">
                                                                {ae_util.to_title_case(
                                                                    event.action.replace(
                                                                        'jitsi_meeting_',
                                                                        ''
                                                                    )
                                                                )}
                                                            </span>
                                                            {#if event.details?.full_name}
                                                                <span
                                                                    class="opacity-60">
                                                                    — {event
                                                                        .details
                                                                        .full_name}
                                                                </span>
                                                            {/if}
                                                        </span>
                                                    </li>
                                                {/each}
                                            </ul>
                                        {:else}
                                            <p class="text-sm italic opacity-60">
                                                No discrete events recorded.
                                            </p>
                                        {/if}
                                    </div>

                                    <!-- Final Participants (exclusion-applied) -->
                                    <div>
                                        <div class="mb-2 flex items-center justify-between gap-2">
                                            <div
                                                class="text-xs tracking-wide uppercase opacity-40">
                                                Final Participants ({meeting.real_participant_count})
                                            </div>
                                            {#if $ae_loc.edit_mode}
                                                <button
                                                    type="button"
                                                    onclick={() =>
                                                        copy_participants(
                                                            meeting.meeting_id,
                                                            meeting.real_participants
                                                        )}
                                                    class="inline-flex items-center gap-1 rounded border border-surface-200-800 bg-surface-100-900 px-2 py-1 text-xs font-medium transition-colors hover:bg-surface-200-800"
                                                    title="Copy participants to clipboard">
                                                    <span
                                                        class="fas fa-copy"
                                                        aria-hidden="true"></span>
                                                    {copied_participants_meeting_id === meeting.meeting_id
                                                        ? 'Copied'
                                                        : 'Copy names'}
                                                </button>
                                            {/if}
                                        </div>
                                        {#if meeting.real_participants && meeting.real_participants.length > 0}
                                            <table class="w-full text-sm">
                                                <thead>
                                                    <tr
                                                        class="border-surface-200-800 border-b">
                                                        <th
                                                            class="text-left font-medium opacity-60"
                                                            style="padding-left: 0.75rem; padding-right: 0.75rem; padding-top: 0.25rem; padding-bottom: 0.25rem;"
                                                            >Name</th>
                                                        <th
                                                            class="text-left font-medium opacity-60"
                                                            style="padding-left: 0.75rem; padding-right: 0.75rem; padding-top: 0.25rem; padding-bottom: 0.25rem;"
                                                            >Role</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    {#each meeting.real_participants as participant (participant.displayName)}
                                                        <tr
                                                            class="border-surface-200-800 bg-surface-50-900 text-surface-950 hover:bg-surface-100-900 dark:bg-surface-900 dark:text-surface-50 border-b transition-colors duration-200">
                                                            <td
                                                                style="padding-left: 0.75rem; padding-right: 0.75rem; padding-top: 0.25rem; padding-bottom: 0.25rem;"
                                                                >{participant.displayName}</td>
                                                            <td
                                                                style="padding-left: 0.75rem; padding-right: 0.75rem; padding-top: 0.25rem; padding-bottom: 0.25rem;"
                                                                >{ae_util.to_title_case(
                                                                    participant.role
                                                                )}</td>
                                                        </tr>
                                                    {/each}
                                                </tbody>
                                            </table>
                                        {:else}
                                            <p class="text-sm italic opacity-60">
                                                No participant data available.
                                            </p>
                                        {/if}
                                    </div>
                                </div>
                            {/if}
                        </div>
                    {/each}
                </div>
            {:else}
                <div
                    class="bg-surface-100-900 border-surface-200-800 rounded-xl border p-6 text-center">
                    {#if meetings_all.length > 0}
                        <div class="font-semibold">
                            No meetings match the current filters
                        </div>
                        <p class="mt-1 text-sm opacity-60">
                            Try lowering the minimum participants or clearing
                            the date range.
                        </p>
                        <button
                            type="button"
                            onclick={reset_filters}
                            class="btn btn-sm preset-tonal-surface border-surface-200-800 mt-3 border">
                            <span class="fas fa-times mr-1" aria-hidden="true"
                            ></span>
                            Reset Filters
                        </button>
                    {:else}
                        <div class="font-semibold">No Meeting Reports Found</div>
                        <p class="mt-1 text-sm opacity-60">
                            There are no Jitsi activity logs to display.
                        </p>
                    {/if}
                </div>
            {/if}
        {/if}
    {/if}
</div>
