// IsoStack — interactive exploded ISO with click-to-reveal speech bubbles
const ISO_MODULES = {
  control: {
    num:"00", name:"Control", role:"EMS · brain", short:"Edge AI",
    desc:"Edge-AI controller die alle modules op locatie aanstuurt. Voorspelt belasting, schakelt bronnen en logt elke kWh.",
    specs:[["Hardware","Edge-AI"],["Connect","4G/5G/LAN"],["Update","OTA"],["Logging","24/7"]],
    anchor:{x:320, y:54, side:"below"}
  },
  core: {
    num:"01", name:"Core", role:"Storage", short:"40–2.000 kWh",
    desc:"Het batterijhart van de stack. Buffert pieken, levert basislast en reduceert generator-uren met tot 70%.",
    specs:[["Capaciteit","40–2.000 kWh"],["Vermogen","tot 500 kVA"],["Behuizing","IP54"],["Cycli","6.000+"]],
    anchor:{x:320, y:290, side:"above"}
  },
  ray: {
    num:"02", name:"Ray", role:"Solar", short:"Tot 30 kWp",
    desc:"Vouwbare zonnepanelen die in minuten uitvouwen. Volledig draagbaar — geen vaste installatie nodig.",
    specs:[["Vermogen","tot 30 kWp"],["Vorm","Foldable"],["Opzet","< 10 min"],["Connector","MC4"]],
    anchor:{x:320, y:180, side:"above"}
  },
  fusion: {
    num:"03", name:"Fusion", role:"Hybrid backup", short:"60–200 kVA",
    desc:"Hybride generator die alleen bijspringt als Core en Ray het niet redden. HVO-ready, tot 92% minder CO₂.",
    specs:[["Vermogen","60–200 kVA"],["Brandstof","HVO/diesel"],["Emissie","Stage V"],["Modus","Smart standby"]],
    anchor:{x:175, y:425, side:"above"}
  },
  grid: {
    num:"04", name:"Grid", role:"EV charging", short:"Tot 360 kW",
    desc:"DC-snelladers gevoed vanuit Core. Laadt voertuigen op locaties zonder netaansluiting.",
    specs:[["Vermogen","tot 360 kW"],["Connectoren","CCS/CHAdeMO"],["Voeding","via Core"],["Net","niet vereist"]],
    anchor:{x:445, y:425, side:"above"}
  }
};
const ISO_ORDER = ["control","core","ray","fusion","grid"];

