// Shared helpers + window globals consumed by the JSX panels.
//
// The hardcoded INITIAL_AGENTS / SKILLS / MCPS arrays from the design have
// been removed — they now come from the live backend via api-client.jsx and
// get written onto `window.SKILLS` / `window.MCPS` / `window.AGENTS` once
// the App's initial load completes. The globals start as empty arrays so
// children that read them at first render (`SKILLS.map(...)`) don't crash.

const AGENT_COLORS = ["#10a37f", "#2563eb", "#c026d3", "#d97706", "#ef4444", "#0ea5e9", "#7c3aed"];

// LLM 模型 + 上下文窗口的**权威来源是后端**(GET /v1/models,见 models_catalog.py)。
// 下面这个 const 只是 **bootstrap fallback** —— app 一挂载就 `api.listModels()` 把
// `window.MODELS` 覆盖成后端返回的真实列表(含 context_window)。所以读窗口一律走
// `window.MODELS`(动态),不要读这个 const。
const MODELS = [
  { id: "deepseek-v4-flash",       label: "deepseek-v4-flash",       context_window: 655360, max_output_tokens: 409600, provider: "baizhi", providers: ["baizhi", "volcengine"] },
  { id: "glm-5.2",                 label: "glm-5.2",                 context_window: 1048576, max_output_tokens: 131072, provider: "baizhi", providers: ["baizhi", "volcengine"] },
  { id: "MiniMax/MiniMax-M2.7",    label: "MiniMax/MiniMax-M2.7",    context_window: 655360, provider: "baizhi" },
  { id: "qwen3.6-plus",            label: "qwen3.6-plus",            context_window: 655360, provider: "baizhi" },
  { id: "deepseek-v4-pro",         label: "deepseek-v4-pro",         context_window: 655360, provider: "baizhi" },
  { id: "MiniMax/MiniMax-M3",      label: "MiniMax/MiniMax-M3",      context_window: 655360, provider: "baizhi" },
  { id: "MiniMax-M2.7",           label: "MiniMax-M2.7",            context_window: 655360 },
  { id: "MiniMax-M2.7-highspeed", label: "MiniMax-M2.7 (highspeed)", context_window: 655360 },
];

// 找不到 model 时的兜底窗口(老 localStorage 里存了已下线 model 等场景)。
const DEFAULT_CONTEXT_WINDOW = 655360;
function modelContextWindow(modelId) {
  // 优先读后端覆盖后的 window.MODELS;它还没 fetch 回来时退回 bootstrap const。
  const list = (typeof window !== "undefined" && window.MODELS) ? window.MODELS : MODELS;
  const m = list.find((x) => x.id === modelId);
  return (m && m.context_window) || DEFAULT_CONTEXT_WINDOW;
}

