/* global React */
// ============================================================
// Trekko — lightweight SVG charts (Stripe-flavored)
// ============================================================

var { useState, useMemo, useRef, useEffect } = React;

const CHART_PALETTE = ["#635bff", "#00d4ff", "#0e7c66", "#ff9f43", "#d4366a", "#4b45c4", "#a78bfa"];

// ---------- shared layout helpers ----------
function useTooltip() {
  const [tt, setTt] = useState(null);
  return [tt, setTt];
}

function ChartTooltip({ x, y, children }) {
  if (x == null) return null;
  return (
    <div
      style={{
        position: "absolute",
        left: x, top: y,
        transform: "translate(-50%, -100%) translateY(-10px)",
        background: "#ffffff",
        border: "1px solid var(--border)",
        boxShadow: "var(--shadow-md)",
        borderRadius: 8,
        padding: "8px 10px",
        fontSize: 12,
        color: "var(--ink-1)",
        pointerEvents: "none",
        zIndex: 5,
        whiteSpace: "nowrap",
        lineHeight: 1.45,
      }}
    >
      {children}
    </div>
  );
}

// ============================================================
// LineChart — multi-series
// data: [{ label: "Jan", a: 12, b: 9 }, ...]
// series: [{ key: "a", name: "Booked", color: "#635bff" }, ...]
// ============================================================
function LineChart({ data, series, height = 240, valueFmt = (v) => v, smooth = true, area = true }) {
  const wrapRef = useRef(null);
  const [W, setW] = useState(600);
  useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);
  const H = height;
  const pad = { t: 16, r: 16, b: 28, l: 44 };
  const innerW = Math.max(40, W - pad.l - pad.r);
  const innerH = Math.max(40, H - pad.t - pad.b);
  const allVals = data.flatMap((d) => series.map((s) => d[s.key] ?? 0));
  const maxV = Math.max(1, ...allVals);
  const minV = Math.min(0, ...allVals);
  const yTicks = 4;
  const x = (i) => pad.l + (data.length === 1 ? innerW / 2 : (i / (data.length - 1)) * innerW);
  const y = (v) => pad.t + innerH - ((v - minV) / (maxV - minV || 1)) * innerH;

  const path = (key) => {
    const pts = data.map((d, i) => [x(i), y(d[key] ?? 0)]);
    if (!smooth || pts.length < 2) return pts.map((p, i) => (i === 0 ? `M${p[0]} ${p[1]}` : `L${p[0]} ${p[1]}`)).join(" ");
    let d = `M${pts[0][0]} ${pts[0][1]}`;
    for (let i = 0; i < pts.length - 1; i++) {
      const [x0, y0] = pts[i];
      const [x1, y1] = pts[i + 1];
      const cx = (x0 + x1) / 2;
      d += ` C${cx} ${y0} ${cx} ${y1} ${x1} ${y1}`;
    }
    return d;
  };
  const areaPath = (key) => `${path(key)} L${x(data.length - 1)} ${pad.t + innerH} L${x(0)} ${pad.t + innerH} Z`;

  const [hover, setHover] = useState(null);

  const onMove = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const px = e.clientX - rect.left;
    const idx = Math.round(((px - pad.l) / innerW) * (data.length - 1));
    if (idx >= 0 && idx < data.length) setHover(idx);
  };

  return (
    <div ref={wrapRef} style={{ position: "relative", width: "100%" }}>
      <svg width={W} height={H} onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
        {/* gridlines + Y labels */}
        {Array.from({ length: yTicks + 1 }).map((_, i) => {
          const v = minV + ((maxV - minV) * i) / yTicks;
          const yy = y(v);
          return (
            <g key={i}>
              <line x1={pad.l} x2={W - pad.r} y1={yy} y2={yy} stroke="var(--c-grid)" strokeDasharray={i === 0 ? "0" : "0"} />
              <text x={pad.l - 8} y={yy + 4} textAnchor="end" fontSize="10.5" fill="var(--ink-3)">
                {valueFmt(Math.round(v))}
              </text>
            </g>
          );
        })}
        {/* X labels */}
        {data.map((d, i) => (
          <text key={i} x={x(i)} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--ink-3)">{d.label}</text>
        ))}
        {/* areas */}
        {area && series.map((s) => (
          <path key={"a" + s.key} d={areaPath(s.key)} fill={s.color} opacity="0.08" />
        ))}
        {/* lines */}
        {series.map((s) => (
          <path key={"l" + s.key} d={path(s.key)} fill="none" stroke={s.color} strokeWidth="2" />
        ))}
        {/* hover dots & vertical line */}
        {hover != null && (
          <g>
            <line x1={x(hover)} x2={x(hover)} y1={pad.t} y2={pad.t + innerH} stroke="var(--ink-4)" strokeDasharray="3 3" />
            {series.map((s) => (
              <circle key={"dot" + s.key} cx={x(hover)} cy={y(data[hover][s.key] ?? 0)} r="4" fill="#fff" stroke={s.color} strokeWidth="2" />
            ))}
          </g>
        )}
      </svg>
      {hover != null && (
        <ChartTooltip x={x(hover)} y={pad.t}>
          <div style={{ fontWeight: 600, marginBottom: 4 }}>{data[hover].label}</div>
          {series.map((s) => (
            <div key={s.key} style={{ display: "flex", gap: 10, alignItems: "center" }}>
              <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }} />
              <span style={{ color: "var(--ink-3)" }}>{s.name}</span>
              <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)" }}>{valueFmt(data[hover][s.key] ?? 0)}</span>
            </div>
          ))}
        </ChartTooltip>
      )}
    </div>
  );
}

