const state = {
  windowMinutes: 20,
  intervalSeconds: 4,
  paused: false,
  source: null,
  previousEventIds: new Set(),
  snapshot: null,
};

const statusPill = document.getElementById('statusPill');
const statusText = document.getElementById('statusText');
const connectionValue = document.getElementById('connectionValue');
const lastUpdateValue = document.getElementById('lastUpdateValue');
const windowValue = document.getElementById('windowValue');
const intervalSelect = document.getElementById('intervalSelect');
const windowSelect = document.getElementById('windowSelect');
const toggleStreamBtn = document.getElementById('toggleStreamBtn');
const refreshBtn = document.getElementById('refreshBtn');

function escapeHTML(value) {
  return String(value ?? '').replace(/[&<>"']/g, (char) => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
  }[char]));
}

function formatDateTime(value) {
  if (!value) return '—';
  try {
    return new Date(value).toLocaleTimeString('vi-VN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
  } catch (_) {
    return value;
  }
}

function formatMetric(value) {
  return Number(value || 0).toLocaleString('vi-VN');
}

function formatUsd(value) {
  return `$${Number(value || 0).toFixed(value >= 1 ? 2 : 4)}`;
}

function formatSeconds(value) {
  return `${Number(value || 0).toFixed(2)}s`;
}

function formatPercent(value) {
  return `${Number(value || 0).toFixed(2)}%`;
}

function formatAge(seconds) {
  if (seconds == null) return '—';
  if (seconds < 60) return `${seconds}s`;
  const mins = Math.floor(seconds / 60);
  const remain = seconds % 60;
  return `${mins}m ${remain}s`;
}

function animateNumber(el, nextValue, formatter) {
  const prevValue = Number(el.dataset.value || 0);
  const target = Number(nextValue || 0);
  if (!Number.isFinite(target)) {
    el.textContent = formatter(0);
    el.dataset.value = '0';
    return;
  }

  const start = performance.now();
  const duration = 420;

  function frame(now) {
    const progress = Math.min((now - start) / duration, 1);
    const eased = 1 - Math.pow(1 - progress, 3);
    const current = prevValue + (target - prevValue) * eased;
    el.textContent = formatter(current);
    if (progress < 1) requestAnimationFrame(frame);
    else {
      el.textContent = formatter(target);
      el.dataset.value = String(target);
    }
  }

  requestAnimationFrame(frame);
}

function setStatus(mode, text) {
  statusPill.className = `status-pill ${mode}`;
  statusText.textContent = text;
  connectionValue.textContent = text;
}

function ensureBarChart(containerId, size, variant) {
  const container = document.getElementById(containerId);
  if (container.children.length === size) return container;
  container.innerHTML = '';
  for (let i = 0; i < size; i += 1) {
    const bar = document.createElement('div');
    bar.className = 'mini-bar';
    bar.innerHTML = `
      <div class="mini-bar-track">
        <div class="mini-bar-fill ${variant}" style="height:4px"></div>
      </div>
      <div class="mini-bar-label"></div>
    `;
    container.appendChild(bar);
  }
  return container;
}

function updateBarChart(containerId, labels, values, variant, formatter) {
  const container = ensureBarChart(containerId, labels.length, variant);
  const max = Math.max(...values, 1);
  Array.from(container.children).forEach((bar, idx) => {
    const fill = bar.querySelector('.mini-bar-fill');
    const label = bar.querySelector('.mini-bar-label');
    const value = Number(values[idx] || 0);
    const pct = value <= 0 ? 4 : Math.max(8, (value / max) * 100);
    fill.style.height = `${pct}%`;
    fill.style.opacity = value === 0 ? '0.35' : '1';
    fill.title = `${labels[idx]} · ${formatter(value)}`;
    label.textContent = (idx === 0 || idx === labels.length - 1 || idx % 4 === 0) ? labels[idx] : '';
  });
}

function renderModelMix(models) {
  const container = document.getElementById('modelMixList');
  if (!models || !models.length) {
    container.innerHTML = '<div class="empty-state">Không có model nào trong window này.</div>';
    return;
  }

  container.innerHTML = models.map((model, idx) => `
    <div class="mix-item">
      <div class="mix-head">
        <div class="mix-name">${escapeHTML(model.model_name)}</div>
        <div class="mix-share">${escapeHTML(model.share_pct.toFixed(1))}%</div>
      </div>
      <div class="mix-track">
        <div class="mix-fill" style="width:${Math.max(model.share_pct, 4)}%"></div>
      </div>
      <div class="mix-meta">
        <span>${escapeHTML(formatMetric(model.traces))} traces</span>
        <span>${escapeHTML(formatUsd(model.total_cost_usd))}</span>
        <span>${escapeHTML(formatSeconds(model.avg_latency_s))}</span>
      </div>
    </div>
  `).join('');

  requestAnimationFrame(() => {
    Array.from(container.querySelectorAll('.mix-fill')).forEach((el, idx) => {
      el.style.width = `${Math.max(models[idx].share_pct, 4)}%`;
    });
  });
}

function renderFeed(events) {
  const container = document.getElementById('feedList');
  if (!events || !events.length) {
    container.innerHTML = '<div class="empty-state">Chưa có trace nào trong window này.</div>';
    return;
  }

  const nextIds = new Set(events.map((event) => event.trace_id));
  container.innerHTML = events.map((event) => {
    const fresh = !state.previousEventIds.has(event.trace_id);
    return `
      <div class="feed-item ${fresh ? 'fresh' : ''}">
        <div class="feed-head">
          <div class="feed-title">${escapeHTML(event.model_name)} · ${escapeHTML(event.trace_id.slice(0, 12) || 'trace')}</div>
          <div class="feed-pill ${escapeHTML(event.severity)}">${escapeHTML(event.severity)}</div>
        </div>
        <div class="feed-meta">
          <div><strong>Session</strong>${escapeHTML(event.session_id ? event.session_id.slice(0, 14) : '—')}</div>
          <div><strong>Latency</strong>${escapeHTML(formatSeconds(event.trace_latency_s))}</div>
          <div><strong>Cost</strong>${escapeHTML(formatUsd(event.total_cost_usd))}</div>
          <div><strong>Age</strong>${escapeHTML(formatAge(event.age_seconds))}</div>
        </div>
      </div>
    `;
  }).join('');

  state.previousEventIds = nextIds;
}

function applySnapshot(snapshot) {
  if (!snapshot) return;
  state.snapshot = snapshot;

  if (snapshot.status === 'degraded') setStatus('degraded', 'Degraded');
  else setStatus('live', 'Live');

  lastUpdateValue.textContent = formatDateTime(snapshot.generated_at);
  windowValue.textContent = `${snapshot.window_minutes} phút`;

  const metrics = snapshot.metrics || {};
  const series = snapshot.series || { labels: [], traces: [], costs: [], latencies: [] };

  animateNumber(document.getElementById('metricTraces'), metrics.traces || 0, (v) => formatMetric(Math.round(v)));
  animateNumber(document.getElementById('metricSessions'), metrics.active_sessions || 0, (v) => formatMetric(Math.round(v)));
  animateNumber(document.getElementById('metricCost'), metrics.total_cost_usd || 0, formatUsd);
  animateNumber(document.getElementById('metricLatency'), metrics.avg_latency_s || 0, formatSeconds);
  animateNumber(document.getElementById('metricError'), metrics.error_rate_pct || 0, formatPercent);

  document.getElementById('metricTracesSub').textContent = `Peak ${formatMetric(metrics.peak_traces_per_min || 0)} / min`;
  document.getElementById('metricSessionsSub').textContent = `Event cuối ${formatDateTime(metrics.last_event_at)}`;
  document.getElementById('metricCostSub').textContent = `Peak ${formatUsd(metrics.peak_cost_per_min || 0)} / min`;
  document.getElementById('metricLatencySub').textContent = `Peak ${formatSeconds(metrics.peak_latency_s || 0)}`;
  document.getElementById('metricErrorSub').textContent = snapshot.status_reason || 'Tỷ lệ trace có lỗi trong window';

  animateNumber(document.getElementById('volumePeak'), metrics.peak_traces_per_min || 0, (v) => formatMetric(Math.round(v)));
  animateNumber(document.getElementById('costPeak'), metrics.peak_cost_per_min || 0, formatUsd);
  animateNumber(document.getElementById('latencyPeak'), metrics.peak_latency_s || 0, formatSeconds);

  document.getElementById('volumeCurrent').textContent = `Hiện tại: ${formatMetric(series.traces?.[series.traces.length - 1] || 0)}`;
  document.getElementById('costCurrent').textContent = `Hiện tại: ${formatUsd(series.costs?.[series.costs.length - 1] || 0)}`;
  document.getElementById('latencyCurrent').textContent = `Hiện tại: ${formatSeconds(series.latencies?.[series.latencies.length - 1] || 0)}`;
  document.getElementById('volumeWindow').textContent = `Window ${snapshot.window_minutes} phút`;

  updateBarChart('volumeBars', series.labels || [], series.traces || [], 'volume', (v) => formatMetric(Math.round(v)));
  updateBarChart('costBars', series.labels || [], series.costs || [], 'cost', formatUsd);
  updateBarChart('latencyBars', series.labels || [], series.latencies || [], 'latency', formatSeconds);

  renderModelMix(snapshot.model_mix || []);
  renderFeed(snapshot.latest_events || []);
}

async function loadBootstrap() {
  setStatus('connecting', 'Connecting');
  const res = await fetch(`/api/live-monitor/bootstrap?window_minutes=${state.windowMinutes}`);
  if (!res.ok) throw new Error(`Bootstrap failed: ${res.status}`);
  const snapshot = await res.json();
  applySnapshot(snapshot);
}

function connectStream() {
  if (state.source) {
    state.source.close();
    state.source = null;
  }
  if (state.paused) {
    setStatus('offline', 'Paused');
    return;
  }

  setStatus('connecting', 'Connecting');
  state.source = new EventSource(`/api/live-monitor/stream?window_minutes=${state.windowMinutes}&interval_seconds=${state.intervalSeconds}`);

  state.source.onmessage = (event) => {
    try {
      const payload = JSON.parse(event.data);
      if (payload.type === 'snapshot' || payload.type === 'dashboard_update') {
        applySnapshot(payload.payload);
      } else if (payload.type === 'heartbeat') {
        if (statusText.textContent !== 'Paused') setStatus(payload.status === 'degraded' ? 'degraded' : 'live', payload.status === 'degraded' ? 'Degraded' : 'Live');
        lastUpdateValue.textContent = formatDateTime(payload.generated_at);
      } else if (payload.type === 'error') {
        setStatus('offline', 'Stream Error');
      }
    } catch (error) {
      console.warn('Failed to parse live SSE payload', error);
    }
  };

  state.source.onerror = () => {
    if (!state.paused) setStatus('offline', 'Reconnecting');
  };
}

async function refreshAll() {
  refreshBtn.disabled = true;
  try {
    await loadBootstrap();
    connectStream();
  } catch (error) {
    console.error(error);
    setStatus('offline', 'Load Failed');
  } finally {
    refreshBtn.disabled = false;
  }
}

toggleStreamBtn.addEventListener('click', () => {
  state.paused = !state.paused;
  toggleStreamBtn.textContent = state.paused ? 'Resume Stream' : 'Pause Stream';
  connectStream();
});

intervalSelect.addEventListener('change', () => {
  state.intervalSeconds = Number(intervalSelect.value);
  connectStream();
});

windowSelect.addEventListener('change', async () => {
  state.windowMinutes = Number(windowSelect.value);
  windowValue.textContent = `${state.windowMinutes} phút`;
  await refreshAll();
});

refreshBtn.addEventListener('click', refreshAll);

refreshAll();