function modelProviderFor(modelId) {
  const list = (typeof window !== "undefined" && window.MODELS) ? window.MODELS : MODELS;
  const explicitProvider = list.find((model) => model.id === modelId)?.provider;
  if (explicitProvider) return explicitProvider;
  const id = String(modelId || "").toLowerCase();
  if (!id) return "";
  if (id.startsWith("minimax-")) return "minimax";
  if (id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4")) return "openai";
  if (id.startsWith("claude-")) return "anthropic";
  if (id.includes("/")) return "openrouter";
  return "";
}

function modelOptionsForProvider(provider) {
  const list = (typeof window !== "undefined" && window.MODELS) ? window.MODELS : MODELS;
  const filtered = list.filter((m) => !provider || modelProviderFor(m.id) === provider || (m.providers || []).includes(provider));
  return filtered;
}

function defaultModelForProvider(provider) {
  const providers = window.MODEL_PROVIDERS || MODEL_PROVIDERS;
  return modelOptionsForProvider(provider)[0]?.id
    || providers.find((item) => item.id === provider)?.default_model
    || MODELS[0]?.id
    || "";
}

const MODEL_PROVIDERS = [
  { id: "baizhi", label: "Baizhi", default_model: "deepseek-v4-flash", endpoint_display: "http://litellm-test.100wiser.com", credential_status: "server_managed" },
  { id: "minimax", label: "MiniMax", default_model: "MiniMax-M2.7", endpoint_display: "https://api.minimaxi.com/v1", credential_status: "server_managed" },
  { id: "openrouter", label: "OpenRouter", default_model: "openai/gpt-4o-mini", endpoint_display: "https://openrouter.ai/api/v1", credential_status: "server_managed" },
  { id: "litellm", label: "LiteLLM compatible", default_model: "deepseek-v4-flash", endpoint_display: "", credential_status: "server_managed" },
  { id: "openai", label: "OpenAI", default_model: "gpt-4o-mini", endpoint_display: "https://api.openai.com/v1", credential_status: "server_managed" },
  { id: "openai-compatible", label: "OpenAI compatible", default_model: "gpt-4o-mini", endpoint_display: "", credential_status: "server_managed" },
  { id: "volcengine", label: "volcengine", default_model: "deepseek-v4-flash", endpoint_display: "https://ark.cn-beijing.volces.com/api/plan/v3", credential_status: "server_managed" },
  { id: "anthropic", label: "Anthropic", default_model: "claude-haiku-4.5", endpoint_display: "https://api.anthropic.com", credential_status: "server_managed" },
];
window.MODEL_PROVIDERS = MODEL_PROVIDERS;

function modelEndpointForProvider(provider) {
  const list = window.MODEL_PROVIDERS || MODEL_PROVIDERS;
  return list.find((item) => item.id === provider)?.endpoint_display || "";
}

function modelCredentialStatusForProvider(provider) {
  const list = window.MODEL_PROVIDERS || MODEL_PROVIDERS;
  return list.find((item) => item.id === provider)?.credential_status || "server_managed";
}

// Start empty — api-client overwrites these once data loads.
const SKILLS = [];
const MCPS = [];
const LOCAL_TOOLS = [];
const INITIAL_AGENTS = [];

// ---------- id + time helpers ----------
function rid(prefix, len = 12) {
  const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
  let s = "";
  for (let i = 0; i < len; i++) s += chars[Math.floor(Math.random() * chars.length)];
  return prefix + "_" + s;
}
function nowHHMMSS() {
  const d = new Date();
  const pad = (n) => String(n).padStart(2, "0");
  return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
function fmtMs(ms) {
  if (!Number.isFinite(ms)) return "0ms";
  if (ms < 1000) return `${Math.round(ms)}ms`;
  return `${(ms / 1000).toFixed(2)}s`;
}

// ---------- syntax-highlight a JSON string ----------
function highlightJSON(json) {
  return json
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/("(?:\\.|[^"\\])*")(\s*:)/g, '<span class="k">$1</span>$2')
    .replace(/:\s*("(?:\\.|[^"\\])*")/g, ': <span class="s">$1</span>')
    .replace(/\b(true|false|null)\b/g, '<span class="b">$1</span>')
    .replace(/:\s*(-?\d+\.?\d*)/g, ': <span class="n">$1</span>');
}

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

function extractModelOutputMarkdown(output) {
  const texts = [];
  const visit = (value) => {
    if (value == null) return;
    if (typeof value === "string") {
      if (value.trim()) texts.push(value);
      return;
    }
    if (Array.isArray(value)) {
      value.forEach(visit);
      return;
    }
    if (typeof value !== "object") return;
    if (typeof value.text === "string") texts.push(value.text);
    if (typeof value.content === "string") texts.push(value.content);
    if (value.parts) visit(value.parts);
    if (value.content && typeof value.content === "object") visit(value.content);
  };
  visit(output?.content?.parts || output?.parts || output?.content || output?.text || output);
  return texts.join("\n\n").trim();
}

function renderMarkdownLite(md) {
  const lines = String(md || "").replace(/\r\n/g, "\n").split("\n");
  let html = "";
  let inCode = false;
  let code = [];
  let inList = false;
  let inTable = false;
  let tableRows = [];

  const inline = (text) => escapeHTML(text)
    .replace(/`([^`]+)`/g, "<code>$1</code>")
    .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
    .replace(/\*([^*]+)\*/g, "<em>$1</em>");

  const closeList = () => {
    if (inList) {
      html += "</ul>";
      inList = false;
    }
  };
  const flushTable = () => {
    if (!inTable) return;
    if (tableRows.length) {
      const [head, ...body] = tableRows;
      html += "<table><thead><tr>" + head.map((c) => `<th>${inline(c.trim())}</th>`).join("") + "</tr></thead>";
      html += "<tbody>" + body.map((row) => "<tr>" + row.map((c) => `<td>${inline(c.trim())}</td>`).join("") + "</tr>").join("") + "</tbody></table>";
    }
    inTable = false;
    tableRows = [];
  };
  const isSep = (cells) => cells.length > 1 && cells.every((c) => /^:?-{3,}:?$/.test(c.trim()));
  const tableCells = (line) => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|");

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    if (line.trim().startsWith("```")) {
      flushTable(); closeList();
      if (inCode) {
        html += `<pre><code>${escapeHTML(code.join("\n"))}</code></pre>`;
        inCode = false; code = [];
      } else {
        inCode = true; code = [];
      }
      continue;
    }
    if (inCode) {
      code.push(line);
      continue;
    }
    const cells = tableCells(line);
    if (line.includes("|") && cells.length > 1) {
      const nextLine = lines[i + 1] || "";
      const nextCells = tableCells(nextLine);
      if (inTable || isSep(nextCells)) {
        closeList();
        if (isSep(cells)) continue;
        inTable = true;
        tableRows.push(cells);
        continue;
      }
    }
    flushTable();
    if (!line.trim()) {
      closeList();
      continue;
    }
    const heading = /^(#{1,4})\s+(.+)$/.exec(line);
    if (heading) {
      closeList();
      const level = heading[1].length;
      html += `<h${level}>${inline(heading[2])}</h${level}>`;
      continue;
    }
    const bullet = /^\s*[-*]\s+(.+)$/.exec(line);
    if (bullet) {
      if (!inList) {
        html += "<ul>";
        inList = true;
      }
      html += `<li>${inline(bullet[1])}</li>`;
      continue;
    }
    const quote = /^>\s?(.+)$/.exec(line);
    if (quote) {
      closeList();
      html += `<blockquote>${inline(quote[1])}</blockquote>`;
      continue;
    }
    closeList();
    html += `<p>${inline(line)}</p>`;
  }
  if (inCode) html += `<pre><code>${escapeHTML(code.join("\n"))}</code></pre>`;
  flushTable();
  closeList();
  return html;
}