// ============================================================
// BarChart — grouped or stacked, vertical
// data: [{ label, a, b }, ...]
// series: [{ key, name, color }]
// ============================================================
function BarChart({ data, series, height = 240, valueFmt = (v) => v, stacked }) {
  const wrapRef = useRef(null);
  const [W, setW] = useState(600);
  useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);
  const H = height;
  const pad = { t: 16, r: 16, b: 28, l: 44 };
  const innerW = Math.max(40, W - pad.l - pad.r);
  const innerH = Math.max(40, H - pad.t - pad.b);
  const groupW = innerW / data.length;
  const barW = stacked ? Math.min(36, groupW * 0.55) : Math.min(22, (groupW - 6) / series.length);
  const allVals = stacked
    ? data.map((d) => series.reduce((a, s) => a + (d[s.key] ?? 0), 0))
    : data.flatMap((d) => series.map((s) => d[s.key] ?? 0));
  const maxV = Math.max(1, ...allVals);
  const yTicks = 4;
  const y = (v) => pad.t + innerH - (v / maxV) * innerH;
  const [hover, setHover] = useState(null);
  return (
    <div ref={wrapRef} style={{ position: "relative", width: "100%" }}>
      <svg width={W} height={H}>
        {Array.from({ length: yTicks + 1 }).map((_, i) => {
          const v = (maxV * i) / yTicks;
          const yy = y(v);
          return (
            <g key={i}>
              <line x1={pad.l} x2={W - pad.r} y1={yy} y2={yy} stroke="var(--c-grid)" />
              <text x={pad.l - 8} y={yy + 4} textAnchor="end" fontSize="10.5" fill="var(--ink-3)">{valueFmt(Math.round(v))}</text>
            </g>
          );
        })}
        {data.map((d, i) => {
          const cx = pad.l + groupW * i + groupW / 2;
          let stackTop = pad.t + innerH;
          return (
            <g key={i} onMouseEnter={() => setHover({ i, x: cx, y: pad.t })} onMouseLeave={() => setHover(null)}>
              <rect x={pad.l + groupW * i} y={pad.t} width={groupW} height={innerH} fill="transparent" />
              {series.map((s, j) => {
                const v = d[s.key] ?? 0;
                const h = (v / maxV) * innerH;
                if (stacked) {
                  const yy = stackTop - h;
                  stackTop -= h;
                  return <rect key={s.key} x={cx - barW / 2} y={yy} width={barW} height={h} fill={s.color} rx="2"
                               opacity={hover && hover.i === i ? 1 : 0.92} />;
                }
                const x0 = cx - (series.length * (barW + 3) - 3) / 2 + j * (barW + 3);
                const yy = pad.t + innerH - h;
                return <rect key={s.key} x={x0} y={yy} width={barW} height={h} fill={s.color} rx="2"
                             opacity={hover && hover.i === i ? 1 : 0.92} />;
              })}
              <text x={cx} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--ink-3)">{d.label}</text>
            </g>
          );
        })}
      </svg>
      {hover && (
        <ChartTooltip x={hover.x} y={hover.y}>
          <div style={{ fontWeight: 600, marginBottom: 4 }}>{data[hover.i].label}</div>
          {series.map((s) => (
            <div key={s.key} style={{ display: "flex", gap: 10, alignItems: "center" }}>
              <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }} />
              <span style={{ color: "var(--ink-3)" }}>{s.name}</span>
              <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)" }}>{valueFmt(data[hover.i][s.key] ?? 0)}</span>
            </div>
          ))}
        </ChartTooltip>
      )}
    </div>
  );
}

