// Chart primitives + UI atoms — refined editorial palette.
// Theme: cream paper background, deep ink text, terracotta accent for PM,
// indigo accent for Researcher. Health gradient stays sequential rust→amber→sage.

function healthColor(score, alpha = 1) {
  // 1 (poor) → muted rust; 5 (excellent) → muted sage
  const t = Math.max(0, Math.min(1, (score - 1) / 4));
  const hue = 25 + t * 115;
  const chroma = 0.10 + t * 0.04;
  const light = 0.58 + t * 0.04;
  return `oklch(${light} ${chroma} ${hue} / ${alpha})`;
}
function riskColor(risk) {
  if (risk === "critical") return "oklch(0.58 0.15 28)";
  if (risk === "watch") return "oklch(0.72 0.13 75)";
  return "oklch(0.62 0.10 145)";
}
function riskLabel(risk) {
  if (risk === "critical") return "Critical";
  if (risk === "watch") return "Watch";
  return "Healthy";
}
function signedN(n, d=1){ const s = n.toFixed(d); return n>=0?`+${s}`:s; }
function avg(a){ return a.length ? a.reduce((s,v)=>s+v,0)/a.length : 0; }
function stddev(arr) { const m = avg(arr); return Math.sqrt(arr.reduce((s,v)=>s+(v-m)*(v-m),0)/arr.length); }
function fmtDate(iso) {
  const d = new Date(iso + "T00:00:00");
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
}
function fmtDateLong(iso) {
  const d = new Date(iso + "T00:00:00");
  return d.toLocaleDateString("en-US", { weekday: "short", month: "long", day: "numeric", year: "numeric" });
}

const INK = "oklch(0.22 0.02 250)";
const SUBINK = "oklch(0.45 0.015 250)";
const FAINT = "oklch(0.72 0.01 250)";
const RULE = "oklch(0.88 0.005 250)";
const PAPER = "oklch(0.985 0.005 80)";
const PAPER_2 = "oklch(0.97 0.006 80)";

function Sparkline({ data, width = 120, height = 32, min = 1, max = 5, stroke, fill }) {
  if (!data || !data.length) return null;
  const pad = 2;
  const w = width - pad * 2, h = height - pad * 2;
  const dx = w / (data.length - 1 || 1);
  const pts = data.map((v, i) => [pad + i * dx, pad + h - ((v - min) / (max - min)) * h]);
  const path = pts.map(([x, y], i) => (i === 0 ? `M${x},${y}` : `L${x},${y}`)).join(" ");
  const last = data[data.length - 1];
  const c = stroke || healthColor(last);
  const f = fill || healthColor(last, 0.16);
  const area = `${path} L${pts[pts.length-1][0]},${pad+h} L${pts[0][0]},${pad+h} Z`;
  return (
    <svg width={width} height={height} className="block">
      <path d={area} fill={f}/>
      <path d={path} fill="none" stroke={c} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
      <circle cx={pts[pts.length-1][0]} cy={pts[pts.length-1][1]} r="2.5" fill={c}/>
    </svg>
  );
}