// Build an AG-UI event list for a (simulated) run.
// Still used by Traces / Evals mock data; the Playground now consumes real
// backend events via api-client and renders them directly.
function buildAGUIEvents({ threadId, runId, userMessage, plan, finalText, model }) {
  const events = [];
  let t = Date.now();
  events.push({ type: "RUN_STARTED", threadId, runId, timestamp: t, metadata: { model, source: "playground" } });
  const msgId = rid("msg");
  events.push({ type: "TEXT_MESSAGE_START", messageId: msgId, role: "assistant", timestamp: t += 12 });
  (plan || []).forEach((step) => {
    if (step.kind === "tool") {
      const tcId = rid("tc");
      events.push({ type: "TOOL_CALL_START", toolCallId: tcId, toolCallName: step.name, parentMessageId: msgId, timestamp: t += step.before || 80 });
      events.push({ type: "TOOL_CALL_ARGS", toolCallId: tcId, toolCallName: step.name, delta: JSON.stringify(step.args || {}), timestamp: t += 4 });
      events.push({ type: "TOOL_CALL_END", toolCallId: tcId, timestamp: t += 1 });
      events.push({ type: "TOOL_CALL_RESULT", messageId: `tool_${tcId}`, toolCallId: tcId, role: "tool", content: { result: step.result, duration_ms: step.durMs }, timestamp: t += step.durMs });
    } else if (step.kind === "llm") {
      events.push({ type: "TEXT_MESSAGE_CONTENT", messageId: msgId, delta: step.chunk || "…", timestamp: t += step.durMs });
    }
  });
  events.push({ type: "TEXT_MESSAGE_END", messageId: msgId, timestamp: t += 8 });
  events.push({ type: "RUN_FINISHED", threadId, runId, timestamp: t += 4, output: { text: finalText } });
  return events;
}

Object.assign(window, {
  AGENT_COLORS,
  MODELS,
  modelContextWindow,
  modelProviderFor,
  modelOptionsForProvider,
  defaultModelForProvider,
  modelEndpointForProvider,
  modelCredentialStatusForProvider,
  DEFAULT_CONTEXT_WINDOW,
  SKILLS,
  MCPS,
  LOCAL_TOOLS,
  INITIAL_AGENTS,
  rid,
  nowHHMMSS,
  fmtMs,
  escapeHTML,
  highlightJSON,
  extractModelOutputMarkdown,
  renderMarkdownLite,
  buildAGUIEvents,
});
