// JARVIS v3 — shared chart primitives + UI atoms

function Sparkline({ values, w = 100, h = 28, color = "currentColor", fill = "none", strokeWidth = 1.4, showDot = false, responsive = false }) {
  if (!values || values.length < 2) return null;
  const min = Math.min(...values);
  const max = Math.max(...values);
  const range = (max - min) || 1;
  const stepX = w / (values.length - 1);
  const pts = values.map((v, i) => [i * stepX, h - ((v - min) / range) * (h - 4) - 2]);
  const d = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(2) + "," + p[1].toFixed(2)).join(" ");
  let area = null;
  if (fill !== "none") {
    const aD = d + ` L${w},${h} L0,${h} Z`;
    area = <path d={aD} fill={fill} stroke="none" />;
  }
  const last = pts[pts.length - 1];
  const svgProps = responsive
    ? { width: "100%", height: h, style: { display: "block" } }
    : { width: w, height: h };
  return (
    <svg {...svgProps} className="spark" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
      {area}
      <path d={d} fill="none" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" />
      {showDot && <circle cx={last[0]} cy={last[1]} r="2.5" fill={color} />}
    </svg>
  );
}

function DonutRing({ value = 0, max = 100, size = 56, stroke = 6, color = "#3f9d6d", track = "#EDECE6", label }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const pct = Math.max(0, Math.min(1, value / max));
  const dash = c * pct;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={track} strokeWidth={stroke} />
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
        strokeDasharray={`${dash} ${c - dash}`}
        strokeDashoffset={c * 0.25}
        strokeLinecap="round"
        transform={`rotate(-90 ${size/2} ${size/2}) rotate(90 ${size/2} ${size/2})`}
      />
    </svg>
  );
}

function Ring({ value, label, sub, color, size = 64 }) {
  return (
    <div className="ring-wrap">
      <div style={{position: "relative", width: size, height: size}}>
        <DonutRing value={value} size={size} color={color} stroke={6} />
        <div style={{position: "absolute", inset: 0, display: "grid", placeItems: "center"}}>
          <div className="val">{value}</div>
        </div>
      </div>
      <div className="lbl">{label}</div>
      {sub ? <div className="sub">{sub}</div> : null}
    </div>
  );
}

function CashFlowChart({ data }) {
  const w = 420, h = 200, pad = { l: 28, r: 8, t: 12, b: 24 };
  const max = Math.max(...data.flatMap(d => [d.in, d.out])) * 1.1;
  const innerW = w - pad.l - pad.r;
  const innerH = h - pad.t - pad.b;
  const groupW = innerW / data.length;
  const bw = groupW * 0.35;
  const ticks = [0, max/4, max/2, (3*max)/4, max];
  return (
    <svg width="100%" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{display: "block"}}>
      {ticks.map((t, i) => {
        const y = pad.t + innerH - (t / max) * innerH;
        return (
          <g key={i}>
            <line x1={pad.l} y1={y} x2={w - pad.r} y2={y} stroke="#EDECE6" strokeWidth="1" strokeDasharray={i === 0 ? "" : "2 3"} />
            <text x={pad.l - 6} y={y + 3} fontSize="9" fill="#8A8A92" textAnchor="end" fontFamily="IBM Plex Mono">
              ${Math.round(t/1000)}k
            </text>
          </g>
        );
      })}
      {data.map((d, i) => {
        const gx = pad.l + i * groupW + groupW/2;
        const hIn = (d.in / max) * innerH;
        const hOut = (d.out / max) * innerH;
        const yIn = pad.t + innerH - hIn;
        const yOut = pad.t + innerH - hOut;
        return (
          <g key={i}>
            <rect x={gx - bw - 1} y={yIn} width={bw} height={hIn} fill="oklch(0.62 0.15 145)" rx="1" />
            <rect x={gx + 1} y={yOut} width={bw} height={hOut} fill="#1c1c20" rx="1" />
            <text x={gx} y={h - 8} fontSize="9.5" fill="#8A8A92" textAnchor="middle" fontFamily="IBM Plex Mono">{d.m}</text>
          </g>
        );
      })}
    </svg>
  );
}

function StackedBar({ segments, h = 6 }) {
  const total = segments.reduce((s, x) => s + x.value, 0) || 1;
  return (
    <div style={{display: "flex", width: "100%", height: h, borderRadius: 3, overflow: "hidden", background: "#EDECE6"}}>
      {segments.map((s, i) => (
        <div key={i} style={{width: ((s.value/total)*100) + "%", background: s.color}} title={`${s.label} ${s.value}`}/>
      ))}
    </div>
  );
}