function TrendLine({ data, width = 720, height = 220, min = 1, max = 5, compare, compareLabel, label }) {
  const padL = 38, padR = 14, padT = 18, padB = 30;
  const w = width - padL - padR, h = height - padT - padB;
  const xs = (i) => padL + (i * w) / (data.length - 1 || 1);
  const ys = (v) => padT + h - ((v - min) / (max - min)) * h;
  const path = data.map((d, i) => `${i ? "L" : "M"}${xs(i)},${ys(d.score)}`).join(" ");
  const area = `${path} L${xs(data.length-1)},${padT+h} L${xs(0)},${padT+h} Z`;
  const cmpPath = compare && compare.length === data.length ? compare.map((d, i) => `${i ? "L" : "M"}${xs(i)},${ys(d.score)}`).join(" ") : null;
  const last = data[data.length - 1];
  return (
    <svg width={width} height={height} className="block">
      {[1,2,3,4,5].map(v => (
        <g key={v}>
          <line x1={padL} x2={width-padR} y1={ys(v)} y2={ys(v)} stroke={RULE} strokeDasharray={v===3?"":"2 4"}/>
          <text x={padL-8} y={ys(v)+4} fontSize="10" textAnchor="end" fill={SUBINK} fontFamily="JetBrains Mono, monospace">{v}</text>
        </g>
      ))}
      {data.map((d, i) => (
        <text key={i} x={xs(i)} y={height-12} fontSize="10" textAnchor="middle" fill={SUBINK} fontFamily="JetBrains Mono, monospace">{d.label || `W${d.week}`}</text>
      ))}
      {cmpPath && <path d={cmpPath} fill="none" stroke={SUBINK} strokeWidth="1.4" strokeDasharray="3 3"/>}
      <path d={area} fill={healthColor(last.score, 0.13)}/>
      <path d={path} fill="none" stroke={healthColor(last.score)} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
      {data.map((d, i) => <circle key={i} cx={xs(i)} cy={ys(d.score)} r="3" fill={healthColor(d.score)} stroke={PAPER} strokeWidth="1.5"/>)}
      {compareLabel && (
        <g>
          <line x1={width-padR-130} x2={width-padR-110} y1={padT-6} y2={padT-6} stroke={SUBINK} strokeDasharray="3 3" strokeWidth="1.4"/>
          <text x={width-padR-106} y={padT-2} fontSize="10" fill={SUBINK} fontFamily="JetBrains Mono, monospace">{compareLabel}</text>
        </g>
      )}
    </svg>
  );
}

function Radar({ dimensions, size = 280, max = 5, compare }) {
  const cx = size/2, cy = size/2;
  const radius = size/2 - 42;
  const n = dimensions.length;
  const ang = (i) => -Math.PI/2 + (i*2*Math.PI)/n;
  const pt = (i, v) => { const r = (v/max)*radius; return [cx+Math.cos(ang(i))*r, cy+Math.sin(ang(i))*r]; };
  const polyD = (vals) => vals.map((v,i)=>pt(i,v)).map(([x,y],i)=>`${i?"L":"M"}${x},${y}`).join(" ") + " Z";
  const teamVals = dimensions.map(d=>d.current);
  const teamAvg = avg(teamVals);
  return (
    <svg width={size} height={size} className="block">
      {[1,2,3,4,5].map(r=>{
        const pts = Array.from({length:n},(_,i)=>pt(i,r));
        const d = pts.map(([x,y],i)=>`${i?"L":"M"}${x},${y}`).join(" ")+" Z";
        return <path key={r} d={d} fill="none" stroke={r===5?"oklch(0.78 0.005 250)":RULE} strokeWidth={r===5?1:0.6}/>;
      })}
      {dimensions.map((d,i)=>{ const [x,y]=pt(i,max); return <line key={i} x1={cx} y1={cy} x2={x} y2={y} stroke={RULE} strokeWidth="0.6"/>; })}
      {compare && <path d={polyD(compare)} fill="oklch(0.55 0.01 250 / 0.06)" stroke={SUBINK} strokeWidth="1.2" strokeDasharray="3 3"/>}
      <path d={polyD(teamVals)} fill={healthColor(teamAvg, 0.18)} stroke={healthColor(teamAvg)} strokeWidth="1.8"/>
      {teamVals.map((v,i)=>{ const [x,y]=pt(i,v); return <circle key={i} cx={x} cy={y} r="3" fill={healthColor(v)} stroke={PAPER} strokeWidth="1.5"/>; })}
      {dimensions.map((d,i)=>{ const [x,y]=pt(i,max+0.5); return (
        <text key={d.id} x={x} y={y} fontSize="10" textAnchor="middle" dominantBaseline="middle" fill={INK} fontFamily="JetBrains Mono, monospace" letterSpacing="0.06em">{d.short}</text>
      ); })}
    </svg>
  );
}

