/* ============================================================
   Shared UI components  (exported to window)
   ============================================================ */

/* ---------- Icons: 24x24 stroke ---------- */
const ICON_PATHS = {
  dashboard: '<path d="M3 13h8V3H3v10Zm10 8h8V3h-8v18ZM3 21h8v-6H3v6Z"/>',
  radar: '<circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.4"/><path d="M12 12 19 6"/>',
  student: '<circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 3.5-6 8-6s8 2 8 6"/>',
  behaviour: '<path d="M5 21V4M5 4l9 1.5L20 4v10l-6 1.5L5 14"/>',
  ilp: '<path d="M19 14c1.5-1.5 3-3.5 3-5.5A3.5 3.5 0 0 0 18.5 5 4 4 0 0 0 12 6a4 4 0 0 0-6.5-1A3.5 3.5 0 0 0 2 8.5c0 2 1.5 4 3 5.5l7 7 7-7Z"/>',
  attendance: '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>',
  progress: '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>',
  coursework: '<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-6-6Z"/><path d="M14 3v6h6M9 14h6M9 17h6"/>',
  docs: '<path d="M4 19.5V5a2 2 0 0 1 2-2h13v18H6a2 2 0 0 1-2-2.5ZM6 17h13"/>',
  compliance: '<path d="M12 2 4 5v6c0 5 3.5 8 8 10 4.5-2 8-5 8-10V5l-8-3Z"/><path d="m9 12 2 2 4-4"/>',
  assistant: '<path d="M12 3v4M12 17v4M5 12H1M23 12h-4M6 6l2.5 2.5M18 18l-2.5-2.5M6 18l2.5-2.5M18 6l-2.5 2.5"/><circle cx="12" cy="12" r="3.2"/>',
  staff: '<circle cx="9" cy="8" r="3.2"/><path d="M3 20c0-3.5 2.7-5.5 6-5.5s6 2 6 5.5"/><path d="M16 5.5a3 3 0 0 1 0 5.8M21 20c0-2.6-1.3-4.4-3.5-5.2"/>',
  settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 0 1-4 0v-.1A1.6 1.6 0 0 0 6.8 19l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1A1.6 1.6 0 0 0 3 13.6H3a2 2 0 0 1 0-4h.1A1.6 1.6 0 0 0 4.6 7l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 2.7-1.1V3a2 2 0 0 1 4 0v.1A1.6 1.6 0 0 0 17 4.6l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-1.1 2.7"/>',
  search: '<circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/>',
  bell: '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0"/>',
  chevronD: '<path d="m6 9 6 6 6-6"/>',
  chevronR: '<path d="m9 6 6 6-6 6"/>',
  chevronL: '<path d="m15 6-6 6 6 6"/>',
  plus: '<path d="M12 5v14M5 12h14"/>',
  filter: '<path d="M3 5h18l-7 8v6l-4-2v-4L3 5Z"/>',
  sort: '<path d="M11 5h10M11 9h7M11 13h4M3 17l3 3 3-3M6 6v14"/>',
  arrowR: '<path d="M5 12h14M13 6l6 6-6 6"/>',
  arrowUp: '<path d="M12 19V5M6 11l6-6 6 6"/>',
  arrowDown: '<path d="M12 5v14M6 13l6 6 6-6"/>',
  close: '<path d="M18 6 6 18M6 6l12 12"/>',
  sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>',
  moon: '<path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8Z"/>',
  command: '<path d="M18 3a3 3 0 0 0-3 3v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"/>',
  check: '<path d="M20 6 9 17l-5-5"/>',
  alert: '<path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/>',
  clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
  external: '<path d="M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
  logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>',
  globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18 14 14 0 0 1 0-18Z"/>',
  download: '<path d="M12 3v12M7 10l5 5 5-5M5 21h14"/>',
  flag: '<path d="M5 21V4M5 4l9 1.5L20 4v10l-6 1.5L5 14"/>',
  doc: '<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-6-6Z"/><path d="M14 3v6h6"/>',
  send: '<path d="M22 2 11 13M22 2l-7 20-4-9-9-4 20-7Z"/>',
  spark: '<path d="m12 3 1.9 5.6L19.5 10l-4.6 1.8L13 17l-2-5.2L6.5 10l5.6-1.4L12 3Z"/>',
  dots: '<circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/>',
  menu: '<path d="M3 6h18M3 12h18M3 18h18"/>',
  pin: '<path d="M12 21s7-6.3 7-12a7 7 0 0 0-14 0c0 5.7 7 12 7 12Z"/><circle cx="12" cy="9" r="2.5"/>',
  note: '<path d="M4 4h16v12l-4 4H4V4Z"/><path d="M16 20v-4h4"/>',
  trend: '<path d="M3 17l5-5 4 4 8-9M21 7h-4M21 7v4"/>',
};

