/* Minimal scroll-driven walking-tour map.
   Fixed isometric tilt, static SVG landmarks standing up as billboards,
   a puck that slides along the route as the user scrolls. */

const { useEffect: mapUE, useRef: mapUR, useState: mapUS } = React;

/* ----------- Landmark line-art (each ~80×100 viewBox) ----------- */

function LM_Eiffel({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M8 134 C 22 110 32 90 38 78" />
      <path d="M92 134 C 78 110 68 90 62 78" />
      <path d="M34 134 L36 78 M66 134 L64 78" />
      <path d="M14 126 Q50 100 86 126" opacity=".5" />
      <path d="M22 78 L78 78" />
      <path d="M38 78 L46 38 M62 78 L54 38" />
      <path d="M44 38 L56 38" />
      <path d="M50 38 L50 6" />
      <path d="M50 6 L50 -2" opacity=".6" />
      <circle cx="50" cy="32" r="1.6" fill={color} stroke="none" />
    </svg>
  );
}
function LM_Arc({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <rect x="14" y="34" width="72" height="100" rx="2" />
      <path d="M34 134 V92 Q50 64 66 92 V134" />
      <path d="M14 56 H86" />
      <path d="M14 80 H86" opacity=".4" />
      <path d="M22 44 V134 M30 44 V134" opacity=".3" />
      <path d="M70 44 V134 M78 44 V134" opacity=".3" />
      <path d="M22 30 H78 V34 H22 Z" />
      <path d="M26 22 L74 22" opacity=".5" />
    </svg>
  );
}
function LM_Pyramid({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M50 24 L14 130 H86 Z" />
      <path d="M50 24 L50 130" opacity=".5" />
      <path d="M28 88 H72" opacity=".4" />
      <path d="M22 110 H78" opacity=".4" />
      <path d="M34 64 H66" opacity=".4" />
      <path d="M40 46 H60" opacity=".35" />
      <path d="M14 130 H86" />
      <path d="M50 24 L46 14 M50 24 L54 14" opacity=".5" />
    </svg>
  );
}
function LM_NotreDame({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M16 134 V52 H38 V134" />
      <path d="M62 134 V52 H84 V134" />
      <path d="M38 134 V64 H62 V134" />
      <path d="M16 52 L27 36 L38 52" />
      <path d="M62 52 L73 36 L84 52" />
      <path d="M38 64 Q50 44 62 64" />
      <circle cx="50" cy="86" r="6" opacity=".6" />
      <path d="M50 80 V92 M44 86 H56" opacity=".5" />
      <path d="M22 70 H32 M68 70 H78" opacity=".4" />
      <path d="M22 96 H32 M68 96 H78 M22 116 H32 M68 116 H78" opacity=".4" />
      <path d="M27 36 V28 M73 36 V28" />
    </svg>
  );
}
function LM_Pantheon({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M14 134 V90 H86 V134" />
      <path d="M14 90 Q50 64 86 90" />
      <path d="M28 90 Q50 76 72 90" />
      <path d="M50 76 V62" />
      <path d="M38 62 Q50 38 62 62 Z" />
      <path d="M50 38 V28" />
      <path d="M22 134 V100 M30 134 V100 M70 134 V100 M78 134 V100" opacity=".4" />
      <path d="M44 134 V104 M56 134 V104" opacity=".4" />
      <path d="M14 134 H86" />
    </svg>
  );
}
function LM_Sacre({ color = "#0E1235", sw = 2 }) {
  return (
    <svg viewBox="0 0 100 140" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
      <path d="M10 134 H90" />
      <path d="M14 134 V92 H86 V134" />
      <path d="M22 92 Q34 70 46 92 Z" />
      <path d="M54 92 Q66 70 78 92 Z" />
      <path d="M30 92 Q50 50 70 92 Z" />
      <path d="M50 50 V38" />
      <path d="M34 92 V72" opacity=".4" />
      <path d="M66 92 V72" opacity=".4" />
      <path d="M22 134 V104 H30 V134" opacity=".4" />
      <path d="M70 134 V104 H78 V134" opacity=".4" />
    </svg>
  );
}

const LANDMARKS = {
  eiffel: LM_Eiffel,
  arc: LM_Arc,
  pyramid: LM_Pyramid,
  cathedral: LM_NotreDame,
  pantheon: LM_Pantheon,
  sacre: LM_Sacre,
};

