// hydor-charts.jsx — dark-navy data-viz with interactive hover tooltips + KPI cards.
// Exports to window: fmt, KPICard, AreaChart, BarGroup, Donut, Sparkline, Delta, PerfBar, ChartFrame.

// ---- formatting -------------------------------------------------------------
const fmt = {
  m0: (n) => Math.round(n).toLocaleString("en-US"),
  m3: (n) => n.toLocaleString("en-US", { minimumFractionDigits: 3, maximumFractionDigits: 3 }),
  pct: (n) => (n >= 0 ? "+" : "") + n.toFixed(1) + "%",
  pctRaw: (n) => n.toFixed(1) + "%",
  int: (n) => Math.round(n).toLocaleString("en-US"),
};

// ---- floating tooltip used by all charts ------------------------------------
function ChartTip({ tip }) {
  if (!tip) return null;
  return (
    <div className="chart-tip" style={{ left: tip.left + "%", top: tip.top + "%", transform: `translate(-50%, -120%) translateY(${tip.top < 18 ? "150%" : "0"})` }}>
      {tip.title && <div className="chart-tip-title">{tip.title}</div>}
      {tip.rows.map((r, i) => (
        <div className="chart-tip-row" key={i}>
          {r.color && <i style={{ background: r.color }} />}
          <span className="chart-tip-label">{r.label}</span>
          <span className="chart-tip-val">{r.val}</span>
        </div>
      ))}
    </div>
  );
}

// ---- small delta chip -------------------------------------------------------
function Delta({ v, suffix = "%" }) {
  const up = v >= 0;
  return (
    <span className={"hx-delta " + (up ? "up" : "dn")}>
      <span className="hx-delta-arrow">{up ? "▲" : "▼"}</span>
      {Math.abs(v).toFixed(1)}{suffix}
    </span>
  );
}

// ---- KPI card (optional info tooltip via `help`) ----------------------------
function KPICard({ label, value, unit, delta, accent, spark, sub, help, onClick }) {
  return (
    <div className={"hx-kpi" + (onClick ? " hx-kpi-click" : "")} style={accent ? { "--kpi-accent": accent } : undefined} onClick={onClick}>
      <div className="hx-kpi-rail" />
      <div className="hx-kpi-body">
        <div className="hx-kpi-label">{label}{help && <InfoTip text={help} />}</div>
        <div className="hx-kpi-value">
          {value}
          {unit && <span className="hx-kpi-unit">{unit}</span>}
        </div>
        <div className="hx-kpi-foot">
          {delta != null && <Delta v={delta} />}
          {sub && <span className="hx-kpi-sub">{sub}</span>}
        </div>
      </div>
      {spark && <Sparkline data={spark} />}
    </div>
  );
}

// ---- "?" info tooltip -------------------------------------------------------
function InfoTip({ text, size = 14 }) {
  const [open, setOpen] = React.useState(false);
  return (
    <span className="infotip" onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}
      onClick={(e) => { e.stopPropagation(); setOpen((x) => !x); }} style={{ width: size, height: size }}>
      ?
      {open && <span className="infotip-pop">{text}</span>}
    </span>
  );
}