// ============================================================
// Donut chart
// data: [{ label, value, color }]
// ============================================================
function Donut({ data, size = 130, thickness = 18, centerLabel, centerValue }) {
  const total = data.reduce((a, d) => a + d.value, 0) || 1;
  const r = (size - thickness) / 2;
  const c = size / 2;
  const C = 2 * Math.PI * r;
  let offset = 0;
  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: 14 }}>
      <svg width={size} height={size} style={{ display: "block" }}>
        <circle cx={c} cy={c} r={r} fill="none" stroke="var(--c-grid)" strokeWidth={thickness} />
        {data.map((d, i) => {
          const frac = d.value / total;
          const len = frac * C;
          const dash = `${len} ${C - len}`;
          const el = (
            <circle key={i} cx={c} cy={c} r={r} fill="none"
                    stroke={d.color || CHART_PALETTE[i % CHART_PALETTE.length]}
                    strokeWidth={thickness}
                    strokeDasharray={dash}
                    strokeDashoffset={-offset}
                    transform={`rotate(-90 ${c} ${c})`}
                    strokeLinecap="butt" />
          );
          offset += len;
          return el;
        })}
        {centerValue && (
          <g>
            <text x={c} y={c - 4} textAnchor="middle" fontSize="16" fontWeight="600" fill="var(--ink-1)">{centerValue}</text>
            {centerLabel && <text x={c} y={c + 14} textAnchor="middle" fontSize="10.5" fill="var(--ink-3)">{centerLabel}</text>}
          </g>
        )}
      </svg>
    </div>
  );
}

