/* global React */
const { useState, useEffect, useMemo, useRef } = window.ReactHooks;

// Deterministic pseudo-random (so charts are stable across renders)
function seeded(seed) {
  let s = seed >>> 0;
  return () => {
    s = (s * 1664525 + 1013904223) >>> 0;
    return s / 0xffffffff;
  };
}

// --- Animated line+area chart (hero) ---
function LineChart({ width = 720, height = 380, seed = 17, live = true }) {
  const [t, setT] = useState(0);
  useEffect(() => {
    if (!live) return;
    let raf, start;
    const loop = (ts) => {
      if (!start) start = ts;
      setT((ts - start) / 1000);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [live]);

  const pad = { l: 44, r: 24, t: 16, b: 32 };
  const w = width, h = height;

  const series = useMemo(() => {
    const rnd = seeded(seed);
    const N = 48;
    const arr = [];
    let v = 120 + rnd() * 40;
    for (let i = 0; i < N; i++) {
      v += (rnd() - 0.4) * 14;
      arr.push(Math.max(40, v));
    }
    return arr;
  }, [seed]);

  const targets = useMemo(() => {
    return series.map((v, i) => v + 8 + Math.sin(i / 6) * 10);
  }, [series]);

  const maxV = Math.max(...series, ...targets) * 1.08;
  const minV = Math.min(...series, ...targets) * 0.9;

  // Moving reveal index
  const reveal = Math.min(series.length, Math.floor((t % 8) * 8));

  const x = (i) => pad.l + (i / (series.length - 1)) * (w - pad.l - pad.r);
  const y = (v) => pad.t + (1 - (v - minV) / (maxV - minV)) * (h - pad.t - pad.b);

  const pathFor = (data, upTo) => {
    let d = "";
    for (let i = 0; i <= upTo && i < data.length; i++) {
      d += (i === 0 ? "M" : "L") + x(i).toFixed(1) + " " + y(data[i]).toFixed(1) + " ";
    }
    return d;
  };

  const areaFor = (data, upTo) => {
    if (upTo < 1) return "";
    let d = "M" + x(0).toFixed(1) + " " + y(data[0]).toFixed(1) + " ";
    for (let i = 1; i <= upTo; i++) d += "L" + x(i).toFixed(1) + " " + y(data[i]).toFixed(1) + " ";
    d += "L" + x(upTo).toFixed(1) + " " + (h - pad.b) + " ";
    d += "L" + x(0).toFixed(1) + " " + (h - pad.b) + " Z";
    return d;
  };

  const gridY = 4;
  const xTicks = 6;

  return (
    <svg viewBox={`0 0 ${w} ${h}`} width="100%" height="100%" role="img" aria-label="Revenue vs target chart">
      <defs>
        <linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.22" />
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
        </linearGradient>
      </defs>

      {/* Gridlines */}
      {Array.from({ length: gridY + 1 }).map((_, i) => {
        const yy = pad.t + (i / gridY) * (h - pad.t - pad.b);
        return (
          <g key={"gy" + i}>
            <line x1={pad.l} x2={w - pad.r} y1={yy} y2={yy} stroke="var(--line-softer)" />
            <text x={pad.l - 8} y={yy + 3} textAnchor="end" fontSize="10" fontFamily="var(--f-mono)" fill="var(--muted)">
              {Math.round(maxV - (i / gridY) * (maxV - minV))}
            </text>
          </g>
        );
      })}

      {/* X ticks */}
      {Array.from({ length: xTicks }).map((_, i) => {
        const idx = Math.round((i / (xTicks - 1)) * (series.length - 1));
        const xx = x(idx);
        return (
          <text key={"xt" + i} x={xx} y={h - pad.b + 18} textAnchor="middle" fontSize="10" fontFamily="var(--f-mono)" fill="var(--muted)">
            {"W" + String(idx + 1).padStart(2, "0")}
          </text>
        );
      })}

      {/* Target (dashed) */}
      <path d={pathFor(targets, reveal)} fill="none" stroke="var(--fg)" strokeOpacity="0.45" strokeWidth="1" strokeDasharray="3 3" />

      {/* Actual area */}
      <path d={areaFor(series, reveal)} fill="url(#areaFill)" />
      <path d={pathFor(series, reveal)} fill="none" stroke="var(--accent)" strokeWidth="1.75" />

      {/* Last point marker */}
      {reveal > 0 && (
        <g>
          <circle cx={x(reveal)} cy={y(series[reveal])} r="3.5" fill="var(--accent)" />
          <circle cx={x(reveal)} cy={y(series[reveal])} r="3.5" fill="none" stroke="var(--bg)" strokeWidth="1.5" />
        </g>
      )}

      {/* Axis */}
      <line x1={pad.l} x2={w - pad.r} y1={h - pad.b} y2={h - pad.b} stroke="var(--line)" />
      <line x1={pad.l} x2={pad.l} y1={pad.t} y2={h - pad.b} stroke="var(--line-soft)" />
    </svg>
  );
}

// --- Bars chart (small) ---
function BarChart({ width = 400, height = 200, seed = 3, accent = false }) {
  const rnd = useMemo(() => seeded(seed), [seed]);
  const N = 14;
  const data = useMemo(() => Array.from({ length: N }, () => 20 + Math.floor(rnd() * 90)), []);
  const max = Math.max(...data);
  const pad = { l: 8, r: 8, t: 10, b: 16 };
  const bw = (width - pad.l - pad.r) / N - 3;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {data.map((v, i) => {
        const bh = ((v / max) * (height - pad.t - pad.b));
        const xx = pad.l + i * ((width - pad.l - pad.r) / N);
        return (
          <rect
            key={i}
            x={xx}
            y={height - pad.b - bh}
            width={bw}
            height={bh}
            fill={accent ? "var(--accent)" : "var(--fg)"}
            opacity={accent ? (0.4 + (i / N) * 0.6) : (0.2 + (i / N) * 0.7)}
          />
        );
      })}
      <line x1={pad.l} x2={width - pad.r} y1={height - pad.b} y2={height - pad.b} stroke="var(--line)" />
    </svg>
  );
}

// --- Sparkline ---
function Sparkline({ seed = 7, accent = true, width = 120, height = 36 }) {
  const rnd = useMemo(() => seeded(seed), [seed]);
  const N = 24;
  const d = useMemo(() => {
    const arr = [];
    let v = 50 + rnd() * 20;
    for (let i = 0; i < N; i++) {
      v += (rnd() - 0.5) * 16;
      arr.push(Math.max(10, Math.min(90, v)));
    }
    return arr;
  }, []);
  const max = Math.max(...d), min = Math.min(...d);
  const x = (i) => (i / (N - 1)) * (width - 2) + 1;
  const y = (v) => height - 2 - ((v - min) / (max - min + 0.001)) * (height - 4);
  let p = "";
  d.forEach((v, i) => (p += (i === 0 ? "M" : "L") + x(i) + " " + y(v) + " "));
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height={height}>
      <path d={p} fill="none" stroke={accent ? "var(--accent)" : "var(--fg)"} strokeWidth="1.25" />
    </svg>
  );
}