// ---- sparkline --------------------------------------------------------------
function Sparkline({ data, w = 96, h = 34, color = "var(--accent)" }) {
  const max = Math.max(...data), min = Math.min(...data);
  const rng = max - min || 1;
  const step = w / (data.length - 1 || 1);
  const pts = data.map((d, i) => [i * step, h - ((d - min) / rng) * (h - 4) - 2]);
  const line = pts.map((p) => p.join(",")).join(" ");
  const area = `0,${h} ` + line + ` ${w},${h}`;
  const id = "spk" + Math.random().toString(36).slice(2, 7);
  return (
    <svg className="hx-spark" width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.35" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <polygon points={area} fill={`url(#${id})`} />
      <polyline points={line} fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ---- area / line chart with hover -------------------------------------------
function AreaChart({ data, labels, h = 240, color = "var(--accent)", pad = 28, yfmt = fmt.int, valueFmt, unit = "" }) {
  const w = 720;
  const [hi, setHi] = React.useState(null);
  const max = (Math.max(...data, 0) || 1) * 1.12, min = 0;
  const rng = max - min || 1;
  const iw = w - pad * 2, ih = h - pad * 2;
  const step = iw / (data.length - 1 || 1);
  const x = (i) => pad + i * step;
  const y = (d) => pad + ih - ((d - min) / rng) * ih;
  const line = data.map((d, i) => `${x(i)},${y(d)}`).join(" ");
  const area = `${pad},${pad + ih} ` + line + ` ${pad + iw},${pad + ih}`;
  const grid = [0, 0.25, 0.5, 0.75, 1].map((t) => pad + ih - t * ih);
  const id = "ar" + Math.random().toString(36).slice(2, 7);
  const vf = valueFmt || fmt.m0;
  const tip = hi != null ? {
    left: (x(hi) / w) * 100, top: (y(data[hi]) / h) * 100,
    title: labels ? labels[hi] : "#" + (hi + 1),
    rows: [{ color, label: "Value", val: vf(data[hi]) + unit }],
  } : null;

  return (
    <div className="chart-wrap" style={{ height: h }}>
      <svg className="hx-chart" width="100%" height="100%" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ position: "absolute", inset: 0 }}>
        <defs>
          <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={color} stopOpacity="0.32" />
            <stop offset="100%" stopColor={color} stopOpacity="0.02" />
          </linearGradient>
        </defs>
        {grid.map((gy, i) => (
          <line key={i} x1={pad} y1={gy} x2={pad + iw} y2={gy} stroke="var(--grid)" strokeWidth="1" vectorEffect="non-scaling-stroke" />
        ))}
        <polygon points={area} fill={`url(#${id})`} />
        <polyline points={line} fill="none" stroke={color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
        {hi != null && <line x1={x(hi)} y1={pad} x2={x(hi)} y2={pad + ih} stroke={color} strokeWidth="1" strokeDasharray="3 3" vectorEffect="non-scaling-stroke" opacity="0.6" />}
      </svg>
      {/* HTML overlay: crisp dots, axis labels, hover columns */}
      <div style={{ position: "absolute", inset: 0 }}>
        {grid.map((gy, i) => (
          <span key={i} className="hx-axis-y" style={{ top: (gy / h) * 100 + "%" }}>{yfmt(max * [1, 0.75, 0.5, 0.25, 0][i])}</span>
        ))}
        {data.map((d, i) => (
          <React.Fragment key={i}>
            <div className="chart-dot" style={{ left: (x(i) / w) * 100 + "%", top: (y(d) / h) * 100 + "%", borderColor: color, opacity: hi === i ? 1 : 0.85, transform: `translate(-50%,-50%) scale(${hi === i ? 1.5 : 1})` }} />
            <div className="chart-hit" style={{ left: (x(i) / w) * 100 + "%", width: (step / w) * 100 + "%" }}
              onMouseEnter={() => setHi(i)} onMouseLeave={() => setHi(null)} />
            {labels && <span className="hx-axis-x" style={{ left: (x(i) / w) * 100 + "%" }}>{labels[i]}</span>}
          </React.Fragment>
        ))}
      </div>
      <ChartTip tip={tip} />
    </div>
  );
}

// ---- grouped bar chart with hover -------------------------------------------
function BarGroup({ series, labels, h = 240, pad = 30, valueFmt }) {
  const w = 560;
  const [hi, setHi] = React.useState(null); // {gi, si}
  const all = series.flatMap((s) => s.data);
  const max = (Math.max(...all, 0) || 1) * 1.12;
  const iw = w - pad * 2, ih = h - pad * 2;
  const groups = labels.length;
  const gw = iw / groups;
  const bw = (gw * 0.62) / series.length;
  const grid = [0, 0.25, 0.5, 0.75, 1];
  const vf = valueFmt || fmt.m0;
  let tip = null;
  if (hi) {
    const gx = pad + hi.gi * gw + gw * 0.19 + hi.si * bw + (bw - 3) / 2;
    const v = series[hi.si].data[hi.gi];
    tip = { left: (gx / w) * 100, top: ((pad + ih - (v / max) * ih) / h) * 100,
      title: labels[hi.gi], rows: series.map((s) => ({ color: s.color, label: s.name, val: vf(s.data[hi.gi]) })) };
  }
  return (
    <div className="chart-wrap" style={{ height: h }}>
      <svg className="hx-chart" width="100%" height="100%" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ position: "absolute", inset: 0 }}>
        {grid.map((t, i) => {
          const gy = pad + ih - t * ih;
          return <line key={i} x1={pad} y1={gy} x2={pad + iw} y2={gy} stroke="var(--grid)" strokeWidth="1" vectorEffect="non-scaling-stroke" />;
        })}
        {labels.map((l, gi) => {
          const gx = pad + gi * gw + gw * 0.19;
          return (
            <g key={gi}>
              {series.map((sObj, si) => {
                const v = sObj.data[gi];
                const bh = (v / max) * ih;
                const active = hi && hi.gi === gi && hi.si === si;
                return (
                  <rect key={si} x={gx + si * bw} y={pad + ih - bh} width={bw - 3} height={bh} rx="2"
                    fill={sObj.color} opacity={!hi || active ? 1 : 0.5}
                    onMouseEnter={() => setHi({ gi, si })} onMouseLeave={() => setHi(null)} style={{ cursor: "pointer" }} />
                );
              })}
            </g>
          );
        })}
      </svg>
      <div style={{ position: "absolute", inset: 0 }}>
        {grid.map((t, i) => <span key={i} className="hx-axis-y" style={{ top: ((pad + ih - t * ih) / h) * 100 + "%" }}>{fmt.int(max * t)}</span>)}
        {labels.map((l, gi) => <span key={gi} className="hx-axis-x" style={{ left: ((pad + gi * gw + gw / 2) / w) * 100 + "%" }}>{l}</span>)}
      </div>
      <ChartTip tip={tip} />
    </div>
  );
}

