// Small UI primitives shared across pages.

const { useState, useEffect, useMemo, useRef } = React;

function Avatar({ agentId, size = 36, lead = false }) {
  const a = teamById(agentId);
  if (!a) {
    return <div style={{ width: size, height: size, borderRadius: "50%", background: "var(--surface-hi)" }} />;
  }
  const ring = lead || a.isLead;
  return (
    <div className={"avatar-ring" + (ring ? " lead" : "")} style={{ display: "inline-block" }}>
      <div
        className="avatar"
        style={{
          width: size,
          height: size,
          backgroundImage: `url(${a.avatar})`,
        }}
        title={`${a.name} — ${a.roleLabel}`}
      />
    </div>
  );
}

function SeverityChip({ severity }) {
  const labels = { critical: "Critical", warning: "Warning", info: "Info" };
  return (
    <span className={`sev-chip ${severity}`}>
      <span className={`dot ${severityToTone(severity) === "crit" ? "crit" : severityToTone(severity) === "warn" ? "warn" : "ok"}`}
        style={{ background: severity === "critical" ? "var(--crit)" : severity === "warning" ? "var(--warn)" : "var(--info)" }}
      />
      {labels[severity] || severity}
    </span>
  );
}

function SeverityBar({ severity }) {
  return <div className={`sev-bar sev-${severity}`} />;
}

// Tiny sparkline svg
function Sparkline({ data, width = 120, height = 28, color = "var(--accent)", fill = true }) {
  const max = Math.max(...data, 1);
  const min = Math.min(...data, 0);
  const range = max - min || 1;
  const stepX = width / Math.max(data.length - 1, 1);
  const pts = data.map((v, i) => [i * stepX, height - ((v - min) / range) * (height - 4) - 2]);
  const d = pts.map(([x, y], i) => (i === 0 ? `M${x},${y}` : `L${x},${y}`)).join(" ");
  const dFill = `${d} L${width},${height} L0,${height} Z`;
  return (
    <svg className="spark" width={width} height={height}>
      {fill && <path d={dFill} fill={color} opacity="0.12" />}
      <path d={d} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// Bar mini (todayLoad)
function Bars({ data, width = 120, height = 22, color = "var(--accent)" }) {
  const max = Math.max(...data, 1);
  const w = width / data.length - 2;
  return (
    <svg className="spark" width={width} height={height}>
      {data.map((v, i) => {
        const h = (v / max) * (height - 2);
        return <rect key={i} x={i * (w + 2)} y={height - h} width={w} height={h} fill={color} opacity={0.5 + 0.5 * (v / max)} rx="1" />;
      })}
    </svg>
  );
}

// Context gauge (semicircular)
function Gauge({ pct, size = 84 }) {
  const r = size / 2 - 8;
  const cx = size / 2;
  const cy = size / 2;
  const circumference = 2 * Math.PI * r;
  const halfCirc = circumference / 2;
  const clamped = Math.max(0, Math.min(100, pct));
  const dash = (clamped / 100) * halfCirc;
  const color = pct >= 70 ? "var(--ok)" : pct >= 30 ? "var(--warn)" : "var(--crit)";
  return (
    <div style={{ position: "relative", width: size, height: size / 2 + 8 }}>
      <svg width={size} height={size} style={{ transform: "rotate(180deg)" }}>
        <circle cx={cx} cy={cy} r={r} fill="none" stroke="var(--border)" strokeWidth="6" strokeDasharray={`${halfCirc} ${circumference}`} strokeLinecap="round" />
        <circle cx={cx} cy={cy} r={r} fill="none" stroke={color} strokeWidth="6" strokeDasharray={`${dash} ${circumference}`} strokeLinecap="round" />
      </svg>
      <div style={{
        position: "absolute", inset: 0, top: 8,
        display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
        pointerEvents: "none",
      }}>
        <div style={{ fontSize: 20, fontWeight: 600, fontFamily: "var(--font-mono)", color }}>{Math.round(pct)}%</div>
        <div style={{ fontSize: 10, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: 0.04 + "em", marginTop: -2 }}>context</div>
      </div>
    </div>
  );
}

// Severity / kind icon (inline svg, monochrome)
function KindIcon({ kind, size = 14 }) {
  const s = size;
  const stroke = "currentColor";
  switch (kind) {
    case "state_changed":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><path d="M4 7h11M4 7l3-3M4 7l3 3M20 17H9M20 17l-3 3M20 17l-3-3" /></svg>;
    case "new_task":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><path d="M12 5v14M5 12h14" /></svg>;
    case "labels_changed":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><path d="M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z" /><circle cx="8" cy="8" r="1.5" fill={stroke} /></svg>;
    case "assignees_changed":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><circle cx="9" cy="8" r="3" /><path d="M3 21c0-3.3 2.7-6 6-6s6 2.7 6 6M19 8v6M16 11h6" /></svg>;
    case "comment":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg>;
    case "alert":
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><path d="M12 9v4M12 17h.01M10.3 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /></svg>;
    default:
      return <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={stroke} strokeWidth="2"><circle cx="12" cy="12" r="9" /></svg>;
  }
}

function kindColor(kind) {
  switch (kind) {
    case "state_changed": return "var(--ok)";
    case "new_task":      return "var(--accent)";
    case "labels_changed":return "var(--warn)";
    case "assignees_changed": return "var(--info)";
    case "alert":         return "var(--crit)";
    case "comment":       return "var(--text-dim)";
    default:              return "var(--text-muted)";
  }
}

function kindLabel(kind) {
  return {
    state_changed: "state",
    new_task: "created",
    labels_changed: "label",
    assignees_changed: "assignee",
    alert: "alert",
    comment: "comment",
  }[kind] || kind;
}

function TaskRef({ seq, title, onClick }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
      <span
        className="mono"
        onClick={onClick}
        role={onClick ? "button" : undefined}
        style={{ cursor: onClick ? "pointer" : "default", color: "var(--accent)", fontSize: 12, fontWeight: 500 }}
      >
        PB-{seq}
      </span>
      {title && <span className="truncate" style={{ color: "var(--text-dim)", maxWidth: 320 }}>{title}</span>}
    </span>
  );
}

// Side panel (drawer) for details
function SidePanel({ open, onClose, title, children, width = 520 }) {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 100 }}>
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,0.4)" }} />
      <aside style={{
        position: "absolute", top: 0, right: 0, bottom: 0, width,
        background: "var(--bg-elev)", borderLeft: "1px solid var(--border)",
        display: "flex", flexDirection: "column",
        boxShadow: "-12px 0 32px rgba(0,0,0,0.4)",
      }}>
        <div style={{ padding: "16px 20px", borderBottom: "1px solid var(--border-soft)", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{title}</div>
          <button className="btn btn-ghost" onClick={onClose} style={{ padding: "4px 8px" }}>✕</button>
        </div>
        <div style={{ flex: 1, overflowY: "auto", padding: "16px 20px" }}>
          {children}
        </div>
      </aside>
    </div>
  );
}

