// Buildings — Starfox-style low-poly pixelated city backdrop. Plays off
// the "edifice" half of HTXEDIFICE. Canvas drawn at ~240px wide internally
// then CSS-scaled with image-rendering: pixelated for chunky retro 3D.
// Flat-shaded boxes, slow horizontal pan, seamlessly looped via x-tiling.
//
// Cheap to run: ~40 buildings × 3 faces × 4 verts per frame ≈ 500 ops.
function Buildings() {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const cvs = canvasRef.current;
    if (!cvs) return;
    const ctx = cvs.getContext('2d');
    if (!ctx) return;

    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    // Tiny internal pixel buffer — CSS scales it up.
    let W = 240, H = 135;
    function resize() {
      const rect = cvs.getBoundingClientRect();
      if (!rect.width || !rect.height) return;
      const aspect = rect.height / rect.width;
      W = 240;
      H = Math.max(80, Math.min(220, Math.round(W * aspect)));
      cvs.width = W;
      cvs.height = H;
    }
    resize();
    const ro = ('ResizeObserver' in window) ? new ResizeObserver(resize) : null;
    if (ro) ro.observe(cvs);
    else window.addEventListener('resize', resize);

    function mulberry32(a) {
      return function () {
        let t = (a = (a + 0x6d2b79f5) | 0);
        t = Math.imul(t ^ (t >>> 15), t | 1);
        t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
        return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
      };
    }

    // Build a tile of buildings; tile repeats every TILE_W along x so the
    // camera can pan forever without seams. Wide tile + many cols so the
    // skyline genuinely spans the hero edge-to-edge instead of clustering
    // in the canvas center.
    const TILE_W = 56;
    const buildings = [];
    {
      const rng = mulberry32(2026);
      const ROWS = 9;   // depth bands
      const COLS = 32;  // across the tile
      for (let r = 0; r < ROWS; r++) {
        for (let c = 0; c < COLS; c++) {
          if (rng() < 0.20) continue; // sparse gaps for skyline variety
          const x = (c - COLS / 2) * (TILE_W / COLS) + (rng() - 0.5) * 0.5;
          const z = 5 + r * 3.6 + (rng() - 0.5) * 1.6;
          const w = 1.0 + rng() * 1.0;
          const d = 1.0 + rng() * 1.0;
          // Skew toward taller buildings in back rows for skyline silhouette.
          const heightBoost = 0.5 + r * 0.35;
          const h = 1.6 + Math.pow(rng(), 0.5) * (3.5 + heightBoost);
          buildings.push({ x, z, w, d, h });
        }
      }
    }

    // Stars are picked once and stay put.
    const stars = [];
    {
      const rng = mulberry32(404);
      for (let i = 0; i < 36; i++) {
        stars.push({ x: rng(), y: rng() * 0.55, bright: rng() > 0.85 });
      }
    }

    let cameraX = 0;
    let last = performance.now();
    let raf = 0;

    function readVar(name, fallback) {
      const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
      return v || fallback;
    }

    // Project (x, y, z) world coord to (sx, sy) screen px.
    // Camera sits at (cameraX, 1.9, 0) looking down +z.
    function project(x, y, z) {
      const fov = W * 0.45;
      const cx = x - cameraX;
      const cy = y - 1.9;
      const sx = W * 0.5 + cx * fov / z;
      const sy = H * 0.6 - cy * fov / z;
      return [sx, sy];
    }

    function drawBuilding(b, palette) {
      const hw = b.w * 0.5, hd = b.d * 0.5;
      const v = [
        project(b.x - hw, 0,   b.z - hd),
        project(b.x + hw, 0,   b.z - hd),
        project(b.x + hw, 0,   b.z + hd),
        project(b.x - hw, 0,   b.z + hd),
        project(b.x - hw, b.h, b.z - hd),
        project(b.x + hw, b.h, b.z - hd),
        project(b.x + hw, b.h, b.z + hd),
        project(b.x - hw, b.h, b.z + hd),
      ];
      // Camera is always in front of building (z > 0) so front face always visible.
      // Pick side based on camera's x relative to building.
      const showRight = cameraX > b.x;
      const faces = [
        // Side first so the front overlaps the seam cleanly.
        { idx: showRight ? [1,2,6,5] : [3,0,4,7], fill: palette.side },
        { idx: [0,1,5,4], fill: palette.front },
        { idx: [4,5,6,7], fill: palette.top   },
      ];
      ctx.strokeStyle = palette.edge;
      ctx.lineWidth = 1;
      for (const f of faces) {
        ctx.fillStyle = f.fill;
        ctx.beginPath();
        const [x0, y0] = v[f.idx[0]];
        ctx.moveTo(x0, y0);
        for (let i = 1; i < f.idx.length; i++) {
          const [xi, yi] = v[f.idx[i]];
          ctx.lineTo(xi, yi);
        }
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
      }
    }

    function frame(now) {
      const dt = Math.min(64, now - last);
      last = now;
      if (!reduced) cameraX += dt * 0.00065;

      const accent = readVar('--accent', 'oklch(0.68 0.27 305)');
      const bg     = readVar('--ink-bg', '#07070a');
      const ink    = readVar('--ink',    '#f4f1ea');

      // Sky
      ctx.fillStyle = bg;
      ctx.fillRect(0, 0, W, H);

      const horizonY = Math.floor(H * 0.58);

      // Stars
      for (const s of stars) {
        const sx = Math.floor(s.x * W);
        const sy = Math.floor(s.y * horizonY);
        ctx.fillStyle = s.bright ? ink : `color-mix(in oklab, ${ink}, transparent 50%)`;
        ctx.fillRect(sx, sy, 1, 1);
      }

      // Horizon glow band
      const glowH = 14;
      const g = ctx.createLinearGradient(0, horizonY - glowH, 0, horizonY);
      g.addColorStop(0, 'rgba(0,0,0,0)');
      g.addColorStop(1, `color-mix(in oklab, ${accent}, transparent 35%)`);
      ctx.fillStyle = g;
      ctx.fillRect(0, horizonY - glowH, W, glowH);

      // Buildings — three tiles (-1, 0, +1) so panning is seamless.
      const palette = {
        top:   accent,
        front: `color-mix(in oklab, ${accent}, black 50%)`,
        side:  `color-mix(in oklab, ${accent}, black 72%)`,
        edge:  `rgba(0, 0, 0, 0.55)`,
      };
      const instances = [];
      for (const tile of [-1, 0, 1]) {
        for (const b of buildings) {
          instances.push({ x: b.x + tile * TILE_W, z: b.z, w: b.w, d: b.d, h: b.h });
        }
      }
      instances.sort((a, b) => b.z - a.z); // far → near (painter's)
      for (const inst of instances) {
        // Cull only the obviously-out-of-frame copies (we render 3 tiles so
        // there's already some redundancy).
        if (Math.abs(cameraX - inst.x) > TILE_W * 1.5) continue;
        drawBuilding(inst, palette);
      }

      // Subtle scanline tint to sell the retro CRT feel.
      ctx.globalAlpha = 0.18;
      ctx.fillStyle = '#000';
      for (let y = 0; y < H; y += 2) ctx.fillRect(0, y, W, 1);
      ctx.globalAlpha = 1;

      raf = requestAnimationFrame(frame);
    }

    raf = requestAnimationFrame(frame);

    function onVisibility() {
      if (document.hidden) {
        cancelAnimationFrame(raf);
        raf = 0;
      } else if (!raf) {
        last = performance.now();
        raf = requestAnimationFrame(frame);
      }
    }
    document.addEventListener('visibilitychange', onVisibility);

    return () => {
      cancelAnimationFrame(raf);
      document.removeEventListener('visibilitychange', onVisibility);
      if (ro) ro.disconnect();
      else window.removeEventListener('resize', resize);
    };
  }, []);

  return (
    <div className="buildings-wrap" aria-hidden="true">
      <canvas ref={canvasRef} className="buildings-canvas" />
    </div>
  );
}

window.Buildings = Buildings;
