<template>
  <div class="h-full flex flex-col bg-slate-900 rounded-lg p-4 shadow-lg overflow-hidden relative">
    <h2 class="text-xl font-semibold mb-4 text-slate-100 flex-shrink-0">Live Telemetry</h2>
    
    <div class="flex-1 w-full h-full min-h-[300px] bg-slate-800 rounded-lg border border-slate-700 overflow-y-auto relative p-2 pt-4" ref="plotContainerRef">
      <!-- We render individual uPlot instances vertically based on profile.plots -->
      <div v-if="profileConfig && profileConfig.plots" class="flex flex-col h-full gap-4 pb-4">
         <div v-for="(plotDef, index) in profileConfig.plots" :key="index" ref="plotWrappers" :data-plot-idx="index" class="plot-wrapper relative resize-y overflow-hidden border border-slate-700/50 rounded bg-slate-800/30 flex flex-col pt-8 flex-1 min-h-[250px]">
            <h3 class="absolute top-2 left-2 z-10 text-xs font-mono bg-slate-900/80 px-2 py-1 rounded text-slate-300">
              {{ plotDef.title || `Plot ${index + 1}` }}
            </h3>
            <div class="flex-1 w-full pl-2">
               <UplotVue 
                  :options="getUplotOptions(plotDef, index)" 
                  :data="getUplotData(plotDef)" 
                  class="w-full"
               />
            </div>
         </div>
      </div>
      <div v-else class="absolute inset-0 flex items-center justify-center text-slate-500">
        Waiting for profile configuration...
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted, onUnmounted, shallowRef, watch, inject } from 'vue';
import UplotVue from 'uplot-vue';
import 'uplot/dist/uPlot.min.css';
import { subscribeToStream, profileConfig } from '../services/backend';

const plotContainerRef = ref(null);
const plotWrappers = ref([]);
const plotSizes = reactive({});

let resizeObserver = null;

onMounted(() => {
    resizeObserver = new ResizeObserver((entries) => {
        for (let entry of entries) {
            const idx = entry.target.getAttribute('data-plot-idx');
            if (idx !== null) {
                const w = entry.contentRect.width;
                const h = entry.contentRect.height;
                plotSizes[idx] = {
                    width: Math.max(200, w - 20),
                    height: Math.max(100, h - 80)
                };
            }
        }
    });

    watch(() => plotWrappers.value, (wrappers) => {
        resizeObserver.disconnect();
        if (wrappers && wrappers.length > 0) {
            wrappers.forEach(el => {
                if (el) resizeObserver.observe(el);
            });
        }
    }, { immediate: true, deep: true });
});

onUnmounted(() => {
    if (resizeObserver) resizeObserver.disconnect();
});


// Data store: { stream_id: { timestamp: [], y_item1: [], y_item2: [] } }
const dataStore = shallowRef({});
// Global markers store for events overlaid on all plots: [ {ts, label, color} ]
const globalMarkers = shallowRef([]);

// Max data points to keep in memory to prevent browser lag
const MAX_DATAPOINTS = 1000;

const clearTrigger = inject('clearTrigger');
if (clearTrigger) {
    watch(clearTrigger, () => {
        // Clear all arrays
        for (const streamId in dataStore.value) {
            for (const key in dataStore.value[streamId]) {
                dataStore.value[streamId][key] = [];
            }
        }
        dataStore.value = { ...dataStore.value };
        globalMarkers.value = [];
    });
}

let isUpdatePending = false;

subscribeToStream((payload) => {
    // Check for marker events
    if (payload && payload.marker) {
        const m = payload.marker;
        globalMarkers.value.push(m);
        if (globalMarkers.value.length > 200) {
            globalMarkers.value.shift();
        }
        // Force a redraw request if not already pending
        if (!isUpdatePending) {
            isUpdatePending = true;
            requestAnimationFrame(() => {
                dataStore.value = { ...dataStore.value };
                isUpdatePending = false;
            });
        }
        return; // Markers are handled via uPlot hooks, no need to add to dataStore
    }
    
    // Process continuous data payload shape from backend: { values: { ts: 123, v1: 45, _stream_id: 'p1' } }
    if (!payload || !payload.values) return;
    
    const incomingData = payload.values;
    const streamId = incomingData._stream_id;
    if (!streamId) return;
    
    if (!dataStore.value[streamId]) {
        // Initialize structure based on incoming keys
        const initial = {};
        for (const key in incomingData) {
            initial[key] = [];
        }
        dataStore.value[streamId] = initial;
    }
    
    const streamData = dataStore.value[streamId];
    
    // Append new data
    for (const key in incomingData) {
        if (streamData[key] !== undefined) {
             streamData[key].push(incomingData[key]);
             // Trim if too long
             if (streamData[key].length > MAX_DATAPOINTS) {
                 streamData[key].shift();
             }
        }
    }
    
    // Throttled UI reactivity: Group updates per frame (60fps) to prevent Vue prop overhead
    if (!isUpdatePending) {
        isUpdatePending = true;
        requestAnimationFrame(() => {
            dataStore.value = { ...dataStore.value };
            isUpdatePending = false;
        });
    }
});

