// Shared primitives used across screens

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

function classNames(...xs) { return xs.filter(Boolean).join(" "); }

const I = (name, props = {}) => { const C = window.Icons[name]; return C ? <C {...props} /> : null; };

function Pill({ tone = "", children, dot = true }) {
  return <span className={`pill ${tone}`}>{dot && <span className="dot" />}{children}</span>;
}

function Tag({ children }) { return <span className="tag">{children}</span>; }

function Btn({ variant = "", size = "", children, onClick, icon, title, disabled = false }) {
  return (
    <button className={classNames("btn", variant, size, disabled && "is-disabled")} onClick={disabled ? undefined : onClick} title={title} disabled={disabled} aria-disabled={disabled || undefined}>
      {icon && I(icon, { size: 13 })}
      {children}
    </button>
  );
}

// Global toast helper — inert/coming-in-v2 buttons can call window.forgeToast(msg)
// without wiring setToast through every screen. The App shell listens for this event.
if (typeof window !== "undefined" && typeof window.forgeToast !== "function") {
  window.forgeToast = function(msg) {
    try { window.dispatchEvent(new CustomEvent("forge:toast", { detail: msg })); }
    catch (e) { /* no-op */ }
  };
}

function Panel({ title, right, children, className, bodyClass, style }) {
  return (
    <div className={classNames("panel", className)} style={Object.assign({ display: "flex", flexDirection: "column", minHeight: 0, minWidth: 0 }, style || {})}>
      {title && (
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between",
          padding: "8px 12px", borderBottom: "1px solid var(--line-soft)", gap: 8, minWidth: 0 }}>
          {/* Title shrinks first + ellipses so a long "Activity · KESTREL-3 timeline" doesn't get
              clipped by the fixed floating UserSwitcher pill at top:14, right:96. */}
          <div style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 0, flex: "1 1 0", overflow: "hidden" }}>
            <span className="label" style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{title}</span>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 6, flexShrink: 0 }}>{right}</div>
        </div>
      )}
      <div className={bodyClass} style={{ minHeight: 0, flex: 1, overflow: "auto" }}>{children}</div>
    </div>
  );
}

function StatPill({ label, value, delta, trend }) {
  return (
    <div style={{ padding: "10px 12px", borderRight: "1px solid var(--line-soft)", minWidth: 0 }}>
      <div className="label" style={{ marginBottom: 4, whiteSpace: "normal", overflow: "visible", textOverflow: "clip", lineHeight: 1.2 }}>{label}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8, flexWrap: "wrap" }}>
        <div className="mono tnum" style={{ fontSize: 20, letterSpacing: "-0.01em" }}>{value}</div>
        <div className="mono tnum" style={{ fontSize: 12, minWidth: 0, color: trend === "up" ? "var(--ok)" : trend === "down" ? "var(--info)" : "var(--ink-3)" }}>
          {trend === "up" ? "▲" : trend === "down" ? "▼" : "·"} {delta}
        </div>
      </div>
    </div>
  );
}

// Sparkline
function Spark({ data, height = 26, stroke = "var(--cool)", fill = true }) {
  const w = 120, h = height;
  const min = Math.min(...data), max = Math.max(...data);
  const rng = max - min || 1;
  const pts = data.map((v, i) => [i * (w / (data.length - 1)), h - ((v - min) / rng) * (h - 2) - 1]);
  const d = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(" ");
  const area = `${d} L${w},${h} L0,${h} Z`;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width={w} height={h} preserveAspectRatio="none">
      {fill && <path d={area} fill={stroke} opacity="0.12" />}
      <path d={d} stroke={stroke} fill="none" strokeWidth="1.2" />
    </svg>
  );
}

function Progress({ value = 0, color = "var(--accent)", height = 4 }) {
  const scale = Math.min(1, Math.max(0, value));
  return (
    <div style={{ height, background: "var(--bg-3)", borderRadius: height, overflow: "hidden" }}>
      <div style={{ width: "100%", height: "100%", background: color, transform: `scaleX(${scale})`, transformOrigin: "left center", transition: "transform .3s ease" }} />
    </div>
  );
}