// --- Waterfall ---
function Waterfall({ width = 520, height = 260 }) {
  const data = [
    { l: "Q1 Rev", v: 100, type: "base" },
    { l: "+ New biz", v: 28, type: "inc" },
    { l: "+ Expansion", v: 14, type: "inc" },
    { l: "− Churn", v: -9, type: "dec" },
    { l: "− FX", v: -5, type: "dec" },
    { l: "Q2 Rev", v: 128, type: "base" }
  ];
  const pad = { l: 32, r: 16, t: 16, b: 40 };
  const max = 150;
  const bw = (width - pad.l - pad.r) / data.length - 10;
  let run = 0;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {data.map((d, i) => {
        const xx = pad.l + i * ((width - pad.l - pad.r) / data.length);
        let y1, y2, fill;
        if (d.type === "base") {
          y1 = pad.t + (1 - d.v / max) * (height - pad.t - pad.b);
          y2 = height - pad.b;
          fill = "var(--fg)";
          run = d.v;
        } else {
          const from = run;
          const to = run + d.v;
          const top = Math.max(from, to);
          const bot = Math.min(from, to);
          y1 = pad.t + (1 - top / max) * (height - pad.t - pad.b);
          y2 = pad.t + (1 - bot / max) * (height - pad.t - pad.b);
          fill = d.type === "inc" ? "var(--accent)" : "var(--fg)";
          run = to;
        }
        return (
          <g key={i}>
            <rect x={xx} y={y1} width={bw} height={y2 - y1} fill={fill} opacity={d.type === "dec" ? 0.35 : 1} />
            <text x={xx + bw / 2} y={height - pad.b + 16} textAnchor="middle" fontSize="10" fontFamily="var(--f-mono)" fill="var(--muted)">
              {d.l}
            </text>
            <text x={xx + bw / 2} y={y1 - 4} textAnchor="middle" fontSize="10" fontFamily="var(--f-mono)" fill="var(--fg)">
              {d.v > 0 && d.type !== "base" ? "+" : ""}{d.v}
            </text>
          </g>
        );
      })}
      <line x1={pad.l} x2={width - pad.r} y1={height - pad.b} y2={height - pad.b} stroke="var(--line)" />
    </svg>
  );
}