function getUplotData(plotDef) {
    const streamId = plotDef.stream_id;
    const store = dataStore.value[streamId];
    
    if (!store) {
        // Return mostly empty arrays conforming to uPlot expectations
        // [ x_values[], y1_values[], y2_values[] ]
        const empty = [[]];
        if (plotDef.y_items) {
           plotDef.y_items.forEach(() => empty.push([]));
        }
        return empty;
    }
    
    // Assume the 'x' axis is always time or the first field, usually 'ts' or 'timestamp'
    // To make it generic, we look for 'ts' or use a proxy index array if not found
    let xArray = store['ts'] || store['timestamp'] || store['time'];
    if (!xArray && Object.keys(store).length > 0) {
        // Fallback: just use 0, 1, 2, ...
        const someY = store[Object.keys(store)[0]];
        xArray = Array.from({length: someY.length}, (_, i) => i);
    }
    
    const result = [xArray || []];
    
    if (plotDef.y_items) {
        plotDef.y_items.forEach(item => {
            const yKey = typeof item === 'string' ? item : item.key;
            result.push(store[yKey] || []);
        });
    }
    
    return result;
}

const colors = ["#6366f1", "#14b8a6", "#f59e0b", "#ec4899", "#8b5cf6"];

function getUplotOptions(plotDef, idx) {
    const size = plotSizes[idx] || { 
        width: plotContainerRef.value ? Math.max(200, plotContainerRef.value.clientWidth - 40) : 600, 
        height: 220 
    };
    
    const seriesOptions = [
        {} // X axis
    ];
    
    if (plotDef.y_items) {
        plotDef.y_items.forEach((item, i) => {
            const labelStr = typeof item === 'string' ? item : (item.label || item.key);
            
            // Extract custom color from profile, fallback to default palette
            let strokeColor = colors[i % colors.length];
            if (plotDef.colors && plotDef.colors[labelStr]) {
                strokeColor = plotDef.colors[labelStr];
            }
            
            seriesOptions.push({
                label: labelStr,
                stroke: strokeColor,
                width: 2,
            });
        });
    }

    return {
        width: size.width,
        height: size.height,
        cursor: {
           sync: {
             key: "esp_sync" // Synchronize cursors across multiple plots
           }
        },
        scales: {
           x: { time: false } // Assuming fast hardware ticks, not unixtimes usually. Adjust if needed.
        },
        series: seriesOptions,
        axes: [
            {
                stroke: "#94a3b8",
                grid: { stroke: "#334155", width: 1 }
            },
            {
                stroke: "#94a3b8",
                grid: { stroke: "#334155", width: 1 }
            }
        ],
        hooks: {
            drawSeries: [
                (u) => {
                    const ctx = u.ctx;
                    const { left, top, width, height } = u.bbox;
                    const xMin = u.scales.x.min;
                    const xMax = u.scales.x.max;
                    
                    if (!xMin || !xMax) return;
                    
                    ctx.save();
                    ctx.beginPath();
                    ctx.rect(left, top, width, height);
                    ctx.clip();
                    
                    globalMarkers.value.forEach(m => {
                       if (m.ts >= xMin && m.ts <= xMax) {
                           const cx = u.valToPos(m.ts, 'x', true);
                           
                           ctx.beginPath();
                           ctx.strokeStyle = m.color || "red";
                           ctx.lineWidth = 1;
                           ctx.moveTo(cx, top);
                           ctx.lineTo(cx, top + height);
                           ctx.stroke();
                           
                           if (m.label) {
                               ctx.fillStyle = m.color || "red";
                               ctx.font = "10px sans-serif";
                               ctx.fillText(m.label, cx + 4, top + 15);
                           }
                       }
                    });
                    
                    ctx.restore();
                }
            ]
        }
    };
}
</script>

<style>
/* Custom overrides for uPlot dark theme */
.uplot .u-legend {
    color: #cbd5e1;
}
.uplot .u-legend th {
    color: #94a3b8;
}
</style>