// Donut + legend block
function DonutCard({ data, total, totalLabel = "Total" }) {
  const sum = data.reduce((a, d) => a + d.value, 0);
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 18 }}>
      <Donut data={data} centerValue={(total ?? sum).toLocaleString()} centerLabel={totalLabel} />
      <div style={{ flex: 1, minWidth: 0 }}>
        {data.map((d, i) => (
          <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6, fontSize: 13 }}>
            <span style={{ width: 10, height: 10, borderRadius: 3, background: d.color || CHART_PALETTE[i % CHART_PALETTE.length] }} />
            <span style={{ color: "var(--ink-2)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.label}</span>
            <span className="tabular bold">{d.value.toLocaleString()}</span>
            <span className="tiny muted" style={{ minWidth: 36, textAlign: "right" }}>{Math.round((d.value / (sum || 1)) * 100)}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ============================================================
// Gauge — semicircle indicator
// ============================================================
function Gauge({ value = 70, color = "#635bff", size = 110, label }) {
  const stroke = 12;
  const r = (size - stroke) / 2;
  const cx = size / 2; const cy = size - 8;
  // arc from 180→0
  const arcLen = Math.PI * r;
  const off = arcLen * (1 - value / 100);
  return (
    <div style={{ position: "relative", width: size, height: size / 2 + 22, display: "inline-block" }}>
      <svg width={size} height={size / 2 + 12} style={{ overflow: "visible" }}>
        <path d={`M ${stroke / 2} ${cy} A ${r} ${r} 0 0 1 ${size - stroke / 2} ${cy}`} fill="none" stroke="var(--c-grid)" strokeWidth={stroke} strokeLinecap="round" />
        <path d={`M ${stroke / 2} ${cy} A ${r} ${r} 0 0 1 ${size - stroke / 2} ${cy}`}
              fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round"
              strokeDasharray={arcLen} strokeDashoffset={off} />
        <text x={cx} y={cy - 6} textAnchor="middle" fontSize="20" fontWeight="700" fill="var(--ink-1)" letterSpacing="-0.02em">{value}%</text>
      </svg>
      {label && <div className="tiny muted center" style={{ marginTop: 2 }}>{label}</div>}
    </div>
  );
}

// ============================================================
// Sparkline
// ============================================================
function Sparkline({ values = [], width = 120, height = 32, color = "var(--indigo)", area = true }) {
  if (!values.length) return null;
  const max = Math.max(...values), min = Math.min(...values);
  const range = max - min || 1;
  const x = (i) => (i / (values.length - 1 || 1)) * (width - 2) + 1;
  const y = (v) => height - 2 - ((v - min) / range) * (height - 4);
  const pts = values.map((v, i) => [x(i), y(v)]);
  const line = pts.map((p, i) => (i === 0 ? `M${p[0]} ${p[1]}` : `L${p[0]} ${p[1]}`)).join(" ");
  const filled = `${line} L${width - 1} ${height - 1} L1 ${height - 1} Z`;
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      {area && <path d={filled} fill={color} opacity="0.13" />}
      <path d={line} fill="none" stroke={color} strokeWidth="1.6" />
    </svg>
  );
}

// ============================================================
// Progress bar
// ============================================================
function ProgressBar({ value = 50, max = 100, color = "var(--indigo)", height = 6 }) {
  const pct = Math.min(100, Math.max(0, (value / max) * 100));
  return (
    <div style={{ background: "var(--bg-subtle)", borderRadius: 999, height, overflow: "hidden" }}>
      <div style={{ width: pct + "%", height: "100%", background: color, borderRadius: 999, transition: "width 240ms" }} />
    </div>
  );
}

// ============================================================
// AreaChart — Stripe-style filled area with smooth gradient + glow
// data: [{ label: "Jan", a: 12, b: 9 }, ...]
// series: [{ key: "a", name: "Booked", color: "#635bff" }, ...]
// ============================================================
function AreaChart({ data, series, height = 240, valueFmt = (v) => v }) {
  var { useState, useRef, useEffect } = React;
  const wrapRef = useRef(null);
  const [W, setW] = useState(600);
  useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);
  const H = height;
  const pad = { t: 18, r: 18, b: 28, l: 48 };
  const innerW = Math.max(40, W - pad.l - pad.r);
  const innerH = Math.max(40, H - pad.t - pad.b);
  const allVals = data.flatMap((d) => series.map((s) => d[s.key] ?? 0));
  const maxV = Math.max(1, ...allVals) * 1.08;
  const minV = Math.min(0, ...allVals);
  const yTicks = 4;
  const x = (i) => pad.l + (data.length === 1 ? innerW / 2 : (i / (data.length - 1)) * innerW);
  const y = (v) => pad.t + innerH - ((v - minV) / (maxV - minV || 1)) * innerH;

  const linePath = (key) => {
    const pts = data.map((d, i) => [x(i), y(d[key] ?? 0)]);
    if (pts.length < 2) return pts.map((p, i) => (i === 0 ? `M${p[0]} ${p[1]}` : `L${p[0]} ${p[1]}`)).join(" ");
    let dp = `M${pts[0][0]} ${pts[0][1]}`;
    for (let i = 0; i < pts.length - 1; i++) {
      const [x0, y0] = pts[i];
      const [x1, y1] = pts[i + 1];
      const cx = (x0 + x1) / 2;
      dp += ` C${cx} ${y0} ${cx} ${y1} ${x1} ${y1}`;
    }
    return dp;
  };
  const areaPath = (key) => `${linePath(key)} L${x(data.length - 1)} ${pad.t + innerH} L${x(0)} ${pad.t + innerH} Z`;

  const [hover, setHover] = useState(null);
  const onMove = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const px = e.clientX - rect.left;
    const idx = Math.round(((px - pad.l) / innerW) * (data.length - 1));
    if (idx >= 0 && idx < data.length) setHover(idx);
  };

  const id = "ar-" + Math.random().toString(36).slice(2, 8);
  return (
    <div ref={wrapRef} style={{ position: "relative", width: "100%" }}>
      <svg width={W} height={H} onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
        <defs>
          {series.map((s, i) => (
            <linearGradient key={s.key} id={id + "-" + i} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={s.color} stopOpacity="0.32" />
              <stop offset="100%" stopColor={s.color} stopOpacity="0" />
            </linearGradient>
          ))}
          <filter id={id + "-glow"} x="-10%" y="-10%" width="120%" height="120%">
            <feGaussianBlur stdDeviation="2" result="blur" />
            <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
          </filter>
        </defs>
        {/* gridlines + Y labels */}
        {Array.from({ length: yTicks + 1 }).map((_, i) => {
          const v = minV + ((maxV - minV) * i) / yTicks;
          const yy = y(v);
          return (
            <g key={i}>
              <line x1={pad.l} x2={W - pad.r} y1={yy} y2={yy} stroke="var(--c-grid)" strokeDasharray={i === 0 ? "0" : "0"} />
              <text x={pad.l - 10} y={yy + 4} textAnchor="end" fontSize="10.5" fill="var(--ink-3)">
                {valueFmt(Math.round(v))}
              </text>
            </g>
          );
        })}
        {data.map((d, i) => (
          <text key={i} x={x(i)} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--ink-3)">{d.label}</text>
        ))}
        {/* areas */}
        {series.map((s, i) => (
          <path key={"a" + s.key} d={areaPath(s.key)} fill={`url(#${id}-${i})`} />
        ))}
        {/* lines (with subtle glow) */}
        {series.map((s) => (
          <path key={"l" + s.key} d={linePath(s.key)} fill="none" stroke={s.color} strokeWidth="2" filter={`url(#${id}-glow)`} />
        ))}
        {/* hover indicator */}
        {hover != null && (
          <g>
            <line x1={x(hover)} x2={x(hover)} y1={pad.t} y2={pad.t + innerH} stroke="var(--ink-4)" strokeDasharray="3 3" />
            {series.map((s) => (
              <g key={"dot" + s.key}>
                <circle cx={x(hover)} cy={y(data[hover][s.key] ?? 0)} r="6" fill={s.color} opacity="0.18" />
                <circle cx={x(hover)} cy={y(data[hover][s.key] ?? 0)} r="4" fill="#fff" stroke={s.color} strokeWidth="2" />
              </g>
            ))}
          </g>
        )}
      </svg>
      {hover != null && (
        <ChartTooltip x={x(hover)} y={y(Math.max(...series.map((s) => data[hover][s.key] ?? 0)))}>
          <div style={{ fontWeight: 600, marginBottom: 4 }}>{data[hover].label}</div>
          {series.map((s) => (
            <div key={s.key} style={{ display: "flex", gap: 10, alignItems: "center", minWidth: 160 }}>
              <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }} />
              <span style={{ color: "var(--ink-3)" }}>{s.name}</span>
              <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)", fontWeight: 600 }}>{valueFmt(data[hover][s.key] ?? 0)}</span>
            </div>
          ))}
        </ChartTooltip>
      )}
    </div>
  );
}