/* ---------- Tour data (positions in 0..1000 viewBox) ---------- */

const STOPS = [
  { id: 1, name: "Tour Eiffel",     kind: "eiffel",    frac: 0.00 },
  { id: 2, name: "Arc de Triomphe", kind: "arc",       frac: 0.20 },
  { id: 3, name: "Louvre",          kind: "pyramid",   frac: 0.40 },
  { id: 4, name: "Notre-Dame",      kind: "cathedral", frac: 0.62 },
  { id: 5, name: "Panthéon",        kind: "pantheon",  frac: 0.82 },
  { id: 6, name: "Sacré-Cœur",      kind: "sacre",     frac: 1.00 },
];

// SVG path that snakes through the city (we'll position landmarks on it)
const ROUTE_D =
  "M 140 800 " +
  "C 220 720, 240 620, 300 540 " +
  "S 460 460, 540 380 " +
  "S 700 320, 760 240 " +
  "S 700 120, 600 140 " +
  "S 460 220, 380 180 " +
  "S 240 80, 180 120";

/* ---------- Landmark billboard with rotating 3D monument ----------
   The WebGL renderer is throttled to 30fps and only the "next" landmark
   actually ticks its render loop (others freeze on their last frame).
   Together with DPR 1.0 this keeps 6 simultaneous canvases lightweight. */

function Landmark({ stop, x, y, isNext, isPast, planeTilt }) {
  const Icon = LANDMARKS[stop.kind];
  const canvasRef = mapUR(null);
  const ctrlRef = mapUR(null);
  mapUE(() => {
    if (canvasRef.current && window.mountMonument) {
      const ctrl = window.mountMonument(canvasRef.current, stop.kind);
      ctrlRef.current = ctrl;
      return () => {
        ctrlRef.current = null;
        ctrl && ctrl.dispose && ctrl.dispose();
      };
    }
  }, [stop.kind]);
  // Only the currently-active monument runs its render loop.
  mapUE(() => {
    if (ctrlRef.current && ctrlRef.current.setActive) {
      ctrlRef.current.setActive(!!isNext);
    }
  }, [isNext]);

  return (
    <div
      className={"landmark " + (isNext ? "next " : "") + (isPast ? "past" : "")}
      style={{ left: `${x / 10}%`, top: `${y / 10}%` }}
    >
      <div className="lm-base">
        <div className="lm-base-ring" />
        <div className="lm-base-dot">{stop.id}</div>
      </div>
      <div className="lm-stem" />
      <div
        className="lm-board"
        style={{ transform: `translate(-50%, -100%) rotateX(${-planeTilt}deg)` }}
      >
        <div className="lm-shadow" />
        <div className="lm-icon">
          {window.mountMonument
            ? <canvas ref={canvasRef} width="180" height="240" />
            : <Icon color="#0E1235" sw={2.2} />}
        </div>
        <div className="lm-label">
          <span className="lm-label-id">{stop.id}</span>
          <span>{stop.name}</span>
        </div>
      </div>
    </div>
  );
}

/* ---------- main scene ---------- */
/* Minimal scroll-driven map:
   - Route stroke + puck position update via direct DOM on every rAF frame.
   - React only re-renders when the discrete current-step index crosses a
     stop boundary (~5 times over the whole scroll). The HUD shows only that
     one piece of info; nothing on this page recomputes per-pixel of scroll. */