function Icon({ name, size = 18, sw = 1.7, style, className }) {
  return React.createElement("svg", {
    width: size, height: size, viewBox: "0 0 24 24", fill: "none",
    stroke: "currentColor", strokeWidth: sw, strokeLinecap: "round", strokeLinejoin: "round",
    style, className, dangerouslySetInnerHTML: { __html: ICON_PATHS[name] || "" },
  });
}

/* ---------- Avatar ---------- */
function Avatar({ name, color = "#777", size = 28, ring }) {
  const initials = name.split(" ").map((w) => w[0]).slice(0, 2).join("");
  return (
    <span style={{
      width: size, height: size, minWidth: size, borderRadius: "50%",
      background: color, color: "#fff", display: "inline-flex", alignItems: "center",
      justifyContent: "center", fontSize: size * 0.36, fontWeight: 600, letterSpacing: "0.01em",
      fontFamily: "var(--font-sans)", boxShadow: ring ? `0 0 0 2px var(--surface), 0 0 0 3px ${color}` : "none",
    }}>{initials}</span>
  );
}

/* ---------- Band badge ---------- */
function BandBadge({ band, showRange }) {
  const b = window.DB.BANDS[band];
  const cls = { critical: "crit", watch: "watch", monitor: "monitor", stable: "stable" }[band];
  return (
    <span className={"badge " + cls}>
      <span className="dot"></span>{t(b.label)}{showRange ? <span className="mono" style={{ opacity: .65, marginLeft: 2 }}>{b.range}</span> : null}
    </span>
  );
}

/* ---------- Risk score chip (the headline motif) ---------- */
function RiskScore({ score, size = "md", showBand }) {
  const band = window.DB.band(score);
  const map = {
    critical: ["var(--critical)", "var(--critical-soft)", "var(--critical-line)"],
    watch: ["oklch(0.5 0.15 55)", "var(--watch-soft)", "var(--watch-line)"],
    monitor: ["oklch(0.5 0.12 90)", "var(--monitor-soft)", "var(--monitor-line)"],
    stable: ["var(--stable)", "var(--stable-soft)", "var(--stable-line)"],
  }[band];
  const dims = { sm: [34, 15, 8], md: [44, 19, 9], lg: [60, 26, 10] }[size];
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 7 }}>
      <span style={{
        minWidth: dims[0], height: dims[0] * 0.62, padding: "0 7px", borderRadius: "var(--r)",
        background: map[1], border: `1px solid ${map[2]}`, color: map[0],
        fontFamily: "var(--font-mono)", fontSize: dims[1], fontWeight: 600,
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        fontVariantNumeric: "tabular-nums",
      }}>{score}</span>
      {showBand ? <BandBadge band={band} /> : null}
    </span>
  );
}

/* ---------- Risk bar ---------- */
function RiskBar({ score, height = 6, width = "100%" }) {
  const band = window.DB.band(score);
  const color = { critical: "var(--critical)", watch: "var(--watch)", monitor: "var(--monitor)", stable: "var(--stable)" }[band];
  return (
    <span style={{ display: "block", width, height, background: "var(--surface-2)", borderRadius: 999, overflow: "hidden", border: "1px solid var(--line)" }}>
      <span style={{ display: "block", width: score + "%", height: "100%", background: color, borderRadius: 999 }}></span>
    </span>
  );
}