function Heatmap({ teams, dimensions, onTeamClick }) {
  const cellH = 30, headerH = 32, labelW = 220, cellW = 64;
  const width = labelW + dimensions.length * cellW + 80;
  const height = headerH + teams.length * cellH;
  return (
    <svg width={width} height={height} className="block">
      {dimensions.map((d, di) => (
        <text key={d.id} x={labelW + di*cellW + cellW/2} y={headerH-10} fontSize="10" textAnchor="middle" fill={SUBINK} fontFamily="JetBrains Mono, monospace" letterSpacing="0.1em">{d.short}</text>
      ))}
      <text x={labelW + dimensions.length*cellW + 40} y={headerH-10} fontSize="10" textAnchor="middle" fill={SUBINK} fontFamily="JetBrains Mono, monospace" letterSpacing="0.1em">AVG</text>
      {teams.map((t, ti) => {
        const y = headerH + ti*cellH;
        const tAvg = avg(t.dimensions.map(d=>d.current));
        return (
          <g key={t.id} className="cursor-pointer group" onClick={()=>onTeamClick && onTeamClick(t.id)}>
            <rect x="0" y={y} width={width} height={cellH} fill="transparent" className="group-hover:fill-black/3"/>
            <text x="12" y={y+cellH/2+4} fontSize="12" fill={INK}>{t.short.length > 26 ? t.short.slice(0,26)+"…" : t.short}</text>
            {dimensions.map((d, di) => {
              const v = t.dimensions[di].current;
              return (
                <g key={d.id}>
                  <rect x={labelW + di*cellW + 4} y={y+5} width={cellW-8} height={cellH-10} rx="3" fill={healthColor(v, 0.32)} stroke={healthColor(v, 0.55)} strokeWidth="1"/>
                  <text x={labelW + di*cellW + cellW/2} y={y+cellH/2+4} fontSize="11" textAnchor="middle" fill={INK} fontFamily="JetBrains Mono, monospace">{v.toFixed(1)}</text>
                </g>
              );
            })}
            <text x={labelW + dimensions.length*cellW + 40} y={y+cellH/2+4} fontSize="11" textAnchor="middle" fill={INK} fontFamily="JetBrains Mono, monospace" fontWeight="600">{tAvg.toFixed(1)}</text>
          </g>
        );
      })}
    </svg>
  );
}

function DistBar({ buckets, width = 140, height = 36 }) {
  const segW = width/5;
  const max = Math.max(...buckets, 1);
  return (
    <svg width={width} height={height} className="block">
      {buckets.map((b, i) => {
        const score = i+1;
        const bh = (b/max) * (height-12);
        return (
          <g key={i}>
            <rect x={i*segW+2} y={height-bh-12} width={segW-4} height={Math.max(bh,2)} fill={healthColor(score, 0.65)} rx="1"/>
            <text x={i*segW+segW/2} y={height-2} fontSize="9" textAnchor="middle" fill={SUBINK} fontFamily="JetBrains Mono, monospace">{score}</text>
          </g>
        );
      })}
    </svg>
  );
}

function ResponseBar({ weekly, width = 720, height = 90, teamSize, onPick, selectedWeek }) {
  const padL = 36, padR = 12, padT = 10, padB = 28;
  const w = width-padL-padR, h = height-padT-padB;
  const max = teamSize || Math.max(...weekly.map(d=>d.responses), 1);
  const slot = w/weekly.length;
  const barW = slot - 8;
  return (
    <svg width={width} height={height} className="block">
      {weekly.map((d, i) => {
        const x = padL + i*slot + 4;
        const bh = (d.responses/max)*h;
        const y = padT + h - bh;
        const sel = selectedWeek === d.week;
        return (
          <g key={i} className={onPick?"cursor-pointer":""} onClick={()=>onPick&&onPick(d.week)}>
            <rect x={x-2} y={padT-2} width={barW+4} height={h+4} fill={sel?"oklch(0.93 0.01 80)":"transparent"} rx="3"/>
            <rect x={x} y={padT} width={barW} height={h} fill="oklch(0.93 0.005 80)" rx="2"/>
            <rect x={x} y={y} width={barW} height={bh} fill={healthColor(d.score, 0.7)} rx="2"/>
            <text x={x+barW/2} y={padT+h+15} fontSize="10" textAnchor="middle" fill={SUBINK} fontFamily="JetBrains Mono, monospace">W{d.week}</text>
            <text x={x+barW/2} y={y-4} fontSize="10" textAnchor="middle" fill={INK} fontFamily="JetBrains Mono, monospace">{d.responses}</text>
          </g>
        );
      })}
      <text x={padL-6} y={padT+6} fontSize="9" textAnchor="end" fill={SUBINK} fontFamily="JetBrains Mono, monospace">{max}</text>
      <text x={padL-6} y={padT+h+4} fontSize="9" textAnchor="end" fill={SUBINK} fontFamily="JetBrains Mono, monospace">0</text>
    </svg>
  );
}

