/* VCP bespoke visualizations: Waterfall, AreaLineChart, MultiLineChart, LegalGraph. */

/* ---------- Value-bridge waterfall ---------- */
function Waterfall({ steps, height = 260, unit = "EUR M" }) {
  const max = Math.max(...steps.map(s => Math.max(s.cumulative, s.kind === "down" ? s.cumulative - s.value : s.cumulative))) * 1.12;
  const h = height;
  const colFor = { start: "var(--accent)", end: "var(--ink)", up: "var(--positive)", down: "var(--danger)" };
  return <div>
    <div style={{ display: "flex", alignItems: "flex-end", gap: 0, height: h + 44, position: "relative", paddingTop: 24 }}>
      {steps.map((s, i) => {
        let barBottom, barH, isFull = s.kind === "start" || s.kind === "end";
        if (isFull) { barBottom = 0; barH = (s.cumulative / max) * h; }
        else if (s.kind === "up") { const prev = steps[i - 1].cumulative; barBottom = (prev / max) * h; barH = (s.value / max) * h; }
        else { const prev = steps[i - 1].cumulative; barBottom = (s.cumulative / max) * h; barH = ((prev - s.cumulative) / max) * h; }
        const topY = barBottom + barH;
        return <div key={i} style={{ flex: 1, position: "relative", height: h }}>
          {/* connector to next */}
          {i < steps.length - 1 && <div style={{ position: "absolute", left: "50%", right: "-50%", bottom: topY, height: 1, borderTop: "1px dashed var(--line)" }} />}
          {/* value label */}
          <div className="tabular" style={{ position: "absolute", bottom: topY + 5, left: 0, right: 0, textAlign: "center", fontSize: 12, fontWeight: 700, color: isFull ? "var(--ink)" : colFor[s.kind] }}>
            {s.kind === "up" ? "+" : s.kind === "down" ? "−" : ""}{Math.abs(isFull ? s.cumulative : s.value).toFixed(0)}
          </div>
          {/* bar */}
          <div style={{
            position: "absolute", bottom: barBottom, left: "18%", right: "18%", height: Math.max(barH, 2),
            background: colFor[s.kind], borderRadius: 3, opacity: isFull ? 1 : 0.92,
          }} />
        </div>;
      })}
      <div style={{ position: "absolute", left: 0, right: 0, bottom: 0, height: 1, background: "var(--line)" }} />
    </div>
    <div style={{ display: "flex", gap: 0, marginTop: 8 }}>
      {steps.map((s, i) => <div key={i} style={{ flex: 1, textAlign: "center", fontSize: 10.5, color: "var(--ink-soft)", lineHeight: 1.25, padding: "0 4px", fontWeight: s.kind === "end" || s.kind === "start" ? 700 : 500 }}>{s.name}</div>)}
    </div>
  </div>;
}

/* measure container width so charts render in true pixels (no distortion) */
function useMeasuredWidth(fallback = 600) {
  const ref = useRef(null);
  const [w, setW] = useState(fallback);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(es => { for (const e of es) if (e.contentRect.width) setW(e.contentRect.width); });
    ro.observe(ref.current);
    if (ref.current.clientWidth) setW(ref.current.clientWidth);
    return () => ro.disconnect();
  }, []);
  return [ref, w];
}