window.IsoStack = function IsoStack(){
  const [active, setActive] = React.useState(null);
  const stageRef = React.useRef(null);
  const svgRef = React.useRef(null);
  const bubbleRef = React.useRef(null);
  const [bubbleStyle, setBubbleStyle] = React.useState({left:0, top:0, side:"above", visible:false});

  const toggle = (k) => setActive(prev => prev === k ? null : k);

  const placeBubble = React.useCallback(() => {
    if (!active) return;
    const m = ISO_MODULES[active];
    const stage = stageRef.current;
    const svg = svgRef.current;
    const bubble = bubbleRef.current;
    if (!stage || !svg || !bubble) return;

    const stageRect = stage.getBoundingClientRect();
    const svgRect = svg.getBoundingClientRect();
    const scale = svgRect.width / 600;
    const ax = (svgRect.left - stageRect.left) + m.anchor.x * scale;
    const ay = (svgRect.top  - stageRect.top ) + m.anchor.y * scale;

    const bw = bubble.offsetWidth || 280;
    const bh = bubble.offsetHeight || 180;
    const tail = 14;

    let left = ax;
    const half = bw / 2;
    left = Math.max(half + 8, Math.min(stageRect.width - half - 8, left));

    const top = m.anchor.side === "below" ? ay + tail : ay - bh - tail;
    setBubbleStyle({ left, top, side: m.anchor.side, visible: true });
  }, [active]);

  React.useEffect(() => {
    if (!active){ setBubbleStyle(s => ({...s, visible:false})); return; }
    // measure on next frame so bubble has dimensions
    const r = requestAnimationFrame(placeBubble);
    return () => cancelAnimationFrame(r);
  }, [active, placeBubble]);

  React.useEffect(() => {
    const onResize = () => placeBubble();
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [placeBubble]);

  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key === "Escape" && active){ setActive(null); return; }
      if (!active) return;
      const idx = ISO_ORDER.indexOf(active);
      if (e.key === "ArrowRight" || e.key === "ArrowDown") setActive(ISO_ORDER[(idx+1)%ISO_ORDER.length]);
      else if (e.key === "ArrowLeft" || e.key === "ArrowUp") setActive(ISO_ORDER[(idx-1+ISO_ORDER.length)%ISO_ORDER.length]);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [active]);

  const isoClass = (k) => "iso-group" + (active && active !== k ? " dim" : "") + (active === k ? " active" : "");
  const m = active ? ISO_MODULES[active] : null;

  return (
    <div className="iso-bp">
      <div className="iso-bp-bar">
        <span>DOC.04 / SHEET 01 — VERT. EXPL.</span>
        <span className="c">EXPLODED ASSEMBLY · INTERACTIVE</span>
        <span className="r">REV 02 · 2026</span>
      </div>
      <div className="iso-bp-content">
        <div className="iso-grid">
          <div className="iso-stage" ref={stageRef}>
            <svg ref={svgRef} viewBox="0 0 600 540" preserveAspectRatio="xMidYMid meet">
              <defs>
                <linearGradient id="iso-og" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0" stopColor="#FF8A4D"/><stop offset="1" stopColor="#C4421A"/>
                </linearGradient>
              </defs>
              <g stroke="rgba(255,107,43,.18)" strokeDasharray="4 4" fill="none">
                <path d="M 100 470 L 380 530 L 540 470 L 260 410 Z"/>
              </g>
              <g stroke="rgba(255,107,43,.4)" strokeDasharray="2 4" fill="none">
                <line x1="320" y1="40" x2="320" y2="460"/>
              </g>

              {/* 04 GRID */}
              <g className={isoClass("grid")} transform="translate(380,420)" onClick={(e)=>{e.stopPropagation();toggle("grid")}}>
                <path d="M 0 30 L 70 55 L 70 95 L 0 70 Z" fill="#221208" stroke="rgba(244,239,226,.22)"/>
                <path d="M 70 55 L 130 30 L 130 70 L 70 95 Z" fill="#180D07" stroke="rgba(244,239,226,.22)"/>
                <path d="M 0 30 L 50 5 L 130 30 L 70 55 Z" fill="#2A1408" stroke="rgba(244,239,226,.22)"/>
                <circle cx="62" cy="60" r="5" fill="#FF6B2B" opacity=".8"/>
                <text className="iso-text" x="65" y="84" textAnchor="middle">04 · GRID</text>
                <path className="iso-hit" d="M 0 5 L 130 5 L 130 95 L 0 95 Z"/>
              </g>

              {/* 03 FUSION */}
              <g className={isoClass("fusion")} transform="translate(110,420)" onClick={(e)=>{e.stopPropagation();toggle("fusion")}}>
                <path d="M 0 30 L 70 55 L 70 95 L 0 70 Z" fill="#221208" stroke="rgba(244,239,226,.22)"/>
                <path d="M 70 55 L 130 30 L 130 70 L 70 95 Z" fill="#180D07" stroke="rgba(244,239,226,.22)"/>
                <path d="M 0 30 L 50 5 L 130 30 L 70 55 Z" fill="#2A1408" stroke="rgba(244,239,226,.22)"/>
                <text className="iso-text" x="65" y="84" textAnchor="middle">03 · FUSION</text>
                <path className="iso-hit" d="M 0 5 L 130 5 L 130 95 L 0 95 Z"/>
              </g>

              {/* 01 CORE */}
              <g className={isoClass("core")} transform="translate(220,300)" onClick={(e)=>{e.stopPropagation();toggle("core")}}>
                <path d="M 0 30 L 100 70 L 100 130 L 0 90 Z" fill="url(#iso-og)" stroke="#FF6B2B"/>
                <path d="M 100 70 L 200 30 L 200 90 L 100 130 Z" fill="#C4421A" stroke="#FF6B2B"/>
                <path d="M 0 30 L 100 -10 L 200 30 L 100 70 Z" fill="#FF8A4D" stroke="#FF6B2B"/>
                <text className="iso-text dark" x="100" y="74" textAnchor="middle" fontFamily="Archivo,sans-serif" fontWeight="800" fontSize="14">CORE</text>
                <text className="iso-text dark" x="100" y="92" textAnchor="middle" opacity=".7">01 · STORAGE</text>
                <path className="iso-hit" d="M 0 -10 L 200 -10 L 200 130 L 0 130 Z"/>
              </g>

              {/* 02 RAY */}
              <g className={isoClass("ray")} transform="translate(230,180)" onClick={(e)=>{e.stopPropagation();toggle("ray")}}>
                <path d="M 0 30 L 90 60 L 90 75 L 0 45 Z" fill="#221208" stroke="rgba(244,239,226,.4)"/>
                <path d="M 90 60 L 180 30 L 180 45 L 90 75 Z" fill="#180D07" stroke="rgba(244,239,226,.4)"/>
                <path d="M 0 30 L 90 0 L 180 30 L 90 60 Z" fill="#2A1408" stroke="rgba(244,239,226,.4)"/>
                <g stroke="#FF6B2B" strokeWidth=".7" opacity=".7">
                  <line x1="20" y1="22" x2="80" y2="42"/><line x1="40" y1="14" x2="100" y2="34"/>
                  <line x1="60" y1="6" x2="120" y2="26"/><line x1="80" y1="-2" x2="140" y2="18"/>
                </g>
                <text className="iso-text ink" x="90" y="38" textAnchor="middle">02 · RAY</text>
                <path className="iso-hit" d="M 0 0 L 180 0 L 180 75 L 0 75 Z"/>
              </g>

              {/* 00 CONTROL */}
              <g className={isoClass("control")} transform="translate(320,80)" onClick={(e)=>{e.stopPropagation();toggle("control")}}>
                <polygon points="0,-26 22,-13 22,13 0,26 -22,13 -22,-13" fill="rgba(255,107,43,.15)" stroke="#FF6B2B" strokeWidth="1.2"/>
                <text className="iso-text hl" x="0" y="2" textAnchor="middle">CONTROL</text>
                <text className="iso-text" x="0" y="14" textAnchor="middle" fontSize="8">EMS · L02</text>
                <polygon className="iso-hit" points="0,-30 26,-15 26,15 0,30 -26,15 -26,-15"/>
              </g>

              {/* callouts */}
              <g className="iso-text hl" fontSize="11">
                <text x="500" y="220">[02]</text>
                <text x="500" y="370">[01]</text>
                <text x="60" y="450">[03]</text>
                <text x="500" y="450">[04]</text>
                <text x="370" y="80">[00]</text>
              </g>
              <g stroke="#FF6B2B" strokeWidth=".8" strokeDasharray="3 3" fill="none" opacity=".6">
                <line x1="420" y1="220" x2="495" y2="217"/>
                <line x1="420" y1="370" x2="495" y2="367"/>
                <line x1="180" y1="450" x2="55" y2="447"/>
                <line x1="500" y1="450" x2="495" y2="447"/>
                <line x1="345" y1="80" x2="365" y2="78"/>
              </g>
            </svg>

            <div
              ref={bubbleRef}
              className={"iso-bubble" + (bubbleStyle.side === "below" ? " below" : "") + (bubbleStyle.visible ? " show" : "")}
              style={{left: bubbleStyle.left + "px", top: bubbleStyle.top + "px", visibility: bubbleStyle.visible ? "visible" : "hidden"}}
            >
              <button className="iso-bb-close" aria-label="Sluiten" onClick={()=>setActive(null)}>×</button>
              {m && (<>
                <div className="iso-bb-head">
                  <span className="iso-bb-num">{m.num}</span>
                  <span className="iso-bb-name">{m.name}</span>
                </div>
                <div className="iso-bb-role">{m.role}</div>
                <div className="iso-bb-desc">{m.desc}</div>
                <div className="iso-bb-specs">
                  {m.specs.map(([k,v])=>(<div className="iso-bb-spec" key={k}><b>{k}</b>{v}</div>))}
                </div>
              </>)}
            </div>
          </div>

          <div className="iso-legend">
            {ISO_ORDER.map(k => {
              const mm = ISO_MODULES[k];
              const cls = "iso-li" + (active === k ? " active" : "");
              return (
                <button key={k} className={cls} type="button" onClick={(e)=>{e.stopPropagation();toggle(k)}}>
                  <div className="num">{mm.num}</div>
                  <div className="iso-li-row"><div className="nm">{mm.name}</div><div className="role">{mm.role}</div></div>
                  <div className="sp">{mm.short}</div>
                </button>
              );
            })}
          </div>
        </div>
      </div>
      <div className="iso-bp-foot">
        <span>SCALE 1:NTS</span>
        <span className="c">VERTICAL EXPLODED · 5 MODULES</span>
        <span className="r">DRWN. WATTIQ ENG.</span>
      </div>
    </div>
  );
};