// Tree node
function Tree({ node, depth = 0, open, setOpen, onSelect, selectedPn, decorate }) {
  const id = node.pn || node.id;
  const isOpen = open[id] ?? depth < 1;
  const hasKids = node.children && node.children.length;
  const _treeRowStyle = {
    display: "grid",
    gridTemplateColumns: `minmax(0, 1fr) 54px 44px 54px 90px 70px`,
    alignItems: "center",
    height: "var(--density-row)",
    fontSize: 12,
    borderBottom: "1px solid var(--line-soft)",
    background: selectedPn === node.pn ? "color-mix(in oklch, var(--accent) 14%, transparent)" : "transparent",
    cursor: "pointer",
  };
  return (
    <>
      <div
        className="mono"
        role="button"
        tabIndex={0}
        onClick={() => { if (hasKids) setOpen(prev => ({ ...prev, [id]: !isOpen })); onSelect && onSelect(node); }}
        onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); if (hasKids) setOpen(prev => ({ ...prev, [id]: !isOpen })); onSelect && onSelect(node); } }}
        style={_treeRowStyle}
      >
        <div style={{ paddingLeft: 8 + depth * 14, display: "flex", alignItems: "center", gap: 4, color: "var(--ink)", minWidth: 0 }}>
          <span style={{ width: 12, color: "var(--ink-3)" }}>
            {hasKids ? (isOpen ? "▾" : "▸") : "·"}
          </span>
          <span style={{ color: "var(--ink-3)", marginRight: 4 }}>{node.pn}</span>
          <span style={{ color: "var(--ink-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{node.name}</span>
        </div>
        <div style={{ color: "var(--ink-3)" }}>{node.type}</div>
        <div className="tnum">{node.qty}</div>
        <div>{node.rev}</div>
        <div style={{ color: "var(--ink-3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{node.mat}</div>
        <div>
          {decorate ? decorate(node) : (
            <Pill tone={node.status === "released" ? "ok" : node.status === "in-work" ? "warn" : ""}>{node.status || "—"}</Pill>
          )}
        </div>
      </div>
      {hasKids && isOpen && node.children.map((c, i) => (
        <Tree key={c.pn || i} node={c} depth={depth + 1} open={open} setOpen={setOpen} onSelect={onSelect} selectedPn={selectedPn} decorate={decorate} />
      ))}
    </>
  );
}

function TreeHeader({ cols }) {
  const _treeHeaderStyle = {
    display: "grid", gridTemplateColumns: cols, height: 26, alignItems: "center",
    borderBottom: "1px solid var(--line)", background: "var(--bg-1)", position: "sticky", top: 0, zIndex: 2,
  };
  return (
    <div className="label" style={_treeHeaderStyle}>
      <div style={{ paddingLeft: 8 }}>Part · Name</div>
      <div>Type</div>
      <div>Qty</div>
      <div>Rev</div>
      <div>Material</div>
      <div>Status</div>
    </div>
  );
}

// Command palette (decorative)
function CommandBar() {
  const [v, setV] = useState("");
  const _cmdBarStyle = {
    display: "flex", alignItems: "center", gap: 8, padding: "4px 10px", background: "var(--bg-2)",
    border: "1px solid var(--line)", borderRadius: 5, minWidth: 380,
  };
  return (
    <div style={_cmdBarStyle}>
      {I("Search", { size: 12 })}
      <input id="ui-f1" name="ui-f1"
        value={v} onChange={e => setV(e.target.value)}
        placeholder="Go to part, WO, ECO, or ask Forge…"
        style={{ flex: 1, background: "transparent", border: 0, outline: "0 solid transparent", color: "var(--ink)", font: "inherit" }}
      />
      <span className="kbd">⌘K</span>
    </div>
  );
}

// Striped placeholder w/ label
function Stripes({ label, h = "100%", children, style }) {
  return (
    <div className="stripe-placeholder" style={{ height: h, ...style }}>
      <div className="lbl">{label}</div>
      {children}
    </div>
  );
}

// A tiny ascii-bar (0..1)
function Bar({ v, w = 80, color = "var(--cool)" }) {
  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
      <div style={{ width: w, height: 4, background: "var(--bg-3)", borderRadius: 2, overflow: "hidden" }}>
        <div style={{ width: `${Math.round(v * 100)}%`, height: "100%", background: color }} />
      </div>
      <span className="mono tnum" style={{ fontSize: 12, color: "var(--ink-3)" }}>{Math.round(v * 100)}%</span>
    </div>
  );
}

// Timecode ticker (looks live)
function Ticker() {
  const [t, setT] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setT(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const p = (n) => String(n).padStart(2, "0");
  return <span className="mono tnum" style={{ color: "var(--ink-3)", fontSize: 12 }}>
    {t.getUTCFullYear()}-{p(t.getUTCMonth()+1)}-{p(t.getUTCDate())} · {p(t.getUTCHours())}:{p(t.getUTCMinutes())}:{p(t.getUTCSeconds())} UTC
  </span>;
}

Object.assign(window, {
  classNames, I, Pill, Tag, Btn, Panel, StatPill, Spark, Progress,
  Tree, TreeHeader, CommandBar, Stripes, Bar, Ticker,
});