// Confirm dialog
function ConfirmDialog({ open, title, message, danger, onConfirm, onCancel }) {
  if (!open) return null;
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(0,0,0,0.5)" }} onClick={onCancel}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: 420, background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 10, overflow: "hidden" }}>
        <div style={{ padding: "16px 20px", borderBottom: "1px solid var(--border-soft)" }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{title}</div>
        </div>
        <div style={{ padding: "16px 20px", color: "var(--text-dim)", fontSize: 13 }}>{message}</div>
        <div style={{ padding: "12px 20px", borderTop: "1px solid var(--border-soft)", display: "flex", justifyContent: "flex-end", gap: 8 }}>
          <button className="btn" onClick={onCancel}>Отмена</button>
          <button className={danger ? "btn btn-danger" : "btn btn-primary"} onClick={onConfirm}>Подтвердить</button>
        </div>
      </div>
    </div>
  );
}

// Toast (simple)
function useToasts() {
  const [toasts, setToasts] = useState([]);
  const push = (text, tone = "info") => {
    const id = Math.random().toString(36).slice(2);
    setToasts((t) => [...t, { id, text, tone }]);
    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3000);
  };
  const node = (
    <div style={{ position: "fixed", bottom: 20, right: 20, zIndex: 300, display: "flex", flexDirection: "column", gap: 8 }}>
      {toasts.map((t) => (
        <div key={t.id} style={{
          padding: "10px 14px", borderRadius: 8, fontSize: 13,
          background: "var(--surface)", border: "1px solid var(--border)",
          color: t.tone === "ok" ? "var(--ok)" : t.tone === "warn" ? "var(--warn)" : t.tone === "crit" ? "var(--crit)" : "var(--text)",
          boxShadow: "0 6px 20px rgba(0,0,0,0.4)",
          minWidth: 220,
        }}>{t.text}</div>
      ))}
    </div>
  );
  return { push, node };
}

// Module color
function moduleColor(m) {
  const map = {
    orders: "#a78bfa",
    kalk: "#f472b6",
    "tg-bot": "#60a5fa",
    seo: "#34d399",
    auth: "#fb923c",
    docs: "#94a3b8",
    scaffold: "#cbd5e1",
    watchdog: "#ec4899",
    notifications: "#f59e0b",
    spec: "#a3e635",
  };
  return map[m] || "#a1a1aa";
}

function ModuleTag({ name, small }) {
  return (
    <span
      className="mono"
      style={{
        display: "inline-flex", alignItems: "center",
        padding: small ? "2px 6px" : "3px 8px",
        borderRadius: 4, fontSize: small ? 10 : 11,
        background: "rgba(255,255,255,0.05)",
        color: moduleColor(name),
        border: `1px solid ${moduleColor(name)}33`,
        fontWeight: 500,
      }}
    >{name}</span>
  );
}

