<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lazzaro | Memory Dashboard</title>
    <!-- Vue 3 CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Lucide Icons CDN -->
    <script src="https://unpkg.com/lucide@latest"></script>
    <!-- Force Graph CDN -->
    <script src="https://unpkg.com/force-graph"></script>

    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono&display=swap');

        body {
            font-family: 'Inter', sans-serif;
            background-color: #0c0c0e;
            color: #e4e4e7;
            overflow: hidden;
        }

        .glass {
            background: rgba(23, 23, 26, 0.7);
            backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .gradient-text {
            background: linear-gradient(135deg, #a78bfa 0%, #60a5fa 100%);
            -webkit-background-clip: text;
            background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .custom-scrollbar::-webkit-scrollbar {
            width: 4px;
        }

        .custom-scrollbar::-webkit-scrollbar-track {
            background: transparent;
        }

        .custom-scrollbar::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
        }

        #graph-container {
            width: 100%;
            height: 100%;
        }

        [v-cloak] {
            display: none;
        }

        .slide-up {
            animation: slideUp 0.3s ease-out;
        }

        @keyframes slideUp {
            from {
                transform: translateY(20px);
                opacity: 0;
            }

            to {
                transform: translateY(0);
                opacity: 1;
            }
        }
    </style>
</head>

<body class="h-screen flex flex-col">
    <div id="app" class="h-full flex flex-col relative" v-cloak>

        <!-- Header -->
        <header class="h-16 flex items-center justify-between px-6 glass z-30 border-b border-white/5">
            <div class="flex items-center gap-3">
                <div
                    class="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 flex items-center justify-center font-bold text-white">
                    L</div>
                <h1 class="text-xl font-semibold tracking-tight gradient-text">Lazzaro Dashboard</h1>

                <!-- User Selector -->
                <div
                    class="ml-8 flex items-center gap-2 bg-white/5 border border-white/10 rounded-lg px-3 py-1.5 transition-all focus-within:border-indigo-500/50">
                    <i data-lucide="users" class="w-3.5 h-3.5 text-zinc-500"></i>
                    <select v-model="currentUser" @change="switchUser"
                        class="bg-transparent border-none text-[13px] font-medium text-zinc-300 focus:ring-0 outline-none cursor-pointer">
                        <option v-for="user in users" :value="user" class="bg-[#17171a]">[[ user ]]</option>
                    </select>
                </div>
            </div>

            <div class="flex items-center gap-6">
                <!-- Node Search -->
                <div class="relative group hidden md:block">
                    <i data-lucide="search"
                        class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-zinc-500 group-focus-within:text-indigo-400 transition-colors"></i>
                    <input type="text" v-model="searchQuery" @input="searchNodes" placeholder="Search memories..."
                        class="bg-white/5 border border-white/5 rounded-full py-1.5 pl-9 pr-4 text-sm w-64 focus:w-80 transition-all outline-none focus:border-indigo-500/30 focus:bg-white/10">
                </div>

                <div class="flex gap-4 text-[11px] font-medium text-zinc-500 font-mono">
                    <div class="flex items-center gap-1.5"><span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
                        LIVE SESSION</div>
                </div>
                <button @click="refreshData" class="p-2 rounded-full hover:bg-white/5 text-zinc-400 transition-colors">
                    <i data-lucide="refresh-cw" class="w-5 h-5"></i>
                </button>
            </div>
        </header>

        <main class="flex-1 flex flex-col md:flex-row overflow-hidden relative">
            <!-- Sidebar -->
            <aside
                class="w-full md:w-80 glass border-b md:border-b-0 md:border-r border-white/5 flex flex-col z-20 overflow-hidden">
                <div class="p-4 md:p-6 overflow-y-auto custom-scrollbar flex-1">
                    <h2 class="text-[10px] font-semibold text-zinc-500 uppercase tracking-widest mb-6">System Health
                    </h2>

                    <div class="grid grid-cols-2 gap-3 mb-8">
                        <div class="p-3 md:p-4 rounded-xl bg-white/5 border border-white/5">
                            <div class="text-[9px] text-zinc-500 mb-1 uppercase tracking-wider">Nodes</div>
                            <div class="text-xl md:text-2xl font-bold font-mono tracking-tight text-indigo-300">[[
                                stats.buffer_nodes || 0 ]]</div>
                        </div>
                        <div class="p-3 md:p-4 rounded-xl bg-white/5 border border-white/5">
                            <div class="text-[9px] text-zinc-500 mb-1 uppercase tracking-wider">Shards</div>
                            <div class="text-xl md:text-2xl font-bold font-mono tracking-tight text-blue-300">[[
                                stats.num_shards || 0 ]]</div>
                        </div>
                    </div>

                    <h2 class="text-[10px] font-semibold text-zinc-500 uppercase tracking-widest mb-4 font-mono">Memory
                        Tools</h2>
                    <div class="space-y-3 mb-8">
                        <button @click="getInsights"
                            class="w-full py-2 rounded-lg bg-emerald-500/10 border border-emerald-500/30 text-emerald-400 text-[13px] font-medium hover:bg-emerald-500/20 transition-all flex items-center justify-center gap-2">
                            <i data-lucide="sparkles" class="w-4 h-4"></i> Generate Insights
                        </button>
                        <button @click="exportMemories"
                            class="w-full py-2 rounded-lg bg-zinc-500/10 border border-white/10 text-zinc-300 text-[13px] font-medium hover:bg-white/5 transition-all flex items-center justify-center gap-2">
                            <i data-lucide="download" class="w-4 h-4"></i> Export Memories
                        </button>
                    </div>

                    <h2 class="text-[10px] font-semibold text-zinc-500 uppercase tracking-widest mb-4 font-mono">
                        Auto-Management</h2>
                    <div class="space-y-3">
                        <div
                            class="flex items-center justify-between p-3 rounded-lg bg-white/5 border border-white/5 text-xs">
                            <span class="text-zinc-500">Consolidation</span>
                            <span
                                class="px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-500 font-medium tracking-tight">[[
                                stats.auto_consolidate ? 'ENABLED' : 'DISABLED' ]]</span>
                        </div>
                        <button @click="runConsolidation"
                            class="w-full py-2.5 rounded-lg bg-indigo-500/10 border border-indigo-500/30 text-indigo-400 text-sm font-medium hover:bg-indigo-500/20 transition-all flex items-center justify-center gap-2">
                            <i data-lucide="box" class="w-4 h-4"></i> Run Consolidation
                        </button>
                    </div>
                </div>

                <!-- Profile Section -->
                <div class="p-6 border-t border-white/5 bg-black/20">
                    <div class="flex items-center justify-between mb-4">
                        <h2 class="text-[10px] font-semibold text-zinc-500 uppercase tracking-widest">User Profile</h2>
                        <i data-lucide="user-circle" class="w-4 h-4 text-zinc-600"></i>
                    </div>
                    <div
                        class="text-[12px] text-zinc-400 leading-relaxed italic line-clamp-6 bg-white/5 p-3 rounded-lg border border-white/5">
                        [[ profileContext || 'Deep analysis pending consolidation...' ]]
                    </div>
                </div>
            </aside>

            <!-- Main Stage: Graph -->
            <section class="flex-1 relative bg-[#09090b] overflow-hidden">
                <div id="graph-container"></div>

                <!-- Insights Overlay -->
                <div v-if="insightsText"
                    class="absolute inset-0 z-40 bg-black/80 backdrop-blur-md flex items-center justify-center p-8 overflow-y-auto">
                    <div
                        class="max-w-3xl w-full bg-[#17171a] border border-white/10 rounded-2xl shadow-2xl p-8 slide-up">
                        <div class="flex justify-between items-center mb-6">
                            <div class="flex items-center gap-3">
                                <i data-lucide="sparkles" class="w-6 h-6 text-emerald-400"></i>
                                <h1 class="text-2xl font-bold tracking-tight text-white font-mono">LLM PERSONALITY
                                    INSIGHTS</h1>
                            </div>
                            <button @click="insightsText = null"
                                class="p-2 rounded-full hover:bg-white/10 text-zinc-500 transition-colors"><i
                                    data-lucide="x" class="w-5 h-5"></i></button>
                        </div>
                        <div
                            class="prose prose-invert max-w-none text-zinc-300 leading-relaxed space-y-4 whitespace-pre-wrap font-mono text-sm border-t border-white/5 pt-6">
                            [[ insightsText ]]
                        </div>
                    </div>
                </div>

                <!-- Floating Legend -->
                <div
                    class="absolute bottom-6 left-6 p-4 rounded-xl glass border border-white/5 pointer-events-none hidden md:block">
                    <div class="flex items-center gap-6">
                        <div class="flex items-center gap-2 text-[10px] text-zinc-500 uppercase tracking-widest">
                            <span class="w-2 h-2 rounded-full bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.5)]"></span>
                            Semantic
                        </div>
                        <div class="flex items-center gap-2 text-[10px] text-zinc-500 uppercase tracking-widest">
                            <span
                                class="w-2 h-2 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]"></span>
                            Episodic
                        </div>
                        <div class="flex items-center gap-2 text-[10px] text-zinc-500 uppercase tracking-widest">
                            <span
                                class="w-2 h-2 rounded-full bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.5)]"></span>
                            Cluster
                        </div>
                    </div>
                </div>

                <!-- Node Inspector -->
                <div v-if="selectedNode"
                    class="absolute top-6 right-8 w-80 glass border border-white/10 p-6 rounded-2xl animate-in slide-in-from-right-4 shadow-2xl z-50">
                    <div class="flex justify-between items-start mb-4">
                        <span
                            class="px-2 py-0.5 rounded-full bg-indigo-500/10 text-indigo-400 text-[9px] font-bold uppercase tracking-widest border border-indigo-500/20">
                            [[ selectedNode.type ]]
                        </span>
                        <button @click="selectedNode = null"
                            class="text-zinc-600 hover:text-white transition-colors p-1 bg-white/5 rounded"><i
                                data-lucide="x" class="w-4 h-4"></i></button>
                    </div>
                    <p class="text-[15px] font-medium leading-relaxed mb-6 text-white overflow-hidden">[[
                        selectedNode.content ]]</p>
                    <div class="grid grid-cols-2 gap-3 mb-2 text-[10px] text-zinc-500 uppercase tracking-widest">
                        <div class="bg-white/5 p-2 rounded">SALIENCE: [[ selectedNode.salience.toFixed(2) ]]</div>
                        <div class="bg-white/5 p-2 rounded">ACCESSES: [[ selectedNode.access_count ]]</div>
                    </div>
                    <div class="bg-white/5 p-2 rounded text-[10px] text-zinc-500 uppercase tracking-widest">
                        SHARD: [[ selectedNode.shard ]]
                    </div>
                </div>
            </section>
        </main>
    </div>

    <script>
        const { createApp, ref, onMounted, nextTick } = Vue;

        createApp({
            setup() {
                const stats = ref({});
                const graphData = ref({ nodes: [], links: [] });
                const users = ref([]);
                const currentUser = ref("default");
                const searchQuery = ref("");
                const profileContext = ref("");
                const selectedNode = ref(null);
                const insightsText = ref(null);
                let graph = null;

                const refreshData = async () => {
                    try {
                        const [statsRes, graphRes, profileRes, usersRes] = await Promise.all([
                            fetch('/api/stats').then(r => r.json()),
                            fetch('/api/graph').then(r => r.json()),
                            fetch('/api/profile').then(r => r.json()),
                            fetch('/api/users').then(r => r.json())
                        ]);

                        stats.value = statsRes;
                        graphData.value = graphRes;
                        users.value = usersRes;
                        currentUser.value = statsRes.user_id;

                        if (profileRes.data) {
                            const domains = [];
                            for (const [k, v] of Object.entries(profileRes.data)) {
                                if (v && typeof v === 'string') domains.push(`${k.replace('_', ' ')}: ${v.substring(0, 100)}...`);
                            }
                            profileContext.value = domains.join(' | ');
                        }

                        updateGraph();
                        lucide.createIcons();
                    } catch (err) {
                        console.error("Failed to fetch dashboard data", err);
                    }
                };

                const switchUser = async () => {
                    await fetch('/api/users/switch', {
                        method: 'POST',
                        body: JSON.stringify({ user_id: currentUser.value }),
                        headers: { 'Content-Type': 'application/json' }
                    });
                    refreshData();
                };

                const searchNodes = () => {
                    if (!searchQuery.value) return;
                    const q = searchQuery.value.toLowerCase();
                    // Access internal graph nodes for live coordinates
                    const nodes = graph.graphData().nodes;
                    const node = nodes.find(n => n.content.toLowerCase().includes(q));

                    if (node && node.x !== undefined) {
                        graph.centerAt(node.x, node.y, 1000);
                        graph.zoom(3.5, 1000);
                        selectedNode.value = node;
                    }
                };

                const getInsights = async () => {
                    insightsText.value = "ANALYZING GRAPH ARCHITECTURE...\nEXTRACTING SALIENT PATTERNS...\nGENERATING PERSONALITY INSIGHTS...";
                    const res = await fetch('/api/insights').then(r => r.json());
                    insightsText.value = res.insights;
                    nextTick(() => lucide.createIcons());
                };

                const exportMemories = async () => {
                    const res = await fetch('/api/export?format=markdown').then(r => r.json());
                    const blob = new Blob([res.content], { type: 'text/markdown' });
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `lazzaro_memories_${currentUser.value}.md`;
                    a.click();
                };

                const updateGraph = () => {
                    if (!graph) return;
                    graph.graphData(graphData.value);
                };

                const initGraph = () => {
                    const container = document.getElementById('graph-container');
                    graph = ForceGraph()(container)
                        .graphData(graphData.value)
                        .nodeId('id')
                        .nodeLabel(node => `[${node.type}] ${node.content.substring(0, 100)}...`)
                        .nodeColor(node => {
                            if (node.is_super_node) return '#f59e0b';
                            if (node.type === 'episodic') return '#10b981';
                            return '#3b82f6';
                        })
                        .nodeVal(node => Math.max(2, node.salience * 12))
                        .linkColor(() => 'rgba(255, 255, 255, 0.08)')
                        .linkDirectionalParticles(2)
                        .linkDirectionalParticleWidth(link => link.weight * 1.5)
                        .backgroundColor('#09090b')
                        .onNodeClick(node => {
                            selectedNode.value = node;
                            nextTick(() => lucide.createIcons());
                        })
                        .nodeRelSize(5)
                        .cooldownTicks(150);

                    graph.d3Force('charge').strength(-200);
                };

                const runConsolidation = async () => {
                    try {
                        await fetch('/api/consolidate', { method: 'POST' });
                        setTimeout(refreshData, 2000);
                    } catch (err) {
                        console.error("Consolidation failed");
                    }
                };

                onMounted(() => {
                    initGraph();
                    refreshData();
                    lucide.createIcons();

                    window.addEventListener('resize', () => {
                        if (graph) {
                            const container = document.getElementById('graph-container');
                            graph.width(container.clientWidth);
                            graph.height(container.clientHeight);
                        }
                    });

                    // Periodic refresh
                    setInterval(refreshData, 30000);
                });

                return {
                    stats, graphData, users, currentUser, searchQuery, insightsText,
                    profileContext, selectedNode, refreshData, switchUser, searchNodes,
                    getInsights, exportMemories, runConsolidation
                };
            },
            delimiters: ['[[', ']]']
        }).mount('#app');
    </script>
</body>

</html>