function Bar({ value, max=5, width=120, height=8, color }) {
  return (
    <div style={{ width, height, background: "oklch(0.93 0.005 80)" }} className="rounded-full overflow-hidden">
      <div className="h-full rounded-full" style={{ width: `${(value/max)*100}%`, background: color || healthColor(value) }}/>
    </div>
  );
}

// Refined UI atoms
function Card({ className = "", children, ...rest }) {
  return <section className={"bg-white rounded-2xl border " + className} style={{ borderColor: RULE }} {...rest}>{children}</section>;
}
function CardHeader({ title, subtitle, action, eyebrow }) {
  return (
    <header className="px-6 pt-5 pb-4 flex items-start justify-between gap-4 border-b" style={{borderColor: RULE}}>
      <div>
        {eyebrow && <div className="text-[10px] font-mono tracking-[0.18em] uppercase mb-1.5" style={{color: SUBINK}}>{eyebrow}</div>}
        {title && <h3 className="text-[15px] tracking-tight font-medium" style={{color: INK}}>{title}</h3>}
        {subtitle && <p className="text-[13px] mt-0.5" style={{color: SUBINK}}>{subtitle}</p>}
      </div>
      {action}
    </header>
  );
}
function CardBody({ className = "", children }) {
  return <div className={"px-6 py-5 " + className}>{children}</div>;
}

