import { initI18n } from "./i18n";
import { initSidebar } from "./sidebar";

// Paliadin admin dashboard client (t-paliad-146 PoC).
//
// Reads /api/admin/paliadin/stats + /api/admin/paliadin/turns and
// renders the cards / bars / sparkline / tables. Pure read-only;
// dashboard refreshes on each visit (no live polling — m comes here
// every few days, not every few seconds).

interface Stats {
  total_turns: number;
  turns_last_7_days: number;
  median_duration_ms: number;
  p90_duration_ms: number;
  tool_use_rate: number;
  abandon_rate: number;
  by_classifier: Record<string, number>;
  daily_counts: { day: string; count: number }[];
  top_prompts: { prompt: string; count: number }[];
}

interface Turn {
  turn_id: string;
  user_id: string;
  user_email: string | null;
  user_display_name: string | null;
  session_id: string;
  started_at: string;
  finished_at: string | null;
  duration_ms: number | null;
  user_message: string;
  response: string | null;
  used_tools: string[] | null;
  rows_seen: number[] | null;
  classifier_tag: string | null;
  abandoned: boolean;
  error_code: string | null;
  page_origin: string | null;
  chip_count: number;
}

document.addEventListener("DOMContentLoaded", async () => {
  initI18n();
  initSidebar();

  const [stats, turns] = await Promise.all([
    fetchJSON<Stats>("/api/admin/paliadin/stats"),
    fetchJSON<Turn[]>("/api/admin/paliadin/turns"),
  ]);

  if (stats) renderStats(stats);
  if (turns) renderTurns(turns);
});

async function fetchJSON<T>(url: string): Promise<T | null> {
  try {
    const r = await fetch(url, { credentials: "same-origin" });
    if (!r.ok) return null;
    return (await r.json()) as T;
  } catch {
    return null;
  }
}

function renderStats(s: Stats): void {
  setText("stat-total", String(s.total_turns));
  setText("stat-7d", String(s.turns_last_7_days));
  setText("stat-median", formatMs(s.median_duration_ms));
  setText("stat-tools", formatPct(s.tool_use_rate));
  setText("stat-abandon", formatPct(s.abandon_rate));

  // Classifier histogram bars.
  const cont = document.getElementById("classifier-bars");
  if (cont) {
    const entries = Object.entries(s.by_classifier).sort((a, b) => b[1] - a[1]);
    const max = Math.max(...entries.map((e) => e[1]), 1);
    cont.innerHTML = entries
      .map(([tag, n]) => {
        const pct = (n / max) * 100;
        return `<div class="paliadin-classifier-row">
          <div class="paliadin-classifier-label">${escapeHTML(tag)}</div>
          <div class="paliadin-classifier-bar"><div class="paliadin-classifier-fill" style="width:${pct}%"></div></div>
          <div class="paliadin-classifier-count">${n}</div>
        </div>`;
      })
      .join("");
  }

  // Daily sparkline (last 30 days, vertical bars).
  const spark = document.getElementById("daily-spark");
  if (spark) {
    const days = s.daily_counts;
    const max = Math.max(...days.map((d) => d.count), 1);
    spark.innerHTML = days
      .map((d) => {
        const h = (d.count / max) * 60;
        return `<div class="paliadin-spark-bar" style="height:${h}px" title="${d.day}: ${d.count}"></div>`;
      })
      .join("");
  }

  // Top prompts table.
  const tbody = document.getElementById("top-prompts-tbody");
  if (tbody) {
    if (s.top_prompts.length === 0) {
      tbody.innerHTML = `<tr><td colspan="2">Noch keine Daten.</td></tr>`;
    } else {
      tbody.innerHTML = s.top_prompts
        .map(
          (p) =>
            `<tr><td>${escapeHTML(p.prompt)}</td><td>${p.count}</td></tr>`,
        )
        .join("");
    }
  }
}

function renderTurns(turns: Turn[]): void {
  const tbody = document.getElementById("recent-turns-tbody");
  if (!tbody) return;
  if (turns.length === 0) {
    tbody.innerHTML = `<tr><td colspan="8">Noch keine Anfragen.</td></tr>`;
    return;
  }
  tbody.innerHTML = turns
    .map((t) => {
      const tag = t.classifier_tag || "—";
      // Tools cell pairs each tool name with its rows_seen count when
      // available — "list_my_projects (11), search_my_deadlines (18)" —
      // so the meta is legible at a glance instead of hidden in a side
      // table. Falls back to "—" for casual chats with no tool calls.
      const tools = t.used_tools && t.used_tools.length > 0
        ? t.used_tools
            .map((name, i) => {
              const r = t.rows_seen?.[i];
              return r != null ? `${name} (${r})` : name;
            })
            .join(", ")
        : "—";
      const dur = t.duration_ms != null ? formatMs(t.duration_ms) : "—";
      const errMark = t.error_code ? ` ⚠ ${t.error_code}` : "";
      const userLabel = t.user_display_name || t.user_email || t.user_id.slice(0, 8);
      const userTitle = [t.user_email, t.user_display_name].filter(Boolean).join(" · ") || t.user_id;
      // Response preview — first 200 chars of cleanBody. Full response
      // available on hover via the title attribute.
      const respPreview = t.response ? truncate(t.response, 80) : "—";
      const respTitle = t.response || "";
      const origin = t.page_origin || "—";
      return `<tr>
        <td>${formatTime(t.started_at)}</td>
        <td title="${escapeAttr(userTitle)}">${escapeHTML(userLabel)}</td>
        <td>${escapeHTML(tag)}</td>
        <td title="${escapeAttr(t.user_message)}">${escapeHTML(truncate(t.user_message, 80))}${errMark}</td>
        <td title="${escapeAttr(respTitle)}">${escapeHTML(respPreview)}</td>
        <td>${escapeHTML(tools)}</td>
        <td>${escapeHTML(origin)}</td>
        <td>${dur}</td>
      </tr>`;
    })
    .join("");
}

function escapeAttr(s: string): string {
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/\n/g, " ");
}

function setText(id: string, val: string): void {
  const el = document.getElementById(id);
  if (el) el.textContent = val;
}

function formatMs(ms: number): string {
  if (ms < 1000) return `${ms} ms`;
  return `${(ms / 1000).toFixed(1)} s`;
}

function formatPct(r: number): string {
  return `${Math.round(r * 100)} %`;
}

function formatTime(iso: string): string {
  const d = new Date(iso);
  return d.toLocaleString();
}

function truncate(s: string, n: number): string {
  if (s.length <= n) return s;
  return s.slice(0, n - 1) + "…";
}

function escapeHTML(s: string): string {
  return s
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}
