/* global React */
/* ============================================================
   HubScrollAnimation — replaces the abstract orchard-cube
   centrepiece. A 3-stage scroll-scrubbed micro-explainer:
     Stage 1  Detection  — RevScout S identifies fruit
     Stage 2  Map        — fruits become per-tree size dots on a block
     Stage 3  Histogram  — dots collapse into a size distribution
   18 dots / fruits are conserved across all stages so the morph
   reads coherently. Pure SVG + CSS, GPU-friendly.
   ============================================================ */

(function () {
  const { useRef, useMemo } = React;

  // ---- CONFIG -----------------------------------------------------
  // 18 fruits, with size in mm and their (x,y) in stage-1 space (0..100, 0..100)
  // and target tree-cell index for stage 2.
  const N = 18;

  // Detection-stage normalised positions (matches the detection screenshot vibe).
  const FRUITS = [
    { x: 22, y: 18, size: 70 },
    { x: 70, y: 14, size: 70 },
    { x: 35, y: 24, size: 65 },
    { x: 80, y: 28, size: 56 },
    { x: 18, y: 32, size: 61 },
    { x: 52, y: 30, size: 70 },
    { x: 64, y: 33, size: 66 },
    { x: 26, y: 44, size: 52 },
    { x: 50, y: 46, size: 50 },
    { x: 60, y: 50, size: 58 },
    { x: 36, y: 54, size: 55 },
    { x: 76, y: 50, size: 60 },
    { x: 14, y: 60, size: 49 },
    { x: 44, y: 62, size: 53 },
    { x: 70, y: 64, size: 57 },
    { x: 28, y: 74, size: 47 },
    { x: 56, y: 76, size: 51 },
    { x: 82, y: 70, size: 63 },
  ];

  // Stage-2 grid: 6 cols × 3 rows = 18 trees. Each fruit lands at one tree.
  // Order is shuffled so it doesn't read as a 1:1 column transfer.
  const TREE_ORDER = [3, 11, 6, 14, 0, 9, 17, 2, 8, 5, 13, 16, 1, 10, 7, 4, 15, 12];

  // Stage-3 histogram bins (mm). Each fruit contributes one tally to a bin.
  const BINS = [
    { lo: 40, hi: 45 },
    { lo: 45, hi: 50 },
    { lo: 50, hi: 55 },
    { lo: 55, hi: 60 },
    { lo: 60, hi: 65 },
    { lo: 65, hi: 70 },
    { lo: 70, hi: 75 },
  ];

  function binIndex(size) {
    for (let i = 0; i < BINS.length; i++) if (size >= BINS[i].lo && size < BINS[i].hi) return i;
    return BINS.length - 1;
  }

  // Map a 0..1 size value (40..75 mm) to the brand size palette.
  // Matches the real Survey-Manager "Avg Size (mm)" legend: purple → blue → green → yellow.
  function sizeColor(mm) {
    const t = Math.max(0, Math.min(1, (mm - 40) / 35));
    // multi-stop gradient
    const stops = [
      [0.00, [86, 35, 132]],   // purple
      [0.35, [55, 95, 180]],   // blue
      [0.60, [80, 160, 90]],   // green
      [0.80, [180, 200, 60]],  // lime
      [1.00, [245, 200, 50]],  // yellow
    ];
    let a = stops[0], b = stops[stops.length - 1];
    for (let i = 0; i < stops.length - 1; i++) {
      if (t >= stops[i][0] && t <= stops[i + 1][0]) { a = stops[i]; b = stops[i + 1]; break; }
    }
    const local = (t - a[0]) / Math.max(1e-6, (b[0] - a[0]));
    const r = Math.round(a[1][0] + (b[1][0] - a[1][0]) * local);
    const g = Math.round(a[1][1] + (b[1][1] - a[1][1]) * local);
    const bl = Math.round(a[1][2] + (b[1][2] - a[1][2]) * local);
    return `rgb(${r},${g},${bl})`;
  }

  // ---- COMPONENT --------------------------------------------------
  window.HubScrollAnimation = function HubScrollAnimation() {
    const wrapRef = useRef(null);
    // Use the section-level scroll progress; stages are split inside.
    const progress = window.useScrollProgress(wrapRef, { start: 0.0, end: 0.95 });

    // Stage progress: 0..1 for each of the three stages.
    // Crossfades happen 0.30..0.40 and 0.65..0.75 of the way through.
    const p = progress;
    // Stage envelopes (alpha):
    const a1 = clamp(invlerp(0.40, 0.30, p));        // 1 → 0 between 0.30 and 0.40
    const a2 = clamp(invlerp(0.30, 0.40, p)) * clamp(invlerp(0.75, 0.65, p));
    const a3 = clamp(invlerp(0.65, 0.75, p));
    // Per-stage internal progress (0..1 within stage):
    const s1 = clamp(invlerp(0.00, 0.30, p));        // detection reveal
    const s2 = clamp(invlerp(0.40, 0.65, p));        // morph to map
    const s3 = clamp(invlerp(0.75, 1.00, p));        // morph to histogram

    // Memoise per-fruit derived geometry.
    const fruits = useMemo(() => FRUITS.map((f, i) => {
      // stage-2 grid coords (6x3)
      const cell = TREE_ORDER[i];
      const col = cell % 6;
      const row = Math.floor(cell / 6);
      // Map padding so dots sit on a friendly inner area
      const mapX = 14 + col * (72 / 5);   // 6 cols across 14..86  → step 14.4
      const mapY = 22 + row * (54 / 2);   // 3 rows across 22..76  → step 27
      // stage-3 histogram coords
      const bi = binIndex(f.size);
      const tally = FRUITS.slice(0, i + 1).filter(o => binIndex(o.size) === bi)
        .filter(o => binIndex(o.size) === bi).length;
      const totalInBin = FRUITS.filter(o => binIndex(o.size) === bi).length;
      const indexInBin = FRUITS.slice(0, i).filter(o => binIndex(o.size) === bi).length;
      // Bar geometry: 7 bins across viewBox x 12..88, stacked from bottom.
      const barX = 12 + bi * ((88 - 12) / BINS.length) + ((88 - 12) / BINS.length) * 0.5;
      // Each tally is a 4-unit-tall dot, stacked from baseline 80.
      const barY = 80 - 4 - indexInBin * 4.5;
      return { ...f, idx: i, mapX, mapY, barX, barY, bi, indexInBin, totalInBin };
    }), []);

    return (
      <div ref={wrapRef} className="hub-stage" aria-label="RevScout S workflow: detection, map, distribution">
        {/* ---------------- BACKGROUND PHOTO (Stage 1) ---------------- */}
        <div
          className="hub-stage-bg"
          style={{
            backgroundImage: `url('assets/revscout-detection-real.png')`,
            opacity: a1 * 0.55,
            transform: `scale(${1 + (1 - a1) * 0.08})`,
          }}
        />
        {/* dark wash baseline so labels stay legible across stages */}
        <div className="hub-stage-wash" />

        {/* ---------------- SVG (all 3 stages) ---------------- */}
        <svg
          viewBox="0 0 100 100"
          preserveAspectRatio="none"
          className="hub-stage-svg"
          aria-hidden="true"
        >
          <defs>
            {/* lime corner-bracket marker for stage 1 */}
            <symbol id="hub-bracket" viewBox="0 0 10 10">
              <path d="M0,3 V0 H3 M7,0 H10 V3 M10,7 V10 H7 M3,10 H0 V7"
                fill="none" stroke="#B8DC73" strokeWidth="1" strokeLinecap="square" />
            </symbol>
            {/* faint orchard-row pattern for stage 2 backdrop */}
            <pattern id="hub-rows" width="4" height="2" patternUnits="userSpaceOnUse">
              <rect width="4" height="2" fill="#0E2E10" />
              <rect width="4" height="0.4" y="1.6" fill="#13401a" />
            </pattern>
          </defs>

          {/* ------ STAGE 2: orchard block backdrop ------ */}
          <g style={{ opacity: a2 + a3 * 0.4 }}>
            {/* outer block frame */}
            <rect x="6" y="14" width="88" height="72" rx="1.2"
              fill="url(#hub-rows)" stroke="rgba(184,220,115,0.35)" strokeWidth="0.3" />
            {/* row separators implied by pattern; add edge labels */}
            <text x="8" y="12" fontSize="3" fontFamily="'JetBrains Mono', monospace"
              fill="#B8DC73" letterSpacing="0.4" opacity="0.85">BLOCK 1A</text>
            <text x="92" y="12" fontSize="3" fontFamily="'JetBrains Mono', monospace"
              fill="rgba(255,255,255,0.55)" letterSpacing="0.4" textAnchor="end">1,824 TREES</text>
            <text x="8" y="92" fontSize="2.6" fontFamily="'JetBrains Mono', monospace"
              fill="rgba(255,255,255,0.45)" letterSpacing="0.3">N ↑   ROW SPACING 4.5 m</text>
          </g>

          {/* ------ STAGE 3: histogram backdrop ------ */}
          <g style={{ opacity: a3 }}>
            {/* baseline */}
            <line x1="6" y1="80" x2="94" y2="80"
              stroke="rgba(255,255,255,0.35)" strokeWidth="0.3" />
            {/* x-axis ticks + labels */}
            {BINS.map((b, i) => {
              const cx = 12 + i * ((88 - 12) / BINS.length) + ((88 - 12) / BINS.length) * 0.5;
              return (
                <g key={i}>
                  <line x1={cx} y1="80" x2={cx} y2="81.4"
                    stroke="rgba(255,255,255,0.4)" strokeWidth="0.25" />
                  <text x={cx} y="86" fontSize="2.6" fontFamily="'JetBrains Mono', monospace"
                    fill="rgba(255,255,255,0.6)" textAnchor="middle">{b.lo}</text>
                </g>
              );
            })}
            <text x="94" y="86" fontSize="2.6" fontFamily="'JetBrains Mono', monospace"
              fill="rgba(255,255,255,0.6)" textAnchor="end">75 mm</text>

            {/* market-bin window (Class 1 · 60–65 mm) */}
            {(() => {
              const idx = 4; // bin lo=60
              const stepW = (88 - 12) / BINS.length;
              const x0 = 12 + idx * stepW;
              const w = stepW;
              return (
                <g style={{ opacity: a3 }}>
                  <rect x={x0} y="20" width={w} height="60" fill="#B8DC73" opacity="0.10" />
                  <rect x={x0} y="20" width={w} height="60" fill="none"
                    stroke="#B8DC73" strokeDasharray="0.8 0.6" strokeWidth="0.25" />
                  <text x={x0 + w / 2} y="18" fontSize="2.4"
                    fontFamily="'JetBrains Mono', monospace" textAnchor="middle"
                    fill="#B8DC73" letterSpacing="0.4">CLASS 1 · 60–65 mm</text>
                </g>
              );
            })()}
            {/* mean line */}
            {(() => {
              const mean = FRUITS.reduce((s, f) => s + f.size, 0) / FRUITS.length;
              const cx = 12 + ((mean - 40) / 35) * (88 - 12);
              return (
                <g>
                  <line x1={cx} y1="22" x2={cx} y2="80"
                    stroke="rgba(255,255,255,0.45)" strokeWidth="0.2" strokeDasharray="0.5 0.5" />
                  <text x={cx + 1} y="24" fontSize="2.2"
                    fontFamily="'JetBrains Mono', monospace"
                    fill="rgba(255,255,255,0.65)">MEAN {mean.toFixed(1)} mm</text>
                </g>
              );
            })()}
          </g>

          {/* ------ THE 18 FRUITS (morph through 3 positions) ------ */}
          {fruits.map((f, i) => {
            // current position: detection → map → histogram, with crossfade.
            // Use stage-anchored interpolations.
            // pos1: detection coords (with slight stagger entry)
            const enter = clamp(invlerp(i / N * 0.6, i / N * 0.6 + 0.4, s1));
            const x1 = f.x, y1 = f.y;
            // pos2: map coords
            const morph2 = ease(s2);
            const x2 = lerp(x1, f.mapX, morph2);
            const y2 = lerp(y1, f.mapY, morph2);
            // pos3: histogram coords
            const morph3 = ease(s3);
            const x3 = lerp(x2, f.barX, morph3);
            const y3 = lerp(y2, f.barY, morph3);

            // Decide which pair to read from based on which stage we're in.
            const cx = (a3 > 0.01) ? x3 : (a2 > 0.01 ? x2 : x1);
            const cy = (a3 > 0.01) ? y3 : (a2 > 0.01 ? y2 : y1);

            const rDetect = 1.6 * enter;
            const rMap = 1.4;
            const rBar = 1.6;
            const r = (a3 > 0.5) ? rBar : (a2 > 0.5 ? rMap : rDetect);

            const colour = sizeColor(f.size);

            return (
              <g key={i}>
                {/* Stage-1 bracket bracket overlays (only visible in stage 1) */}
                {a1 > 0.02 && (
                  <g style={{ opacity: a1 * enter }}>
                    <use href="#hub-bracket"
                      x={x1 - 2.2} y={y1 - 2.2} width="4.4" height="4.4" />
                    {/* Size: NN×NN mm tag */}
                    <text x={x1 + 2.6} y={y1 - 1.8}
                      fontSize="2.2" fontFamily="'JetBrains Mono', monospace"
                      fill="#B8DC73" letterSpacing="0.2">
                      Size: {f.size}×{Math.max(40, f.size - (i % 4) - 2)}mm
                    </text>
                    <text x={x1 + 2.6} y={y1 + 0.6}
                      fontSize="1.9" fontFamily="'JetBrains Mono', monospace"
                      fill="rgba(255,255,255,0.7)" letterSpacing="0.2">
                      Dist: {(1.4 + ((i * 0.27) % 1.6)).toFixed(1)}m
                    </text>
                  </g>
                )}
                {/* The dot itself — visible across stages 2 and 3, fades in late stage 1 */}
                <circle cx={cx} cy={cy} r={r}
                  fill={colour}
                  fillOpacity={Math.min(1, a2 * 1.4 + a3)}
                  stroke="rgba(255,255,255,0.85)"
                  strokeWidth="0.18"
                />
              </g>
            );
          })}
        </svg>

        {/* ---------------- HUD overlays ---------------- */}
        <div className="hub-stage-hud">
          {/* Stage tag (top-left) */}
          <div className="hub-tag">
            {a3 > 0.5 ? '03 · DISTRIBUTION'
              : a2 > 0.5 ? '02 · MAP'
              : '01 · DETECT'}
          </div>

          {/* Stage label (bottom) */}
          <div className="hub-cap">
            {a3 > 0.5 ? (
              <>HARVEST DISTRIBUTION · WEEK 28</>
            ) : a2 > 0.5 ? (
              <>BLOCK 1A · {N} TREES · AVG SIZE BY TREE</>
            ) : (
              <>REV-SIZER ⇄ CAMERA · CALIBRATED</>
            )}
          </div>

          {/* Stage progress dots */}
          <div className="hub-stepper">
            <span className={p < 0.33 ? 'on' : ''}></span>
            <span className={p >= 0.33 && p < 0.66 ? 'on' : ''}></span>
            <span className={p >= 0.66 ? 'on' : ''}></span>
          </div>

          {/* Calibration tick (only in stage 1) */}
          {a1 > 0.4 && (
            <div className="hub-tick" style={{ opacity: a1 }}>
              <span className="hub-tick-dot"></span>
              CAL · 2.4m REF · ±0.5mm
            </div>
          )}
        </div>
      </div>
    );
  };

  // ---- math helpers -----------------------------------------------
  function clamp(v) { return Math.max(0, Math.min(1, v)); }
  function lerp(a, b, t) { return a + (b - a) * t; }
  function invlerp(a, b, v) { return (v - a) / (b - a); }
  function ease(t) { return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; }

  // backwards-compat: HubGlobe3D / OrchardCube3D now return this scroll animation
  // (we override only on the home page, not globally — see pages-home.jsx)
})();