function LineChart({ series, w = 600, h = 220, pad = { l: 36, r: 12, t: 16, b: 26 }, yFmt = (v) => v, xLabels = [] }) {
  const allVals = series.flatMap(s => s.values.filter(v => v != null));
  if (!allVals.length) return null;
  const min = Math.min(...allVals);
  const max = Math.max(...allVals);
  const range = (max - min) || 1;
  const innerW = w - pad.l - pad.r;
  const innerH = h - pad.t - pad.b;
  const N = Math.max(...series.map(s => s.values.length));
  const xAt = (i) => pad.l + (i * innerW) / (N - 1);
  const yAt = (v) => pad.t + innerH - ((v - min) / range) * innerH;
  const ticks = 4;
  return (
    <svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{display: "block"}}>
      {Array.from({length: ticks + 1}).map((_, i) => {
        const v = min + (range * i) / ticks;
        const y = yAt(v);
        return (
          <g key={i}>
            <line x1={pad.l} y1={y} x2={w - pad.r} y2={y} stroke="#EDECE6" strokeDasharray={i === 0 ? "" : "2 3"} />
            <text x={pad.l - 6} y={y + 3} fontSize="9" fill="#8A8A92" textAnchor="end" fontFamily="IBM Plex Mono">{yFmt(v)}</text>
          </g>
        );
      })}
      {series.map((s, si) => {
        const pts = s.values.map((v, i) => v != null ? xAt(i).toFixed(1) + "," + yAt(v).toFixed(1) : null);
        let segments = [];
        let cur = [];
        pts.forEach((p) => {
          if (p != null) { cur.push(p); }
          else if (cur.length) { segments.push(cur); cur = []; }
        });
        if (cur.length) segments.push(cur);
        return segments.map((seg, segi) => (
          <path key={si + "-" + segi} d={seg.map((p, i) => (i === 0 ? "M" : "L") + p).join(" ")} fill="none" stroke={s.color} strokeWidth={s.strokeWidth || 1.6} strokeLinecap="round" strokeLinejoin="round" strokeDasharray={s.dash || ""}/>
        ));
      })}
      {xLabels.map((lbl, i) => (
        <text key={i} x={xAt(i)} y={h - 8} fontSize="9.5" fill="#8A8A92" textAnchor="middle" fontFamily="IBM Plex Mono">{lbl}</text>
      ))}
    </svg>
  );
}

function Progress({ value, max = 100, color = "var(--fg)", height = 4 }) {
  return (
    <div className="bar-track" style={{height}}>
      <div className="bar-fill" style={{width: ((value/max)*100) + "%", background: color}} />
    </div>
  );
}

function StatusPill({ status }) {
  const map = {
    live: { cls: "blue", txt: "LIVE" },
    passed: { cls: "green", txt: "PASSED" },
    "not-started": { cls: "", txt: "NOT STARTED" },
    active: { cls: "green", txt: "ACTIVE" },
    stalled: { cls: "red", txt: "STALLED" },
    blocked: { cls: "red", txt: "BLOCKED" },
    scheduled: { cls: "blue", txt: "SCHEDULED" },
    paused: { cls: "amber", txt: "PAUSED" },
    lead: { cls: "", txt: "LEAD" },
    draft: { cls: "", txt: "DRAFT" }
  };
  const v = map[status] || { cls: "", txt: status.toUpperCase() };
  return <span className={"pill " + v.cls}>{v.txt}</span>;
}

function fmtMoney(n, withSign) {
  const sign = n < 0 ? "−" : (withSign && n > 0 ? "+" : "");
  const abs = Math.abs(n);
  if (abs >= 1000000) return sign + "$" + (abs/1000000).toFixed(2) + "M";
  if (abs >= 10000) return sign + "$" + Math.round(abs/1000) + "k";
  if (abs >= 1000) return sign + "$" + (abs/1000).toFixed(1) + "k";
  return sign + "$" + Math.round(abs).toLocaleString();
}
function fmtMoneyFull(n, withSign) {
  const sign = n < 0 ? "−" : (withSign && n > 0 ? "+" : "");
  return sign + "$" + Math.abs(n).toLocaleString();
}

Object.assign(window, {
  Sparkline, DonutRing, Ring, CashFlowChart, StackedBar, LineChart, Progress, StatusPill, fmtMoney, fmtMoneyFull
});