// Tiny syntax-highlighted YAML
function highlightYaml(src) {
  return src
    .split("\n")
    .map((ln) => {
      // comment
      let m = ln.match(/^(\s*)(#.*)$/);
      if (m) return `${m[1]}<span class="c">${escapeHtml(m[2])}</span>`;
      // key: value
      m = ln.match(/^(\s*-?\s*)([A-Za-z_][\w-]*)(\s*:\s*)(.*)$/);
      if (m) {
        const val = m[4];
        const valHtml = val
          ? (val.startsWith('"') || val.startsWith("'")
              ? `<span class="s">${escapeHtml(val)}</span>`
              : (/^-?\d+(\.\d+)?$/.test(val) ? `<span class="n">${escapeHtml(val)}</span>` : escapeHtml(val)))
          : "";
        return `${escapeHtml(m[1])}<span class="k">${escapeHtml(m[2])}</span>${escapeHtml(m[3])}${valHtml}`;
      }
      return escapeHtml(ln);
    })
    .join("\n");
}
function escapeHtml(s) { return s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c])); }

function YamlBlock({ code }) {
  return <pre className="code" dangerouslySetInnerHTML={{ __html: highlightYaml(code) }} />;
}

// Tiny markdown renderer (headings, lists, paragraphs, inline `code`).
// We highlight individual "points" — lines/blocks corresponding to subsections —
// by injecting className when a section title matches a known reference.
function renderMarkdown(src, highlights = {}) {
  // highlights: { "3.1": "active", "1.2": "alert" }
  const lines = src.split("\n");
  const out = [];
  let i = 0;
  let currentPt = null; // section like "3.1" or "1"
  let currentTone = null;

  const pushP = (chunk) => {
    const html = chunk
      .replace(/`([^`]+)`/g, '<code>$1</code>')
      .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
    out.push(html);
  };

  while (i < lines.length) {
    const ln = lines[i];
    // Headings — h2 (## …), h3 (### …)
    let m = ln.match(/^##\s+(\d+(?:\.\d+)?)\.\s+(.*)$/);
    if (m) {
      const pt = m[1];
      const title = m[2];
      const tone = highlights[pt];
      currentPt = pt;
      currentTone = tone;
      const ptClass = tone === "active" ? "pt pt-active" : tone === "alert" ? "pt pt-alert" : "pt";
      out.push(`<div class="${ptClass}" data-pt="${pt}"><h2>${pt}. ${escapeHtml(title)}</h2>`);
      i++;
      // collect content until next h2/h3 or eof
      let buf = [];
      while (i < lines.length && !lines[i].match(/^##\s+\d/) && !lines[i].match(/^###\s+\d/) ) {
        buf.push(lines[i]); i++;
      }
      out.push(renderBlock(buf.join("\n")));
      out.push(`</div>`);
      continue;
    }
    m = ln.match(/^###\s+(\d+\.\d+)\s+(.*)$/);
    if (m) {
      const pt = m[1];
      const title = m[2];
      const tone = highlights[pt];
      const ptClass = tone === "active" ? "pt pt-active" : tone === "alert" ? "pt pt-alert" : "pt";
      out.push(`<div class="${ptClass}" data-pt="${pt}"><h3>${pt} ${escapeHtml(title)}</h3>`);
      i++;
      let buf = [];
      while (i < lines.length && !lines[i].match(/^##\s+\d/) && !lines[i].match(/^###\s+\d/)) {
        buf.push(lines[i]); i++;
      }
      out.push(renderBlock(buf.join("\n")));
      out.push(`</div>`);
      continue;
    }
    if (ln.startsWith("# ")) {
      out.push(`<h1>${escapeHtml(ln.slice(2))}</h1>`);
      i++;
      continue;
    }
    // skip blank
    i++;
  }
  return out.join("\n");
}

function renderBlock(src) {
  const lines = src.split("\n");
  let html = "";
  let listOpen = false;
  let para = [];
  const flushPara = () => {
    if (para.length) {
      html += `<p>${para.join(" ")
        .replace(/`([^`]+)`/g, '<code>$1</code>')
        .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')}</p>`;
      para = [];
    }
  };
  const closeList = () => { if (listOpen) { html += "</ul>"; listOpen = false; } };

  for (const ln of lines) {
    if (/^\s*-\s+/.test(ln)) {
      flushPara();
      if (!listOpen) { html += "<ul>"; listOpen = true; }
      html += `<li>${ln.replace(/^\s*-\s+/, "")
        .replace(/`([^`]+)`/g, '<code>$1</code>')
        .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')}</li>`;
      continue;
    }
    closeList();
    if (ln.trim() === "") {
      flushPara();
    } else {
      para.push(escapeInline(ln));
    }
  }
  closeList();
  flushPara();
  return html;
}
function escapeInline(s) {
  return s.replace(/[<>]/g, (c) => ({ "<": "&lt;", ">": "&gt;" }[c]));
}

function Md({ src, highlights }) {
  return <div className="md" dangerouslySetInnerHTML={{ __html: renderMarkdown(src, highlights) }} />;
}

Object.assign(window, {
  Avatar, SeverityChip, SeverityBar, Sparkline, Bars, Gauge,
  KindIcon, kindColor, kindLabel,
  TaskRef, SidePanel, ConfirmDialog, useToasts,
  ModuleTag, moduleColor,
  YamlBlock, Md,
});