// ---- donut with hover -------------------------------------------------------
function Donut({ data, size = 150, thickness = 22, onHoverChange }) {
  const [hi, setHi] = React.useState(null);
  const total = data.reduce((s, d) => s + d.value, 0) || 1;
  const r = (size - thickness) / 2;
  const c = size / 2;
  const circ = 2 * Math.PI * r;
  let offset = 0;
  const segs = data.map((d, i) => { const len = (d.value / total) * circ; const o = offset; offset += len; return { d, i, len, o }; });
  return (
    <div style={{ position: "relative", width: size, height: size, flex: "none" }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="hx-donut">
        <circle cx={c} cy={c} r={r} fill="none" stroke="var(--grid)" strokeWidth={thickness} />
        {segs.map(({ d, i, len, o }) => (
          <circle key={i} cx={c} cy={c} r={r} fill="none" stroke={d.color}
            strokeWidth={hi === i ? thickness + 4 : thickness} strokeDasharray={`${len} ${circ - len}`}
            strokeDashoffset={-o} transform={`rotate(-90 ${c} ${c})`} strokeLinecap="butt"
            opacity={hi == null || hi === i ? 1 : 0.45} style={{ cursor: "pointer", transition: "stroke-width .12s, opacity .12s" }}
            onMouseEnter={() => setHi(i)} onMouseLeave={() => setHi(null)} />
        ))}
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", pointerEvents: "none", textAlign: "center" }}>
        {hi != null ? (
          <div>
            <div style={{ fontSize: 10.5, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{data[hi].label}</div>
            <div className="hx-num" style={{ fontSize: 17, fontWeight: 600, marginTop: 2 }}>{fmt.pctRaw(data[hi].value / total * 100)}</div>
          </div>
        ) : (
          <div>
            <div style={{ fontSize: 10.5, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: ".05em" }}>Total</div>
            <div className="hx-num" style={{ fontSize: 15, fontWeight: 600, marginTop: 2 }}>{fmt.m0(total)}</div>
          </div>
        )}
      </div>
    </div>
  );
}

// ---- horizontal performance bar (vs 100%) -----------------------------------
function PerfBar({ value, help }) {
  const over = value >= 100;
  const pct = Math.min(Math.abs(value), 160);
  const w = (pct / 160) * 100;
  return (
    <div className="hx-perfbar" title={`${value.toFixed(1)}% of recommended sales value` + (over ? " — above target" : " — below target")}>
      <div className="hx-perfbar-track">
        <div className="hx-perfbar-mid" />
        <div className={"hx-perfbar-fill " + (over ? "over" : "under")} style={{ width: w + "%" }} />
      </div>
      <span className={"hx-perfbar-val " + (over ? "over" : "under")}>{value.toFixed(1)}%</span>
    </div>
  );
}

Object.assign(window, { fmt, KPICard, AreaChart, BarGroup, Donut, Sparkline, Delta, PerfBar, InfoTip, ChartTip });