function MapScene() {
  const PLANE_TILT = 30;
  const stageRef = mapUR(null);
  const routeRef = mapUR(null);
  const routeBgRef = mapUR(null);
  const arrowRef = mapUR(null);
  const pathSamples = mapUR(null);
  const [stopIdx, setStopIdx] = mapUS(0);
  const [stopCoords, setStopCoords] = mapUS([]);

  // Measure path & cache samples ONCE — getPointAtLength is too expensive
  // to call per scroll frame.
  mapUE(() => {
    if (!routeRef.current) return;
    const path = routeRef.current;
    const L = path.getTotalLength();
    if (routeBgRef.current) routeBgRef.current.style.strokeDasharray = "none";
    path.style.strokeDasharray = L;
    path.style.strokeDashoffset = L;

    const N = 240;
    const samples = new Float32Array((N + 1) * 2);
    for (let i = 0; i <= N; i++) {
      const pt = path.getPointAtLength((L * i) / N);
      samples[i * 2] = pt.x;
      samples[i * 2 + 1] = pt.y;
    }
    pathSamples.current = samples;

    const coords = STOPS.map((s) => {
      const pt = path.getPointAtLength(L * s.frac);
      return { x: pt.x, y: pt.y };
    });
    setStopCoords(coords);
  }, []);

  // Single rAF-throttled scroll handler. All per-frame work is composite-only
  // (transform changes, no layout). React only re-renders when the discrete
  // step index crosses a boundary.
  mapUE(() => {
    let pending = false;
    let rafId = 0;
    let lastStopIdx = -1;
    let cachedArrowInner = null;
    let cachedLen = 0;
    // Cache layout-affecting reads on mount + resize so the per-frame path
    // never calls getBoundingClientRect (which forces layout).
    let sectionTop = 0;
    let sectionScrollRange = 1;
    let planeW = 0;
    let planeH = 0;
    function measure() {
      const el = stageRef.current;
      if (!el) return;
      // Use bounding rect + scrollY for the absolute document offset.
      // offsetTop returns position relative to the offset parent, which on
      // this layout is the <section>, not the document — wrong reference.
      const rect = el.getBoundingClientRect();
      sectionTop = rect.top + (window.scrollY || window.pageYOffset || 0);
      sectionScrollRange = Math.max(1, rect.height - window.innerHeight);
      // Plane pixel size for converting viewBox coords to actual pixels.
      const plane = el.querySelector(".map-plane");
      if (plane) {
        planeW = plane.clientWidth;
        planeH = plane.clientHeight;
      }
      if (routeRef.current) cachedLen = routeRef.current.getTotalLength();
    }
    measure();

    const compute = () => {
      pending = false;
      const scroll = window.scrollY || window.pageYOffset;
      const p = Math.min(1, Math.max(0, (scroll - sectionTop) / sectionScrollRange));

      const path = routeRef.current;
      const samples = pathSamples.current;
      if (path && samples && cachedLen) {
        // Route fill — strokeDashoffset is paint-only, no layout.
        path.style.strokeDashoffset = String(cachedLen * (1 - p));
        const N = (samples.length / 2) - 1;
        const idx = Math.min(N, Math.max(0, Math.round(p * N)));
        const nIdx = Math.min(N, idx + 2);
        const px = samples[idx * 2], py = samples[idx * 2 + 1];
        const nx = samples[nIdx * 2], ny = samples[nIdx * 2 + 1];
        const ang = Math.atan2(ny - py, nx - px) * 180 / Math.PI + 90;
        if (arrowRef.current && planeW) {
          // Pixel coords (no layout — pure transform, GPU-compositable).
          const tx = (px / 1000) * planeW;
          const ty = (py / 1000) * planeH;
          arrowRef.current.style.transform =
            `translate3d(${tx}px, ${ty}px, 4px) translate(-50%, -50%)`;
          if (!cachedArrowInner) cachedArrowInner = arrowRef.current.querySelector(".arrow-inner");
          if (cachedArrowInner) cachedArrowInner.style.transform = `rotate(${ang}deg)`;
        }
      }

      // Next-destination index: stops we've passed + 1. As soon as the puck
      // crosses a stop's frac, that stop turns green (validated) and the
      // following one becomes the active highlight.
      let nextIdx = 0;
      for (let i = 0; i < STOPS.length; i++) {
        if (p >= STOPS[i].frac + 0.005) nextIdx = i + 1;
      }
      if (nextIdx > STOPS.length - 1) nextIdx = STOPS.length - 1;
      if (nextIdx !== lastStopIdx) {
        lastStopIdx = nextIdx;
        setStopIdx(nextIdx);
      }
    };
    const onScroll = () => {
      if (pending) return;
      pending = true;
      rafId = requestAnimationFrame(compute);
    };
    const onResize = () => {
      measure();
      onScroll();
    };
    // Defer the first compute one frame so layout/measure is settled.
    requestAnimationFrame(() => { measure(); compute(); });
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onResize);
    return () => {
      cancelAnimationFrame(rafId);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onResize);
    };
  }, []);

  const currentStop = STOPS[stopIdx];

  return (
    <section className="map-section" id="tour">
      <div className="wrap">
        <div className="section-eyebrow reveal">Living map · powered by Mapbox</div>
        <h2 className="section-title display reveal">A 3D map that walks with you.</h2>
        <p className="section-sub reveal">
          Scroll down to follow a real Paris tour — the route paints itself ahead,
          and each stop lights up as you pass: Eiffel, Arc, Louvre, Notre-Dame,
          Panthéon, Sacré-Cœur.
        </p>
      </div>

      <div ref={stageRef} className="map-stage">
        <div className="map-sticky">
          {/* Top-left: discrete stop counter — updates 5 times max */}
          <div className="map-hud-tl">
            <div className="hud-stop">
              <span className="hud-stop-num">{stopIdx + 1}</span>
              <span className="hud-stop-sep">/</span>
              <span className="hud-stop-total">{STOPS.length}</span>
            </div>
            <div>
              <div className="hud-stop-label">Étape suivante</div>
              <div className="hud-stop-name">{currentStop.name}</div>
            </div>
          </div>

          {/* Top-right: language + audio (purely static) */}
          <div className="map-hud-tr">
            <div className="hud-pill"><span className="hud-flag">🇫🇷</span> Français</div>
            <div className="hud-pill hud-pill-audio">
              <span className="hud-rec" />
              Audio guide
            </div>
          </div>

          <div className="map-canvas">
            <div className="map-plane" style={{ transform: `rotateX(${PLANE_TILT}deg)` }}>
              <div className="map-base" />

              <div className="map-park" style={{ left: "30%", top: "20%", width: "32%", height: "22%" }} />
              <div className="map-park" style={{ left: "8%",  top: "70%", width: "26%", height: "22%", borderRadius: "55% 45% 60% 40% / 50%" }} />
              <div className="map-park" style={{ left: "60%", top: "62%", width: "28%", height: "24%", borderRadius: "60% 40% 55% 45% / 50%" }} />

              <svg className="map-svg" viewBox="0 0 1000 1000" preserveAspectRatio="none">
                <path d="M -20 720 C 200 660 400 720 600 660 S 900 600 1020 660" stroke="#BFE3F2" strokeWidth="34" fill="none" strokeLinecap="round" opacity=".9" />
                <path d="M -20 720 C 200 660 400 720 600 660 S 900 600 1020 660" stroke="#E0F2FA" strokeWidth="6" fill="none" strokeLinecap="round" opacity=".7" />
              </svg>

              <svg className="map-svg" viewBox="0 0 1000 1000" preserveAspectRatio="none">
                <g stroke="#F2F4F8" strokeWidth="14" fill="none" strokeLinecap="round">
                  <path d="M 0 220 L 1000 280" />
                  <path d="M 0 470 L 1000 440" />
                  <path d="M 0 900 L 1000 860" />
                  <path d="M 220 0 L 320 1000" />
                  <path d="M 540 0 L 600 1000" />
                  <path d="M 820 0 L 880 1000" />
                </g>
              </svg>

              <svg className="map-svg" viewBox="0 0 1000 1000" preserveAspectRatio="none">
                <path ref={routeBgRef} d={ROUTE_D} stroke="#EBEDF3" strokeWidth="44" fill="none" strokeLinecap="round" strokeLinejoin="round" />
                <path ref={routeRef} d={ROUTE_D} className="map-route" />
                <path d={ROUTE_D} className="map-route-inner" strokeDasharray="2 14" />
              </svg>

              {stopCoords.map((c, i) => (
                <Landmark
                  key={STOPS[i].id}
                  stop={STOPS[i]}
                  x={c.x}
                  y={c.y}
                  isNext={i === stopIdx}
                  isPast={i < stopIdx}
                  planeTilt={PLANE_TILT}
                />
              ))}

              <div ref={arrowRef} className="arrow-puck">
                <div className="arrow-pulse" />
                <div className="arrow-inner">
                  <svg viewBox="0 0 24 24" fill="white">
                    <path d="M12 2 L20 20 L12 16 L4 20 Z" />
                  </svg>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

window.MapScene = MapScene;
window.LANDMARKS = LANDMARKS;