/* ---------- KPI card ---------- */
function KPI({ label, value, unit, sub, delta, deltaDir, accent, onClick }) {
  return (
    <div className="panel" style={{ padding: "var(--pad)", cursor: onClick ? "pointer" : "default", transition: "border-color .12s" }}
      onClick={onClick}
      onMouseEnter={(e) => { if (onClick) e.currentTarget.style.borderColor = "var(--faint)"; }}
      onMouseLeave={(e) => { if (onClick) e.currentTarget.style.borderColor = "var(--line)"; }}>
      <div className="eyebrow" style={{ marginBottom: 12 }}>{label}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 6 }}>
        <span className="mono" style={{ fontSize: 30, fontWeight: 600, lineHeight: 1, color: accent || "var(--ink)" }}>{value}</span>
        {unit ? <span className="mono muted" style={{ fontSize: 14 }}>{unit}</span> : null}
        {delta != null ? (
          <span className="mono" style={{ marginLeft: "auto", fontSize: 12, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 2,
            color: deltaDir === "bad" ? "var(--critical)" : deltaDir === "good" ? "var(--stable)" : "var(--muted)" }}>
            <Icon name={deltaDir === "bad" ? "arrowUp" : "arrowDown"} size={12} sw={2.4} />{delta}
          </span>
        ) : null}
      </div>
      {sub ? <div className="muted" style={{ fontSize: "var(--fs-sm)", marginTop: 10 }}>{sub}</div> : null}
    </div>
  );
}

/* ---------- Sparkline ---------- */
function Sparkline({ data, w = 120, h = 32, color = "var(--accent)", fill = true }) {
  const max = Math.max(...data), min = Math.min(...data);
  const rng = max - min || 1;
  const pts = data.map((d, i) => [(i / (data.length - 1)) * w, h - 4 - ((d - min) / rng) * (h - 8)]);
  const line = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  const area = line + ` L${w} ${h} L0 ${h} Z`;
  return (
    <svg width={w} height={h} style={{ display: "block", overflow: "visible" }}>
      {fill ? <path d={area} fill={color} opacity="0.08" /> : null}
      <path d={line} fill="none" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2.4" fill={color} />
    </svg>
  );
}

/* ---------- Donut ---------- */
function Donut({ segments, size = 96, thickness = 12, center }) {
  const r = (size - thickness) / 2;
  const c = 2 * Math.PI * r;
  const tot = segments.reduce((a, s) => a + s.value, 0) || 1;
  let off = 0;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--surface-2)" strokeWidth={thickness} />
      {segments.map((s, i) => {
        const len = (s.value / tot) * c;
        const el = <circle key={i} cx={size / 2} cy={size / 2} r={r} fill="none" stroke={s.color} strokeWidth={thickness}
          strokeDasharray={`${len} ${c - len}`} strokeDashoffset={-off} transform={`rotate(-90 ${size / 2} ${size / 2})`} strokeLinecap="butt" />;
        off += len;
        return el;
      })}
      {center ? <text x="50%" y="50%" textAnchor="middle" dominantBaseline="central"
        fontFamily="var(--font-mono)" fontSize={size * 0.24} fontWeight="600" fill="var(--ink)">{center}</text> : null}
    </svg>
  );
}

/* ---------- Stacked risk bar (class composition) ---------- */
function StackBar({ parts, height = 8 }) {
  const tot = parts.reduce((a, p) => a + p.value, 0) || 1;
  return (
    <span style={{ display: "flex", width: "100%", height, borderRadius: 999, overflow: "hidden", background: "var(--surface-2)" }}>
      {parts.map((p, i) => p.value ? <span key={i} title={t(p.label)} style={{ width: (p.value / tot) * 100 + "%", background: p.color }}></span> : null)}
    </span>
  );
}