// --- Heatmap ---
function Heatmap({ cols = 20, rows = 7, width = 400, height = 180, seed = 11 }) {
  const rnd = useMemo(() => seeded(seed), [seed]);
  const grid = useMemo(() => {
    const g = [];
    for (let r = 0; r < rows; r++) {
      const row = [];
      for (let c = 0; c < cols; c++) {
        row.push(rnd());
      }
      g.push(row);
    }
    return g;
  }, [seed, cols, rows]);
  const cw = width / cols;
  const ch = height / rows;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {grid.map((row, r) =>
        row.map((v, c) => (
          <rect
            key={r + "-" + c}
            x={c * cw + 0.5}
            y={r * ch + 0.5}
            width={cw - 1}
            height={ch - 1}
            fill="var(--accent)"
            opacity={0.08 + v * 0.85}
          />
        ))
      )}
    </svg>
  );
}

// --- Small multiples ---
function SmallMultiples({ width = 520, height = 260 }) {
  const panels = 8;
  const cols = 4, rows = 2;
  const pw = width / cols, ph = height / rows;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {Array.from({ length: panels }).map((_, i) => {
        const r = Math.floor(i / cols), c = i % cols;
        const rnd = seeded(i * 7 + 3);
        const N = 14;
        const data = Array.from({ length: N }, () => 20 + rnd() * 80);
        const max = Math.max(...data), min = Math.min(...data);
        const x = (j) => c * pw + 6 + (j / (N - 1)) * (pw - 12);
        const y = (v) => r * ph + 12 + (1 - (v - min) / (max - min)) * (ph - 24);
        let p = "";
        data.forEach((v, j) => (p += (j === 0 ? "M" : "L") + x(j).toFixed(1) + " " + y(v).toFixed(1) + " "));
        return (
          <g key={i}>
            <rect x={c * pw} y={r * ph} width={pw} height={ph} fill="none" stroke="var(--line-softer)" />
            <text x={c * pw + 6} y={r * ph + 12} fontSize="9" fontFamily="var(--f-mono)" fill="var(--muted)">
              {"Seg " + String(i + 1).padStart(2, "0")}
            </text>
            <path d={p} fill="none" stroke="var(--accent)" strokeWidth="1.2" />
          </g>
        );
      })}
    </svg>
  );
}

// --- Funnel ---
function Funnel({ width = 400, height = 240 }) {
  const stages = [
    { l: "Visitors", v: 100 },
    { l: "Sign-ups", v: 62 },
    { l: "Activated", v: 41 },
    { l: "Paid", v: 17 }
  ];
  const pad = 16;
  const rowH = (height - pad * 2) / stages.length;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {stages.map((s, i) => {
        const w = (s.v / 100) * (width - pad * 2);
        const xx = (width - w) / 2;
        const yy = pad + i * rowH + 3;
        return (
          <g key={i}>
            <rect x={xx} y={yy} width={w} height={rowH - 6} fill="var(--fg)" opacity={1 - i * 0.18} />
            <text x={width / 2} y={yy + rowH / 2 + 2} textAnchor="middle" fontFamily="var(--f-mono)" fontSize="10" fill="var(--bg)">
              {s.l.toUpperCase() + "  " + s.v + "%"}
            </text>
          </g>
        );
      })}
    </svg>
  );
}