// ============================================================
// Heatmap — modern cohort/activity grid
// rows: [{ label, cells: [v, v, v, ...] }]
// columns: ["Mon", "Tue", ...]
// ============================================================
function Heatmap({ rows, columns, color = "#635bff", valueFmt = (v) => v }) {
  const max = Math.max(1, ...rows.flatMap((r) => r.cells));
  const cellSize = 22;
  return (
    <div style={{ overflowX: "auto" }}>
      <table style={{ borderCollapse: "separate", borderSpacing: 2, fontSize: 11 }}>
        <thead>
          <tr>
            <th></th>
            {columns.map((c) => <th key={c} style={{ color: "var(--ink-3)", fontWeight: 500, padding: "0 4px", textAlign: "center" }}>{c}</th>)}
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => (
            <tr key={r.label}>
              <td style={{ color: "var(--ink-3)", paddingRight: 8, fontSize: 11, textAlign: "right", whiteSpace: "nowrap" }}>{r.label}</td>
              {r.cells.map((v, i) => {
                const op = v === 0 ? 0.08 : 0.18 + (v / max) * 0.82;
                return (
                  <td key={i} title={`${r.label} · ${columns[i]}: ${valueFmt(v)}`}
                      style={{
                        width: cellSize, height: cellSize, borderRadius: 4,
                        background: v === 0 ? "var(--bg-subtle)" : color,
                        opacity: op, cursor: "pointer",
                      }} />
                );
              })}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Object.assign(window, { LineChart, BarChart, Donut, DonutCard, Gauge, Sparkline, ProgressBar, CHART_PALETTE, AreaChart, Heatmap });