/* ---------- Single-series area+line chart with clickable points ---------- */
function AreaLineChart({ points, accessor, format, selected, onSelect, height = 180, color = "var(--accent)" }) {
  const [ref, W] = useMeasuredWidth(620);
  const H = height, padL = 16, padR = 24, padT = 30, padB = 26;
  const vals = points.map(accessor);
  const lo = Math.min(...vals), hi = Math.max(...vals);
  const range = hi - lo || 1;
  const yMin = lo - range * 0.22, yMax = hi + range * 0.3;
  const innerW = Math.max(W - padL - padR, 10);
  const x = i => padL + (i / (points.length - 1)) * innerW;
  const y = v => padT + (1 - (v - yMin) / (yMax - yMin)) * (H - padT - padB);
  const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${x(i).toFixed(1)},${y(accessor(p)).toFixed(1)}`).join(" ");
  const area = line + ` L${x(points.length - 1).toFixed(1)},${(H - padB).toFixed(1)} L${x(0).toFixed(1)},${(H - padB).toFixed(1)} Z`;
  const gid = "alc" + Math.round(yMin * 13 + yMax);
  const grid = [0.5, 0.75, 1.0];
  return <div ref={ref} style={{ width: "100%" }}>
    <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{ display: "block" }}>
      <defs><linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
        <stop offset="0%" stopColor={color} stopOpacity="0.15" /><stop offset="100%" stopColor={color} stopOpacity="0" />
      </linearGradient></defs>
      {grid.map((g, i) => { const yy = padT + g * (H - padT - padB); return <line key={i} x1={padL} x2={W - padR} y1={yy} y2={yy} stroke="var(--line-soft)" strokeWidth="1" />; })}
      <path d={area} fill={`url(#${gid})`} />
      <path d={line} fill="none" stroke={color} strokeWidth="2.25" strokeLinejoin="round" strokeLinecap="round" />
      {points.map((p, i) => {
        const on = selected === p.q;
        return <g key={i} style={{ cursor: onSelect ? "pointer" : "default" }} onClick={() => onSelect && onSelect(p.q)}>
          {on && <line x1={x(i)} x2={x(i)} y1={padT - 8} y2={H - padB} stroke="var(--accent)" strokeWidth="1" strokeDasharray="3 3" />}
          <rect x={x(i) - innerW / (points.length * 2)} y={0} width={innerW / points.length} height={H} fill="transparent" />
          <circle cx={x(i)} cy={y(accessor(p))} r={on ? 5.5 : 4} fill={on ? color : "var(--surface-card)"} stroke={color} strokeWidth="2" />
          <text x={x(i)} y={y(accessor(p)) - 12} textAnchor={i === 0 ? "start" : i === points.length - 1 ? "end" : "middle"} fontSize="11.5" fontWeight="700" fill="var(--ink)" fontFamily="var(--font-mono)" style={{ fontVariantNumeric: "tabular-nums" }}>{format(accessor(p))}</text>
          <text x={x(i)} y={H - 7} textAnchor={i === 0 ? "start" : i === points.length - 1 ? "end" : "middle"} fontSize="11" fill={on ? "var(--accent)" : "var(--ink-soft)"} fontWeight={on ? 700 : 500} fontFamily="var(--font-mono)">{p.q}</text>
        </g>;
      })}
    </svg>
  </div>;
}

/* ---------- Multi-series line chart (trend) ---------- */
function MultiLineChart({ series, labels, height = 240, yDomain = [1, 5] }) {
  const [ref, W] = useMeasuredWidth(720);
  const H = height, padL = 32, padR = 14, padT = 14, padB = 26;
  const x = i => padL + (i / (labels.length - 1)) * (W - padL - padR);
  const y = v => padT + (1 - (v - yDomain[0]) / (yDomain[1] - yDomain[0])) * (H - padT - padB);
  const ticks = [1, 2, 3, 4, 5];
  return <div ref={ref} style={{ width: "100%" }}>
    <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{ display: "block" }}>
      {ticks.map(t => <g key={t}>
        <line x1={padL} x2={W - padR} y1={y(t)} y2={y(t)} stroke="var(--line-soft)" strokeWidth="1" />
        <text x={padL - 7} y={y(t) + 3} textAnchor="end" fontSize="10" fill="var(--ink-faint)" fontFamily="var(--font-mono)">{t}.0</text>
      </g>)}
      {labels.map((l, i) => <text key={l} x={x(i)} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--ink-soft)" fontFamily="var(--font-mono)">{l}</text>)}
      {series.filter(s => s.visible !== false).map(s => {
        const d = s.values.map((v, i) => v == null ? null : `${i === 0 || s.values[i - 1] == null ? "M" : "L"}${x(i).toFixed(1)},${y(v).toFixed(1)}`).filter(Boolean).join(" ");
        return <g key={s.name}>
          <path d={d} fill="none" stroke={s.color} strokeWidth={s.bold ? 3 : 1.8} strokeLinejoin="round" strokeLinecap="round" opacity={s.bold ? 1 : 0.85} />
          {s.values.map((v, i) => v == null ? null : <circle key={i} cx={x(i)} cy={y(v)} r={s.bold ? 3.5 : 2.6} fill={s.color} />)}
        </g>;
      })}
    </svg>
  </div>;
}

/* ---------- Legal-structure ownership graph (true-pixel, no distortion) ---------- */
const LG_NODE_H = 60;
function truncate(s, n) { return s.length > n ? s.slice(0, n - 1) + "…" : s; }
function LegalGraph({ data, editMode, height = 600, onSelect, selectedId }) {
  const [ref, W] = useMeasuredWidth(760);
  const H = height;
  const [zoom, setZoom] = useState(1);
  const [pan, setPan] = useState(null); // null => auto-centered
  const [collapsed, setCollapsed] = useState({});
  const drag = useRef(null);
  const moved = useRef(false);

  const hidden = {};
  data.edges.forEach(e => { if (collapsed[e.from]) markDesc(e.to); });
  function markDesc(id) { hidden[id] = true; data.edges.filter(e => e.from === id).forEach(e => markDesc(e.to)); }
  const nodeById = id => data.nodes.find(n => n.id === id);
  const vis = data.nodes.filter(n => !hidden[n.id]);

  const minX = Math.min(...vis.map(n => n.x)), maxX = Math.max(...vis.map(n => n.x + n.w));
  const minY = Math.min(...vis.map(n => n.y)), maxY = Math.max(...vis.map(n => n.y + LG_NODE_H));
  const cw = (maxX - minX) || 1, ch = (maxY - minY) || 1;
  const pad = 44;
  const fit = Math.max(0.2, Math.min((W - pad * 2) / cw, (H - pad * 2) / ch, 1.25));
  const scale = fit * zoom;
  const cx = (W - cw * scale) / 2 - minX * scale;
  const cy = (H - ch * scale) / 2 - minY * scale;
  const tx = pan ? pan.x : cx, ty = pan ? pan.y : cy;

  const onDown = e => { drag.current = { x: e.clientX, y: e.clientY, px: tx, py: ty }; moved.current = false; };
  const onMove = e => { if (drag.current) { if (Math.abs(e.clientX - drag.current.x) + Math.abs(e.clientY - drag.current.y) > 3) moved.current = true; setPan({ x: drag.current.px + (e.clientX - drag.current.x), y: drag.current.py + (e.clientY - drag.current.y) }); } };
  const onUp = () => { drag.current = null; };

  return <div ref={ref} style={{ position: "relative", background: "var(--surface)", border: "1px solid var(--line)", borderRadius: "var(--radius)", overflow: "hidden", height }}>
    <div style={{ position: "absolute", top: 12, left: 12, zIndex: 3, display: "flex", gap: 6 }}>
      <Badge variant="neutral">{editMode ? "Edit mode" : "View mode"}</Badge>
    </div>
    <div style={{ position: "absolute", top: 12, right: 12, zIndex: 3, display: "flex", gap: 6, background: "var(--surface-card)", border: "1px solid var(--line)", borderRadius: 9, padding: 3 }}>
      <IconBtn name="minus" size={15} onClick={() => setZoom(z => Math.max(0.5, z - 0.15))} title="Zoom out" />
      <IconBtn name="plus" size={15} onClick={() => setZoom(z => Math.min(2.2, z + 0.15))} title="Zoom in" />
      <IconBtn name="refresh" size={15} onClick={() => { setZoom(1); setPan(null); }} title="Fit to view" />
    </div>
    <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{ display: "block", cursor: drag.current ? "grabbing" : "grab", userSelect: "none" }}
      onMouseDown={onDown} onMouseMove={onMove} onMouseUp={onUp} onMouseLeave={onUp}>
      <defs><marker id="lg-arrow" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="var(--ink-faint)" /></marker></defs>
      <g transform={`translate(${tx},${ty}) scale(${scale})`}>
        {data.edges.map((e, i) => {
          if (hidden[e.from] || hidden[e.to]) return null;
          const a = nodeById(e.from), b = nodeById(e.to);
          const ax = a.x + a.w / 2, ay = a.y + LG_NODE_H, bx = b.x + b.w / 2, by = b.y;
          const midY = (ay + by) / 2;
          const full = e.own === 100 && e.vote === 100;
          return <g key={i}>
            <path d={`M${ax},${ay} C${ax},${midY} ${bx},${midY} ${bx},${by}`} fill="none" stroke="var(--ink-faint)" strokeWidth="1.5" markerEnd="url(#lg-arrow)" />
            <g transform={`translate(${(ax + bx) / 2},${midY})`}>
              <rect x={full ? -20 : -36} y={-11} width={full ? 40 : 72} height={22} rx={6} fill="var(--surface-card)" stroke="var(--line)" />
              <text textAnchor="middle" y={full ? 4 : 0} fontSize="10" fontWeight="700" fill="var(--ink-muted)" fontFamily="var(--font-mono)">{e.own}%</text>
              {!full && <text textAnchor="middle" y={9} fontSize="8" fill="var(--ink-soft)" fontFamily="var(--font-mono)">vote {e.vote}%</text>}
            </g>
          </g>;
        })}
        {data.nodes.map(n => {
          if (hidden[n.id]) return null;
          const hasChildren = data.edges.some(e => e.from === n.id);
          const sel = selectedId === n.id;
          const stroke = sel ? "var(--accent)" : n.capTable ? "var(--accent)" : n.addOn ? "var(--positive)" : "var(--line)";
          return <g key={n.id} transform={`translate(${n.x},${n.y})`} style={{ cursor: "pointer" }}
            onClick={(ev) => { ev.stopPropagation(); if (!moved.current && onSelect) onSelect(n); }}>
            {sel && <rect x={-3} y={-3} width={n.w + 6} height={LG_NODE_H + 6} rx={11} fill="none" stroke="var(--accent)" strokeWidth="2" opacity="0.5" />}
            <rect width={n.w} height={LG_NODE_H} rx={9} fill="var(--surface-card)" stroke={stroke} strokeWidth={n.capTable || sel ? 1.6 : 1} strokeDasharray={n.addOn ? "4 3" : "0"} />
            {n.capTable && <rect width={n.w} height={LG_NODE_H} rx={9} fill="var(--accent-soft)" opacity="0.45" />}
            {n.addOn && <rect width={n.w} height={LG_NODE_H} rx={9} fill="var(--positive-soft)" opacity="0.4" />}
            <text x={12} y={22} fontSize="12.5" fontWeight="700" fill="var(--ink)">{truncate(n.name, 26)}</text>
            <text x={12} y={39} fontSize="10" fill="var(--ink-soft)">{truncate(n.role, 30)}</text>
            <g transform={`translate(${n.w - 36},10)`}>
              <rect width={28} height={15} rx={4} fill="var(--surface-deep)" />
              <text x={14} y={11} textAnchor="middle" fontSize="9" fontWeight="700" fill="var(--ink-muted)">{n.country}</text>
            </g>
            {n.reg && <text x={12} y={53} fontSize="8.5" fill="var(--ink-faint)" fontFamily="var(--font-mono)">{n.reg}</text>}
            {hasChildren && <g style={{ cursor: "pointer" }} onClick={(ev) => { ev.stopPropagation(); setCollapsed(c => ({ ...c, [n.id]: !c[n.id] })); }} transform={`translate(${n.w / 2 - 9},${LG_NODE_H - 8})`}>
              <rect width={18} height={16} rx={4} fill="var(--surface-card)" stroke="var(--line)" />
              <g transform="translate(9,8)" stroke="var(--ink-soft)" strokeWidth="1.4" strokeLinecap="round"><path d="M-3,0 L3,0" />{collapsed[n.id] && <path d="M0,-3 L0,3" />}</g>
            </g>}
          </g>;
        })}
      </g>
    </svg>
    <div style={{ position: "absolute", bottom: 12, left: 12, fontSize: 10.5, color: "var(--ink-faint)", display: "flex", gap: 14, flexWrap: "wrap" }}>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 5 }}><span style={{ width: 10, height: 10, borderRadius: 3, border: "1.6px solid var(--accent)", background: "var(--accent-soft)" }} /> Cap table entity</span>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 5 }}><span style={{ width: 10, height: 10, borderRadius: 3, border: "1.6px dashed var(--positive)", background: "var(--positive-soft)" }} /> Buy-and-build add-on</span>
      <span>Click an entity for detail · drag to pan</span>
    </div>
  </div>;
}

Object.assign(window, { Waterfall, AreaLineChart, MultiLineChart, LegalGraph });