function Pill({ children, color = "neutral", className = "" }) {
  const palette = {
    neutral: { bg: "oklch(0.95 0.005 250)", fg: INK, border: RULE },
    healthy: { bg: "oklch(0.96 0.04 145)", fg: "oklch(0.4 0.10 145)", border: "oklch(0.86 0.06 145)" },
    watch:   { bg: "oklch(0.97 0.05 75)",  fg: "oklch(0.45 0.10 75)",  border: "oklch(0.88 0.08 75)" },
    critical:{ bg: "oklch(0.96 0.04 28)",  fg: "oklch(0.45 0.13 28)",  border: "oklch(0.85 0.08 28)" },
    accent:  { bg: "oklch(0.96 0.04 270)", fg: "oklch(0.42 0.13 270)", border: "oklch(0.86 0.08 270)" },
    pm:      { bg: "oklch(0.96 0.04 38)",  fg: "oklch(0.45 0.13 38)",  border: "oklch(0.85 0.09 38)" },
  };
  const p = palette[color];
  return <span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-[10px] font-mono tracking-[0.1em] uppercase border ${className}`}
    style={{background: p.bg, color: p.fg, borderColor: p.border}}>{children}</span>;
}

function Segmented({ value, onChange, options }) {
  return (
    <div className="inline-flex p-0.5 rounded-lg border" style={{background: PAPER_2, borderColor: RULE}}>
      {options.map(o => (
        <button key={o.v} onClick={()=>onChange(o.v)}
          className={"px-3 py-1.5 text-[11px] font-mono tracking-wider rounded-md transition uppercase " +
            (value===o.v ? "bg-white shadow-sm" : "")}
          style={value===o.v ? {color: INK} : {color: SUBINK}}>
          {o.label}
        </button>
      ))}
    </div>
  );
}

function Select({ value, onChange, options, className = "" }) {
  return (
    <select value={value} onChange={(e)=>onChange(e.target.value)}
      className={"appearance-none bg-white border rounded-lg px-3 py-1.5 pr-8 text-[12px] font-mono " + className}
      style={{borderColor: RULE, color: INK, backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 5l3 3 3-3' stroke='%23666' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")", backgroundRepeat: "no-repeat", backgroundPosition: "right 8px center"}}>
      {options.map(o => <option key={o.v} value={o.v}>{o.label}</option>)}
    </select>
  );
}

// Calendar dropdown for week selection
function WeekPicker({ weeks, value, onChange, startDate = "2026-04-27" }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onClick);
    return () => document.removeEventListener("mousedown", onClick);
  }, []);
  const weekDates = React.useMemo(() => {
    const start = new Date(startDate + "T00:00:00");
    return weeks.map((w) => {
      const d = new Date(start); d.setDate(start.getDate() + (w.week - 1) * 7);
      return { ...w, date: d, label: d.toLocaleDateString("en-US", { month: "short", day: "numeric" }) };
    });
  }, [weeks, startDate]);
  const sel = weekDates.find(w => w.week === value) || weekDates[weekDates.length - 1];
  return (
    <div className="relative" ref={ref}>
      <button onClick={()=>setOpen(!open)}
        className="flex items-center gap-2 px-3 py-1.5 text-[12px] font-mono rounded-lg border bg-white hover:shadow-sm transition"
        style={{borderColor: RULE, color: INK}}>
        <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4">
          <rect x="2" y="3.5" width="12" height="11" rx="1.5"/><path d="M2 6.5h12M5.5 2v3M10.5 2v3"/>
        </svg>
        <span>Week {sel.week}</span>
        <span style={{color: SUBINK}}>· {sel.label}</span>
        <svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 5l3 3 3-3"/></svg>
      </button>
      {open && (
        <div className="absolute top-full mt-2 right-0 z-30 w-[280px] bg-white rounded-xl shadow-lg border overflow-hidden" style={{borderColor: RULE}}>
          <div className="px-4 py-3 border-b flex items-center justify-between" style={{borderColor: RULE, background: PAPER_2}}>
            <div>
              <div className="text-[10px] font-mono tracking-[0.18em] uppercase" style={{color: SUBINK}}>Pulse week</div>
              <div className="text-[13px] mt-0.5" style={{color: INK}}>{weekDates[0].label} – {weekDates[weekDates.length-1].label}, 2026</div>
            </div>
          </div>
          <div className="grid grid-cols-2 gap-1 p-2">
            {weekDates.map((w) => (
              <button key={w.week} onClick={()=>{ onChange(w.week); setOpen(false); }}
                className={"flex items-center justify-between px-3 py-2 rounded-lg text-left transition " + (w.week===value ? "" : "hover:bg-stone-50")}
                style={w.week===value ? {background: "oklch(0.95 0.04 38)"} : {}}>
                <div>
                  <div className="text-[12px] font-mono" style={{color: INK}}>Week {w.week}</div>
                  <div className="text-[11px]" style={{color: SUBINK}}>{w.label}</div>
                </div>
                <span className="text-[11px] font-mono" style={{color: healthColor(w.score)}}>{w.score.toFixed(1)}</span>
              </button>
            ))}
          </div>
          <div className="px-4 py-2.5 border-t flex items-center justify-between text-[11px]" style={{borderColor: RULE, background: PAPER_2, color: SUBINK}}>
            <span className="font-mono">Latest · W{weekDates[weekDates.length-1].week}</span>
            <button onClick={()=>{onChange(weekDates[weekDates.length-1].week); setOpen(false);}} className="font-mono uppercase tracking-wider" style={{color: INK}}>Jump to current</button>
          </div>
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  healthColor, riskColor, riskLabel, signedN, avg, stddev, fmtDate, fmtDateLong,
  INK, SUBINK, FAINT, RULE, PAPER, PAPER_2,
  Sparkline, TrendLine, Radar, Heatmap, DistBar, ResponseBar, Bar,
  Card, CardHeader, CardBody, Pill, Segmented, Select, WeekPicker,
});
