/* Animated junction simulation for the hero.
   A 4-way intersection seen from above. Cars (rectangles) move along
   four lanes; signals cycle NS / EW; cars stop at stop-line on red. */

function JunctionSim({ density = 0.7, speedMul = 1.0 }) {
  const canvasRef = React.useRef(null);
  const stateRef = React.useRef({
    cars: [],
    phase: 0,        // 0 = NS green, 1 = NS amber, 2 = EW green, 3 = EW amber
    phaseT: 0,
    lastSpawn: 0,
    fps: 60,
    counted: 0,
    avgWait: 0.0,
    waiting: 0,
  });
  const [readout, setReadout] = React.useState({ flow: 0, queue: 0, phase: "NS-GO" });

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf = 0;
    let mounted = true;

    const resize = () => {
      const r = canvas.getBoundingClientRect();
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      canvas.width = r.width * dpr;
      canvas.height = r.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas);

    // ---- helpers
    const COLORS = {
      bg: "#0d1320",
      road: "#13192a",
      lane: "#2a3553",
      laneStripe: "#475377",
      stopLine: "#94a3c4",
      car: ["#e9bf4a", "#f1e8d6", "#5fb1d0", "#a8b3cc", "#f4d77a"],
      red: "#e8473a",
      green: "#3aa766",
      amber: "#e9bf4a",
      signalShell: "#1c2236",
    };

    const PHASE_DURATIONS = [4.5, 1.0, 4.5, 1.0]; // seconds
    const CAR_W = 9, CAR_L = 18;

    const spawnCar = () => {
      const r = canvas.getBoundingClientRect();
      // 4 directions: 0=N->S (top->bottom), 1=E->W, 2=S->N, 3=W->E
      const dir = Math.floor(Math.random() * 4);
      const cx = r.width / 2, cy = r.height / 2;
      const laneOff = 13;
      let x, y, vx = 0, vy = 0;
      if (dir === 0) { x = cx + laneOff; y = -CAR_L; vy = 1; }
      if (dir === 1) { x = r.width + CAR_L; y = cy + laneOff; vx = -1; }
      if (dir === 2) { x = cx - laneOff; y = r.height + CAR_L; vy = -1; }
      if (dir === 3) { x = -CAR_L; y = cy - laneOff; vx = 1; }
      stateRef.current.cars.push({
        x, y, vx, vy, dir,
        speed: 60 * speedMul,
        target: 60 * speedMul,
        color: COLORS.car[Math.floor(Math.random() * COLORS.car.length)],
        bornAt: performance.now(),
        waitStart: 0,
        totalWait: 0,
        passed: false,
      });
    };

    const drawSignal = (x, y, state) => {
      // shell
      ctx.fillStyle = COLORS.signalShell;
      ctx.fillRect(x - 5, y - 5, 10, 10);
      // light
      const fill = state === "green" ? COLORS.green
                 : state === "amber" ? COLORS.amber
                 : COLORS.red;
      ctx.fillStyle = fill;
      ctx.beginPath();
      ctx.arc(x, y, 2.6, 0, Math.PI * 2);
      ctx.fill();
      // glow
      ctx.fillStyle = fill + "55";
      ctx.beginPath();
      ctx.arc(x, y, 5, 0, Math.PI * 2);
      ctx.fill();
    };

    let lastT = performance.now();
    const tick = (t) => {
      if (!mounted) return;
      const dt = Math.min(0.05, (t - lastT) / 1000);
      lastT = t;
      const r = canvas.getBoundingClientRect();
      const W = r.width, H = r.height;
      const cx = W / 2, cy = H / 2;

      // Phase update
      const s = stateRef.current;
      s.phaseT += dt;
      if (s.phaseT > PHASE_DURATIONS[s.phase]) {
        s.phaseT = 0;
        s.phase = (s.phase + 1) % 4;
      }
      const nsGreen = s.phase === 0;
      const nsAmber = s.phase === 1;
      const ewGreen = s.phase === 2;
      const ewAmber = s.phase === 3;

      // Spawn
      s.lastSpawn += dt;
      const spawnInterval = 0.7 / density;
      if (s.lastSpawn > spawnInterval) {
        s.lastSpawn = 0;
        if (s.cars.length < 60) spawnCar();
      }

      // ---- draw bg
      ctx.fillStyle = COLORS.bg;
      ctx.fillRect(0, 0, W, H);

      // dim grid
      ctx.strokeStyle = "rgba(180,200,255,0.04)";
      ctx.lineWidth = 1;
      for (let i = 0; i < W; i += 40) {
        ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, H); ctx.stroke();
      }
      for (let j = 0; j < H; j += 40) {
        ctx.beginPath(); ctx.moveTo(0, j); ctx.lineTo(W, j); ctx.stroke();
      }

      // roads
      const roadWidth = 60;
      ctx.fillStyle = COLORS.road;
      ctx.fillRect(0, cy - roadWidth / 2, W, roadWidth); // horizontal
      ctx.fillRect(cx - roadWidth / 2, 0, roadWidth, H); // vertical

      // lane center stripes (dashed)
      ctx.strokeStyle = COLORS.laneStripe;
      ctx.lineWidth = 1;
      ctx.setLineDash([6, 8]);
      ctx.beginPath(); ctx.moveTo(0, cy); ctx.lineTo(cx - roadWidth / 2, cy); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(cx + roadWidth / 2, cy); ctx.lineTo(W, cy); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(cx, 0); ctx.lineTo(cx, cy - roadWidth / 2); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(cx, cy + roadWidth / 2); ctx.lineTo(cx, H); ctx.stroke();
      ctx.setLineDash([]);

      // stop lines
      ctx.fillStyle = COLORS.stopLine;
      ctx.fillRect(cx + 2, cy - roadWidth / 2 - 3, roadWidth / 2 - 2, 2);     // N approach (right of vertical, top of horizontal)
      ctx.fillRect(cx - roadWidth / 2, cy - roadWidth / 2 - 3, 0.0001, 0.0001); // noop
      ctx.fillRect(cx + roadWidth / 2 + 1, cy + 2, 2, roadWidth / 2 - 2);     // E approach
      ctx.fillRect(cx - roadWidth / 2 + 0, cy + roadWidth / 2 + 1, roadWidth / 2 - 2, 2); // S approach
      ctx.fillRect(cx - roadWidth / 2 - 3, cy - roadWidth / 2, 2, roadWidth / 2 - 2);     // W approach

      // ---- update cars
      const stopLineN = cy - roadWidth / 2 - 4;
      const stopLineS = cy + roadWidth / 2 + 4;
      const stopLineE = cx + roadWidth / 2 + 4;
      const stopLineW = cx - roadWidth / 2 - 4;

      let queueCt = 0;

      for (const c of s.cars) {
        // determine if this car must stop
        const nsCar = c.dir === 0 || c.dir === 2;
        const ewCar = c.dir === 1 || c.dir === 3;
        const greenForMe = (nsCar && nsGreen) || (ewCar && ewGreen);
        const amberForMe = (nsCar && nsAmber) || (ewCar && ewAmber);

        // forward distance ahead lookup (simple): find closest car ahead in same lane
        let aheadGap = 9999;
        for (const o of s.cars) {
          if (o === c || o.dir !== c.dir) continue;
          if (c.dir === 0 && o.y > c.y && Math.abs(o.x - c.x) < 8) aheadGap = Math.min(aheadGap, o.y - c.y);
          if (c.dir === 2 && o.y < c.y && Math.abs(o.x - c.x) < 8) aheadGap = Math.min(aheadGap, c.y - o.y);
          if (c.dir === 1 && o.x < c.x && Math.abs(o.y - c.y) < 8) aheadGap = Math.min(aheadGap, c.x - o.x);
          if (c.dir === 3 && o.x > c.x && Math.abs(o.y - c.y) < 8) aheadGap = Math.min(aheadGap, o.x - c.x);
        }

        // distance to stop line
        let toStop = 9999;
        if (c.dir === 0) toStop = stopLineN - c.y;
        if (c.dir === 2) toStop = c.y - stopLineS;
        if (c.dir === 1) toStop = c.x - stopLineE;
        if (c.dir === 3) toStop = stopLineW - c.x;

        let mustStopAtSignal = false;
        if (toStop > 0 && toStop < 50 && !greenForMe) mustStopAtSignal = true;
        // amber: only if not too close
        if (amberForMe && toStop > 30) mustStopAtSignal = true;

        let target = 60 * speedMul;
        if (aheadGap < 26) target = Math.max(0, (aheadGap - 18) * 6);
        if (mustStopAtSignal && toStop < 45) target = Math.min(target, Math.max(0, (toStop - 4) * 6));

        // smooth speed
        c.speed += (target - c.speed) * Math.min(1, dt * 4);
        if (c.speed < 1 && (mustStopAtSignal || aheadGap < 24)) {
          if (!c.waitStart) c.waitStart = t;
          queueCt++;
        } else if (c.waitStart) {
          c.totalWait += (t - c.waitStart) / 1000;
          c.waitStart = 0;
        }

        c.x += c.vx * c.speed * dt;
        c.y += c.vy * c.speed * dt;

        if (!c.passed) {
          if ((c.dir === 0 && c.y > cy) || (c.dir === 2 && c.y < cy) ||
              (c.dir === 1 && c.x < cx) || (c.dir === 3 && c.x > cx)) {
            c.passed = true;
            s.counted++;
            s.avgWait = s.avgWait * 0.9 + c.totalWait * 0.1;
          }
        }
      }
      // remove offscreen
      s.cars = s.cars.filter(c =>
        c.x > -40 && c.x < W + 40 && c.y > -40 && c.y < H + 40
      );

      // draw cars
      for (const c of s.cars) {
        ctx.save();
        ctx.translate(c.x, c.y);
        const horiz = c.dir === 1 || c.dir === 3;
        const w = horiz ? CAR_L : CAR_W;
        const h = horiz ? CAR_W : CAR_L;
        ctx.fillStyle = c.color;
        ctx.fillRect(-w / 2, -h / 2, w, h);
        // windshield
        ctx.fillStyle = "rgba(0,0,0,0.35)";
        if (c.dir === 0) ctx.fillRect(-w / 2 + 1, -h / 2 + 4, w - 2, 4);
        if (c.dir === 2) ctx.fillRect(-w / 2 + 1, h / 2 - 8, w - 2, 4);
        if (c.dir === 1) ctx.fillRect(-w / 2 + 4, -h / 2 + 1, 4, h - 2);
        if (c.dir === 3) ctx.fillRect(w / 2 - 8, -h / 2 + 1, 4, h - 2);
        ctx.restore();
      }

      // signals (4)
      const nsState = nsGreen ? "green" : nsAmber ? "amber" : "red";
      const ewState = ewGreen ? "green" : ewAmber ? "amber" : "red";
      drawSignal(cx + roadWidth / 2 + 10, cy - roadWidth / 2 - 10, ewState); // NE
      drawSignal(cx - roadWidth / 2 - 10, cy + roadWidth / 2 + 10, ewState); // SW
      drawSignal(cx + roadWidth / 2 + 10, cy + roadWidth / 2 + 10, nsState); // SE
      drawSignal(cx - roadWidth / 2 - 10, cy - roadWidth / 2 - 10, nsState); // NW

      // overlay HUD lines (data overlays — flow vectors)
      ctx.strokeStyle = "rgba(105,180,220,0.25)";
      ctx.lineWidth = 1;
      ctx.setLineDash([2, 4]);
      ctx.beginPath();
      ctx.arc(cx, cy, roadWidth / 2 + 4, 0, Math.PI * 2);
      ctx.stroke();
      ctx.setLineDash([]);

      // Update readout occasionally
      if (Math.floor(t / 500) !== Math.floor(lastT / 500 - 1)) {
        const flow = Math.round(s.counted / Math.max(1, (t / 1000)) * 60);
        setReadout({
          flow,
          queue: queueCt,
          phase: nsGreen ? "NS · GO" : nsAmber ? "NS · AMBER" : ewGreen ? "EW · GO" : "EW · AMBER",
        });
      }

      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    return () => {
      mounted = false;
      cancelAnimationFrame(raf);
      ro.disconnect();
    };
  }, [density, speedMul]);

  return (
    <div className="sim-wrap">
      <canvas ref={canvasRef}></canvas>
      <div className="sim-overlay">
        <div style={{ display: "flex", justifyContent: "space-between", gap: 8 }}>
          <div className="sim-tag"><span className="pulse"></span> LIVE · Junction A · 4-phase</div>
          <div className="sim-tag" style={{ background: "color-mix(in oklch, var(--ink) 70%, transparent)" }}>
            {readout.phase}
          </div>
        </div>
        <div className="sim-readout">
          <div className="cell"><div className="v">{readout.flow}</div><div className="l">veh / min</div></div>
          <div className="cell"><div className="v">{readout.queue}</div><div className="l">queued</div></div>
          <div className="cell"><div className="v">98<small style={{ fontSize: 12, color: "var(--signal)" }}>%</small></div><div className="l">throughput</div></div>
        </div>
      </div>
    </div>
  );
}

window.JunctionSim = JunctionSim;