// --- Choropleth placeholder (stylised grid map) ---
function GridMap({ width = 520, height = 260, seed = 5 }) {
  const rnd = useMemo(() => seeded(seed), [seed]);
  const cols = 22, rows = 11;
  const cw = width / cols, ch = height / rows;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {Array.from({ length: rows * cols }).map((_, i) => {
        const r = Math.floor(i / cols), c = i % cols;
        const cx = c * cw + cw / 2, cy = r * ch + ch / 2;
        // rough continent-shaped mask
        const nx = (c - cols / 2) / (cols / 2);
        const ny = (r - rows / 2) / (rows / 2);
        const inside = (nx * nx * 0.7 + ny * ny * 1.4) < (0.55 + rnd() * 0.25);
        if (!inside) return null;
        const v = rnd();
        return (
          <rect
            key={i}
            x={c * cw + 1}
            y={r * ch + 1}
            width={cw - 2}
            height={ch - 2}
            fill="var(--accent)"
            opacity={0.12 + v * 0.8}
          />
        );
      })}
    </svg>
  );
}

// --- Decomposition tree ---
function DecompTree({ width = 520, height = 260 }) {
  const root = { l: "Revenue", v: 128, x: 40, y: 130 };
  const mids = [
    { l: "NA", v: 72, y: 70 },
    { l: "EU", v: 38, y: 130 },
    { l: "APAC", v: 18, y: 200 }
  ];
  const leafs = [
    ["Enterprise", 46, 40],
    ["Mid-market", 26, 90],
    ["Enterprise", 22, 140],
    ["SMB", 16, 180],
    ["APAC total", 18, 220]
  ];
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      <circle cx={root.x} cy={root.y} r="4" fill="var(--fg)" />
      <text x={root.x + 8} y={root.y - 8} fontFamily="var(--f-mono)" fontSize="10" fill="var(--muted)">ROOT</text>
      <text x={root.x + 8} y={root.y + 6} fontFamily="var(--f-display)" fontSize="14" fill="var(--fg)">Revenue · 128</text>
      {mids.map((m, i) => (
        <g key={i}>
          <path d={`M${root.x + 8},${root.y} C160,${root.y} 160,${m.y} 230,${m.y}`} fill="none" stroke="var(--line-soft)" />
          <circle cx="230" cy={m.y} r="3" fill="var(--accent)" />
          <text x="238" y={m.y + 4} fontFamily="var(--f-mono)" fontSize="10" fill="var(--fg)">{m.l}  {m.v}</text>
        </g>
      ))}
      {leafs.map((l, i) => (
        <g key={i}>
          <path d={`M245,${mids[Math.min(i, 2)].y} C340,${mids[Math.min(i, 2)].y} 340,${l[2]} 410,${l[2]}`} fill="none" stroke="var(--line-softer)" />
          <rect x="410" y={l[2] - 10} width="100" height="20" fill="var(--surface-2)" stroke="var(--line-softer)" />
          <text x="418" y={l[2] + 4} fontFamily="var(--f-mono)" fontSize="10" fill="var(--fg)">{l[0]} · {l[1]}</text>
        </g>
      ))}
    </svg>
  );
}

// --- Gauge ---
function Gauge({ width = 220, height = 160, value = 72 }) {
  const cx = width / 2, cy = height - 18, r = 80;
  const arc = (from, to) => {
    const a1 = Math.PI + from * Math.PI;
    const a2 = Math.PI + to * Math.PI;
    const x1 = cx + Math.cos(a1) * r, y1 = cy + Math.sin(a1) * r;
    const x2 = cx + Math.cos(a2) * r, y2 = cy + Math.sin(a2) * r;
    return `M${x1},${y1} A${r},${r} 0 0 1 ${x2},${y2}`;
  };
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      <path d={arc(0, 1)} fill="none" stroke="var(--line-softer)" strokeWidth="14" />
      <path d={arc(0, value / 100)} fill="none" stroke="var(--accent)" strokeWidth="14" />
      <text x={cx} y={cy - 16} textAnchor="middle" fontFamily="var(--f-display)" fontSize="30" fontWeight="500">{value}%</text>
      <text x={cx} y={cy + 4} textAnchor="middle" fontFamily="var(--f-mono)" fontSize="10" fill="var(--muted)">OF TARGET</text>
    </svg>
  );
}