/* ---------- Section title ---------- */
function SectionTitle({ children, sub, action }) {
  return (
    <div className="between" style={{ marginBottom: 14 }}>
      <div>
        <h2 style={{ fontSize: 16 }}>{children}</h2>
        {sub ? <div className="muted" style={{ fontSize: "var(--fs-sm)", marginTop: 3 }}>{sub}</div> : null}
      </div>
      {action || null}
    </div>
  );
}

/* ---------- Grade pip (1-7) ---------- */
function Grade({ value, predicted }) {
  const col = value <= 3 ? "var(--critical)" : value === 4 ? "var(--watch)" : value >= 6 ? "var(--stable)" : "var(--ink-2)";
  return (
    <span className="mono" style={{ fontWeight: 600, color: col }}>
      {value}{predicted != null && predicted !== value ? <span className="faint" style={{ fontWeight: 400 }}> / {predicted}p</span> : null}
    </span>
  );
}

/* ---------- Trend direction arrow ---------- */
function TrendArrow({ v }) {
  if (!v) return <span className="faint mono" style={{ fontSize: 11 }}>—</span>;
  const up = v > 0;
  return <span className="mono" style={{ fontSize: 11, fontWeight: 600, color: up ? "var(--stable)" : "var(--critical)" }}>{up ? "▲" : "▼"}{Math.abs(v)}</span>;
}

/* ---------- Reusable Modal + FormModal ---------- */
function Modal({ title, subtitle, onClose, children, footer, width = 460 }) {
  return (
    <div className="cmd-overlay" style={{ alignItems: "center", paddingTop: 0 }} onMouseDown={onClose}>
      <div className="form-modal" style={{ width }} onMouseDown={(e) => e.stopPropagation()}>
        <div className="between" style={{ marginBottom: subtitle ? 4 : 16 }}>
          <h3 style={{ fontSize: 16 }}>{title}</h3>
          <button className="icon-btn" onClick={onClose}><Icon name="close" size={18} /></button>
        </div>
        {subtitle ? <p className="muted" style={{ fontSize: 12.5, marginBottom: 18 }}>{subtitle}</p> : null}
        {children}
        {footer}
      </div>
    </div>
  );
}

function FormModal({ title, subtitle, fields, submitLabel = "Save", submitIcon, onClose, onSubmit }) {
  const [vals, setVals] = React.useState(Object.fromEntries(fields.map((f) => [f.k, f.default || (f.type === "select" ? f.options[0] : "")])));
  const set = (k, v) => setVals((s) => ({ ...s, [k]: v }));
  const ready = fields.filter((f) => f.required).every((f) => String(vals[f.k] || "").trim());
  return (
    <Modal title={title} subtitle={subtitle} onClose={onClose} footer={
      <div className="row" style={{ gap: 8, marginTop: 22, justifyContent: "flex-end" }}>
        <button className="btn" onClick={onClose}>{t("Cancel")}</button>
        <button className="btn btn-primary" disabled={!ready} onClick={() => onSubmit(vals)}>{submitIcon ? <Icon name={submitIcon} size={14} /> : null}{t(submitLabel)}</button>
      </div>
    }>
      <div className="col" style={{ gap: 14 }}>
        {fields.map((f) => (
          <div className="field" key={f.k}>
            <label>{t(f.label)}</label>
            {f.type === "select" ? (
              <select className="input" value={vals[f.k]} onChange={(e) => set(f.k, e.target.value)}>{f.options.map((o) => <option key={o} value={o}>{t(o)}</option>)}</select>
            ) : f.type === "textarea" ? (
              <textarea className="input" style={{ height: 88, padding: "10px 12px", resize: "vertical", lineHeight: 1.5 }} placeholder={t(f.ph)} value={vals[f.k]} onChange={(e) => set(f.k, e.target.value)}></textarea>
            ) : (
              <input className="input" placeholder={t(f.ph)} value={vals[f.k]} onChange={(e) => set(f.k, e.target.value)} />
            )}
          </div>
        ))}
      </div>
    </Modal>
  );
}

Object.assign(window, {
  Icon, Avatar, BandBadge, RiskScore, RiskBar, KPI, Sparkline, Donut, StackBar, SectionTitle, Grade, TrendArrow, Modal, FormModal,
});