// --- Network ---
function Network({ width = 520, height = 260, seed = 4 }) {
  const rnd = useMemo(() => seeded(seed), [seed]);
  const N = 28;
  const nodes = useMemo(() => {
    return Array.from({ length: N }, (_, i) => ({
      x: 40 + rnd() * (width - 80),
      y: 30 + rnd() * (height - 60),
      g: Math.floor(rnd() * 3),
      r: 3 + rnd() * 4
    }));
  }, [seed]);
  const edges = useMemo(() => {
    const e = [];
    for (let i = 0; i < N; i++) {
      for (let j = i + 1; j < N; j++) {
        const d = Math.hypot(nodes[i].x - nodes[j].x, nodes[i].y - nodes[j].y);
        if (d < 90 && rnd() < 0.6) e.push([i, j]);
      }
    }
    return e;
  }, [nodes]);
  const colors = ["var(--fg)", "var(--accent)", "var(--muted)"];
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      {edges.map(([a, b], i) => (
        <line key={i} x1={nodes[a].x} y1={nodes[a].y} x2={nodes[b].x} y2={nodes[b].y} stroke="var(--line-soft)" />
      ))}
      {nodes.map((n, i) => (
        <circle key={i} cx={n.x} cy={n.y} r={n.r} fill={colors[n.g]} opacity={0.85} />
      ))}
    </svg>
  );
}

// --- Table visual (ledger) ---
function Ledger() {
  const rows = [
    ["Americas", 46200, 0.12, 7],
    ["  United States", 38800, 0.14, 8],
    ["  Canada", 4900, 0.07, 4],
    ["  Latam", 2500, -0.03, 3],
    ["EMEA", 31400, 0.05, 5],
    ["  UK", 12100, 0.09, 6],
    ["  DACH", 10200, 0.04, 5],
    ["  Iberia", 4800, 0.18, 9],
    ["APAC", 16800, 0.22, 7]
  ];
  return (
    <table style={{ width: "100%", borderCollapse: "collapse", fontFamily: "var(--f-mono)", fontSize: 12 }}>
      <thead>
        <tr style={{ color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.08em", fontSize: 10 }}>
          <th style={{ textAlign: "left", padding: "6px 8px", borderBottom: "1px solid var(--line-soft)" }}>Segment</th>
          <th style={{ textAlign: "right", padding: "6px 8px", borderBottom: "1px solid var(--line-soft)" }}>Revenue</th>
          <th style={{ textAlign: "right", padding: "6px 8px", borderBottom: "1px solid var(--line-soft)" }}>YoY</th>
          <th style={{ textAlign: "left", padding: "6px 8px", borderBottom: "1px solid var(--line-soft)" }}>Trend</th>
        </tr>
      </thead>
      <tbody>
        {rows.map((r, i) => {
          const top = !r[0].startsWith(" ");
          return (
            <tr key={i}>
              <td style={{ padding: "8px 8px", borderBottom: "1px solid var(--line-softer)", whiteSpace: "pre", fontWeight: top ? 600 : 400, color: top ? "var(--fg)" : "var(--fg-2)" }}>{r[0]}</td>
              <td style={{ padding: "8px 8px", borderBottom: "1px solid var(--line-softer)", textAlign: "right" }}>{r[1].toLocaleString()}</td>
              <td style={{ padding: "8px 8px", borderBottom: "1px solid var(--line-softer)", textAlign: "right", color: r[2] < 0 ? "var(--danger)" : "var(--ok)" }}>
                {(r[2] * 100).toFixed(1)}%
              </td>
              <td style={{ padding: "8px 8px", borderBottom: "1px solid var(--line-softer)", width: 120 }}>
                <Sparkline seed={r[3]} />
              </td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

// Route product id to a representative chart
function ProductVisual({ id, big = false }) {
  const common = big ? { width: 720, height: 360 } : { width: 320, height: 140 };
  switch (id) {
    case "horizon-kpi":
      return <BarChart {...common} seed={9} accent />;
    case "strata-waterfall":
      return <Waterfall {...common} />;
    case "atlas-small-multiples":
      return <SmallMultiples {...common} />;
    case "meridian-map":
      return <GridMap {...common} />;
    case "cascade-funnel":
      return <Funnel {...common} />;
    case "lattice-heatmap":
      return <Heatmap {...common} />;
    case "beacon-gauge":
      return <Gauge width={big ? 300 : 220} height={big ? 200 : 140} />;
    case "prism-decomp":
      return <DecompTree {...common} />;
    case "orbit-network":
      return <Network {...common} />;
    case "ledger-table":
      return big ? <Ledger /> : <BarChart {...common} seed={22} />;
    default:
      return <LineChart {...common} live={false} />;
  }
}

Object.assign(window, {
  LineChart, BarChart, Sparkline, Waterfall, Heatmap, SmallMultiples, Funnel, GridMap, DecompTree, Gauge, Network, Ledger, ProductVisual
});
