// ============================================================
// screens/LiveMatch.jsx — Real-time playable match
// ============================================================
const { useState: useState_L, useEffect: useEffect_L, useRef: useRef_L, useMemo: useMemo_L } = React;

// Pitch is 100 x 140 virtual units (same as replay pitch). Top = attacking direction for opponent, bottom = player.
// Player team attacks UP (toward y=0). Opponent attacks DOWN (toward y=140).

function LiveMatchScreen({ playerSquad, opponent, onFinish }) {
  const [controlledIdx, setControlledIdx] = useState_L(-1); // -1 = AI-only
  const [score, setScore] = useState_L({ a: 0, b: 0 });
  const [clock, setClock] = useState_L(0); // 0..90
  const [finished, setFinished] = useState_L(false);
  const [events, setEvents] = useState_L([{ kind: 'kickoff', t: 0, text: `Kickoff — ${opponent.name}.` }]);
  const [paused, setPaused] = useState_L(false);
  const [lastAction, setLastAction] = useState_L(null);

  // Build entities once
  const stateRef = useRef_L(null);
  const keysRef = useRef_L({});
  const animRef = useRef_L(null);
  const lastTickRef = useRef_L(performance.now());
  const rerenderRef = useRef_L(0);
  const [, forceRender] = useState_L(0);

  if (!stateRef.current) {
    stateRef.current = initWorld(playerSquad, opponent);
  }
  const world = stateRef.current;

  // Keyboard
  useEffect_L(() => {
    const down = (e) => {
      keysRef.current[e.key.toLowerCase()] = true;
      // control switching
      if (e.key >= '1' && e.key <= '9') {
        const n = parseInt(e.key) - 1;
        if (n < world.teamA.length) setControlledIdx(n);
        e.preventDefault();
      }
      if (e.key === '0' || e.key === 'Escape') {
        setControlledIdx(-1);
        e.preventDefault();
      }
      if (e.key === ' ') {
        if (controlledIdx >= 0) doPass(world, controlledIdx, logEvent);
        e.preventDefault();
      }
      if (e.key.toLowerCase() === 'x') {
        if (controlledIdx >= 0) doShoot(world, controlledIdx, logEvent);
        e.preventDefault();
      }
      if (e.key.toLowerCase() === 'p') setPaused(p => !p);
    };
    const up = (e) => { keysRef.current[e.key.toLowerCase()] = false; };
    window.addEventListener('keydown', down);
    window.addEventListener('keyup', up);
    return () => { window.removeEventListener('keydown', down); window.removeEventListener('keyup', up); };
  }, [controlledIdx]);

  const logEvent = (ev) => {
    // Pass events fire many times per second — keep them out of React state,
    // they flood the render loop and make the match hitch over time.
    if (ev.kind !== 'pass') {
      setEvents(prev => {
        const next = [...prev, { ...ev, t: world.clock }];
        return next.length > 200 ? next.slice(-200) : next;
      });
    }
    if (ev.kind === 'goal_a' || ev.kind === 'goal_b') {
      setLastAction({ text: ev.text, kind: 'goal', at: performance.now() });
    } else if (ev.flash) {
      setLastAction({ text: ev.text, kind: ev.kind, at: performance.now() });
    }
  };

  // Tick loop
  useEffect_L(() => {
    if (finished || paused) return;
    const tick = (now) => {
      const dt = Math.min(0.05, (now - lastTickRef.current) / 1000);
      lastTickRef.current = now;

      // advance world by dt seconds (sim clock: 1 real second = 1 match-minute to keep short)
      stepWorld(world, dt, keysRef.current, controlledIdx, logEvent);
      world.clock += dt * 1.0; // 1 real sec = 1 match min

      if (world.clock >= 90) {
        setFinished(true);
        setEvents(prev => [...prev, { kind: 'fulltime', t: 90, text: `Full time. ${world.scoreA}–${world.scoreB}.` }]);
        return;
      }

      // react render at ~30fps
      rerenderRef.current++;
      if (rerenderRef.current % 2 === 0) {
        setClock(world.clock);
        setScore({ a: world.scoreA, b: world.scoreB });
        forceRender(x => x + 1);
      }

      animRef.current = requestAnimationFrame(tick);
    };
    lastTickRef.current = performance.now();
    animRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(animRef.current);
  }, [finished, paused, controlledIdx]);

  // Fade last action
  useEffect_L(() => {
    if (!lastAction) return;
    const t = setTimeout(() => setLastAction(null), 1600);
    return () => clearTimeout(t);
  }, [lastAction]);

  if (finished) {
    // Build a fake match object compatible with PostMatch
    const fakeMatch = {
      scoreA: world.scoreA, scoreB: world.scoreB,
      seed: world.seed,
      teamA: { possession: world.possA, name: 'Your XI' },
      teamB: { possession: world.possB, name: opponent.name },
      events: events.map((e, i) => ({ ...e, card: e.scorerId, scoreA: e.scoreA ?? 0, scoreB: e.scoreB ?? 0, team: e.team, t: i })),
    };
    return (
      <div style={{ padding: '20px 32px', maxWidth: 1400, margin: '0 auto' }}>
        <window.PostMatchLive match={fakeMatch} opponent={opponent} onFinish={onFinish} />
      </div>
    );
  }

  const controlled = controlledIdx >= 0 ? world.teamA[controlledIdx] : null;

  return (
    <div style={{ padding: '20px 32px 40px', maxWidth: 1400, margin: '0 auto' }}>
      <LiveScoreboard score={score} clock={clock} opponent={opponent} paused={paused} />

      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 20, marginTop: 16 }}>
        <div>
          <LivePitch world={world} controlled={controlled} lastAction={lastAction} />
          <ControlPanel
            world={world}
            controlledIdx={controlledIdx}
            setControlledIdx={setControlledIdx}
            paused={paused} setPaused={setPaused}
          />
        </div>
        <LiveSidebar events={events} world={world} opponent={opponent} />
      </div>
    </div>
  );
}

// ============================================================
// World init
// ============================================================
function initWorld(playerSquadIds, opponent) {
  const sizeA = playerSquadIds.length;
  const sizeB = opponent.cards.length;
  const formA = window.FORMATIONS[sizeA] || window.FORMATIONS[5];
  const formB = window.FORMATIONS[sizeB] || window.FORMATIONS[5];

  const mkPlayer = (id, slot, team) => {
    const card = window.CARDS.find(c => c.id === id);
    if (!card) return null;
    // team A (player) at bottom, team B at top. formA.slots have y=90 near goal.
    // For team A: home positions are mirrored (bottom half)
    const home = team === 'A'
      ? { x: slot.x, y: 50 + (slot.y/100)*50 }   // bottom half
      : { x: 100 - slot.x, y: 50 - (slot.y/100)*50 }; // top half
    return {
      id: card.id, card, team, pos: card.pos,
      x: home.x, y: home.y,
      vx: 0, vy: 0,
      home,
      cooldown: 0,
    };
  };

  const teamA = formA.slots.map((s, i) => mkPlayer(playerSquadIds[i], s, 'A')).filter(Boolean);
  const teamB = formB.slots.map((s, i) => mkPlayer(opponent.cards[i], s, 'B')).filter(Boolean);

  return {
    teamA, teamB,
    ball: { x: 50, y: 70, vx: 0, vy: 0, owner: null, ownerTeam: null },
    scoreA: 0, scoreB: 0,
    clock: 0,
    possA: 0, possB: 0,
    seed: Math.floor(Math.random()*1e9),
    sincePossCheck: 0,
    teamAName: 'Your XI',
    teamBName: opponent.name,
    opponentTeamKey: opponent.teamKey,
  };
}

// ============================================================
// Step: advance world by dt seconds
// ============================================================
function stepWorld(w, dt, keys, controlledIdx, log) {
  // Input for controlled player
  let input = { mx: 0, my: 0 };
  if (controlledIdx >= 0 && w.teamA[controlledIdx]) {
    if (keys['arrowleft'] || keys['a']) input.mx -= 1;
    if (keys['arrowright'] || keys['d']) input.mx += 1;
    if (keys['arrowup'] || keys['w']) input.my -= 1;
    if (keys['arrowdown'] || keys['s']) input.my += 1;
    const mag = Math.hypot(input.mx, input.my);
    if (mag > 0) { input.mx /= mag; input.my /= mag; }
  }

  // Cooldowns + reception grace (a player that just picked up the ball has a brief tackle immunity)
  [...w.teamA, ...w.teamB].forEach(p => {
    if (p.cooldown > 0) p.cooldown -= dt;
    if (p.holdGrace > 0) p.holdGrace -= dt;
  });

  // AI for all players
  w.teamA.forEach((p, i) => {
    if (i === controlledIdx) {
      // human-controlled
      const speed = 18 + (p.card.pac - 60) * 0.15;
      p.vx = input.mx * speed;
      p.vy = input.my * speed;
    } else {
      aiMove(p, w, 'A', dt);
    }
  });
  w.teamB.forEach(p => aiMove(p, w, 'B', dt));

  // Integrate positions
  [...w.teamA, ...w.teamB].forEach(p => {
    p.x += p.vx * dt;
    p.y += p.vy * dt;
    p.x = clamp(p.x, 2, 98);
    p.y = clamp(p.y, 2, 138);
  });

  // Ball physics
  if (w.ball.owner) {
    // glued to owner — lerp toward the carry position instead of hard-snapping,
    // otherwise pickups, stops, and turns all teleport the ball by several units.
    const p = getPlayer(w, w.ball.owner);
    if (p) {
      const dir = heading(p);
      const targetX = p.x + dir.x * 2;
      const targetY = p.y + dir.y * 2;
      const k = Math.min(1, dt * 25);
      w.ball.x += (targetX - w.ball.x) * k;
      w.ball.y += (targetY - w.ball.y) * k;
      w.ball.vx = p.vx;
      w.ball.vy = p.vy;
    }
  } else {
    // free ball — friction
    w.ball.x += w.ball.vx * dt;
    w.ball.y += w.ball.vy * dt;
    const fric = 0.96;
    w.ball.vx *= Math.pow(fric, dt * 60);
    w.ball.vy *= Math.pow(fric, dt * 60);
    // clamp vertical to pitch (bounce off sides of pitch - walls)
    if (w.ball.x < 2) { w.ball.x = 2; w.ball.vx *= -0.4; }
    if (w.ball.x > 98) { w.ball.x = 98; w.ball.vx *= -0.4; }

    // Check goals — goal mouth is x∈[38,62], y<2 (team A attacks up, scores at top) or y>138 (team B scores at bottom)
    if (w.ball.y < 2 && w.ball.x > 38 && w.ball.x < 62) {
      // Team A scores
      w.scoreA++;
      const scorer = findLastTouch(w, 'A') || w.teamA.find(p=>p.pos==='FWD');
      log({ kind: 'goal_a', text: `GOAL! ${scorer?.card.name || 'Your XI'} scores!`, scorerId: scorer?.id, team: 'A', scoreA: w.scoreA, scoreB: w.scoreB, flash: true });
      resetKickoff(w, 'B');
      return;
    }
    if (w.ball.y > 138 && w.ball.x > 38 && w.ball.x < 62) {
      w.scoreB++;
      const scorer = findLastTouch(w, 'B') || w.teamB.find(p=>p.pos==='FWD');
      log({ kind: 'goal_b', text: `GOAL! ${scorer?.card.name || opponent?.name || 'Opponent'} scores.`, scorerId: scorer?.id, team: 'B', scoreA: w.scoreA, scoreB: w.scoreB, flash: true });
      resetKickoff(w, 'A');
      return;
    }
    // bounce off top/bottom (not goal)
    if (w.ball.y < 2) { w.ball.y = 2; w.ball.vy *= -0.4; }
    if (w.ball.y > 138) { w.ball.y = 138; w.ball.vy *= -0.4; }

    // Pickup: closest player within 3 units grabs it
    let closest = null; let cd = Infinity;
    [...w.teamA, ...w.teamB].forEach(p => {
      if (p.cooldown > 0) return;
      const d = dist(p, w.ball);
      if (d < 3.2 && d < cd) { closest = p; cd = d; }
    });
    if (closest) {
      w.ball.owner = closest.id;
      w.ball.ownerTeam = closest.team;
      w.ball.lastTouchTeam = closest.team;
      w.ball.lastTouchId = closest.id;
      // Brief tackle immunity so the receiver isn't mobbed on the same frame they collect.
      // Without this, the passer can instantly tackle back once their cooldown expires and
      // the ball appears to "glitch" back to them.
      closest.holdGrace = closest.pos === 'GK' ? 0.6 : 0.35;
      if (closest.pos === 'GK' && (closest.y < 15 || closest.y > 125)) {
        log({ kind: 'save', text: `${closest.card.name} gathers it.`, team: closest.team, flash: true });
      }
    }
  }

  // Tackles: opponent within 2.3 of owner might steal
  if (w.ball.owner) {
    const owner = getPlayer(w, w.ball.owner);
    // Skip while the owner is still in their reception grace period — avoids
    // flip-flop possession where a fresh interception is immediately tackled back.
    if (owner && !(owner.holdGrace > 0)) {
      const enemies = owner.team === 'A' ? w.teamB : w.teamA;
      // Stop at the first successful tackle so ownership can't swap multiple times per frame.
      enemies.some(e => {
        if (e.cooldown > 0) return false;
        if (dist(e, owner) >= 2.3) return false;
        const roll = Math.random() * (e.card.def + 20) - Math.random() * (owner.card.pac + 20);
        if (roll <= 0) return false;
        w.ball.owner = e.id;
        w.ball.ownerTeam = e.team;
        w.ball.lastTouchId = e.id;
        w.ball.lastTouchTeam = e.team;
        e.cooldown = 0.4;
        e.holdGrace = 0.25;
        owner.cooldown = 0.6;
        return true;
      });
    }
  }

  // Possession tracking
  w.sincePossCheck += dt;
  if (w.sincePossCheck > 0.5) {
    w.sincePossCheck = 0;
    if (w.ball.ownerTeam === 'A') w.possA++;
    else if (w.ball.ownerTeam === 'B') w.possB++;
  }

  // AI pass/shoot decisions
  if (w.ball.owner && w.ball.ownerTeam) {
    const owner = getPlayer(w, w.ball.owner);
    const isHuman = w.ball.ownerTeam === 'A' && owner === w.teamA[controlledIdx];
    if (!isHuman && owner && owner.cooldown <= 0) {
      aiDecide(owner, w, dt, log);
    }
  }
}

function aiMove(p, w, team, dt) {
  const baseSpeed = 10 + (p.card.pac - 60) * 0.12;
  const ball = w.ball;
  const ownBall = ball.ownerTeam === team;
  const enemyBall = ball.ownerTeam && ball.ownerTeam !== team;
  const isCarrier = ball.owner === p.id;
  const attackDir = team === 'A' ? -1 : 1;       // y-direction toward opponent goal
  const opponentGoalY = team === 'A' ? 0 : 140;
  const ownGoalY = team === 'A' ? 140 : 0;

  const teammates = team === 'A' ? w.teamA : w.teamB;
  const enemies = team === 'A' ? w.teamB : w.teamA;

  let closestIdx = 0, cd = Infinity;
  teammates.forEach((t, i) => {
    const d = dist(t, ball);
    if (d < cd) { cd = d; closestIdx = i; }
  });
  const isPursuer = teammates[closestIdx] === p;

  let tx = p.home.x, ty = p.home.y;
  let speed = baseSpeed;

  if (p.pos === 'GK') {
    tx = 50 + (ball.x - 50) * 0.25;
    ty = team === 'A' ? 134 : 6;
  } else if (isCarrier) {
    // Carry the ball toward goal, sidestepping the nearest defender in our path.
    const threat = nearestEnemyAhead(p, enemies, attackDir, 14);
    if (threat) {
      const side = threat.x >= p.x ? -1 : 1;
      tx = clamp(p.x + side * 10, 6, 94);
      ty = p.y + attackDir * 8;
    } else {
      // Funnel toward the middle of the pitch as we advance.
      tx = p.x + (50 - p.x) * 0.35;
      ty = p.y + attackDir * 20;
    }
    // Carriers push the ball with intent — a touch quicker than stock AI.
    speed = baseSpeed * 1.15;
  } else if (ownBall) {
    // Off-ball attacking run: spread out and get ahead of the ball.
    const carrier = ball.owner ? getPlayer(w, ball.owner) : null;
    const lane = p.home.x < 50 ? -1 : p.home.x > 50 ? 1 : 0;
    // 0 at our own goal, 1 at the opponent's goal — DEFs push higher as the ball advances.
    const ballAdvance = clamp((ownGoalY - ball.y) * -attackDir / 140, 0, 1);
    if (p.pos === 'FWD') {
      tx = clamp(p.home.x + (ball.x - 50) * 0.25, 10, 90);
      ty = clamp(p.home.y + attackDir * 42, 8, 132);
    } else if (p.pos === 'MID') {
      tx = clamp(p.home.x + (ball.x - 50) * 0.35, 8, 92);
      ty = clamp(p.home.y + attackDir * (18 + ballAdvance * 30), 10, 130);
    } else if (p.pos === 'DEF') {
      // Defenders can follow the attack past midfield when the ball is advanced.
      const defPush = 8 + ballAdvance * 32;
      tx = clamp(p.home.x + (ball.x - 50) * 0.2, 6, 94);
      ty = clamp(p.home.y + attackDir * defPush, 10, 132);
    }
    // Break out of clustering with the carrier — drift sideways.
    if (carrier && dist(p, carrier) < 10) {
      tx = clamp(tx + (lane || (p.x < carrier.x ? -1 : 1)) * 10, 6, 94);
    }
    // Hustle into the run so passes land.
    speed = baseSpeed * 1.1;
  } else if (enemyBall) {
    if (isPursuer && cd < 30) {
      tx = ball.x; ty = ball.y;
    } else {
      // Hold a defensive shape between the ball and our own goal.
      tx = clamp(p.home.x + (ball.x - 50) * 0.2, 6, 94);
      ty = p.home.y + attackDir * -4;
    }
    // Sprint back if we're way ahead of our home line (e.g. a defender caught upfield).
    const outOfPos = (p.home.y - p.y) * -attackDir;
    if (outOfPos > 15) speed = baseSpeed * 1.35;
  } else if (isPursuer) {
    tx = ball.x; ty = ball.y;
  }

  const dx = tx - p.x, dy = ty - p.y;
  const mag = Math.hypot(dx, dy);
  if (mag > 0.5) {
    p.vx = (dx / mag) * speed;
    p.vy = (dy / mag) * speed;
  } else {
    p.vx *= 0.8; p.vy *= 0.8;
  }
}

// ----- AI decision making: pass a lot, shoot only when it's actually on -----
function aiDecide(owner, w, dt, log) {
  if (owner.pos === 'GK') {
    // Keeper always clears upfield rather than dribbling.
    if (Math.random() < 3.0 * dt) doPassFor(owner, w, log);
    return;
  }

  const attackDir = owner.team === 'A' ? -1 : 1;
  const goalY = owner.team === 'A' ? 0 : 140;
  const distToGoal = Math.hypot(owner.x - 50, owner.y - goalY);
  const attackDepth = (owner.y - goalY) * -attackDir; // 0 = on goal line, 140 = own goal
  const enemies = owner.team === 'A' ? w.teamB : w.teamA;
  const teammates = (owner.team === 'A' ? w.teamA : w.teamB).filter(t => t !== owner);

  const pressure = enemies.reduce((m, e) => Math.min(m, dist(e, owner)), Infinity);
  const shot = evalShot(owner, enemies, goalY);
  // "Blocked" = near goal but no real shot on — happens when a defender sits in front.
  const shotBlocked = distToGoal < 25 && shot.quality < 0.4;
  const pass = bestPassTarget(owner, teammates, enemies, attackDir, pressure, shotBlocked);

  // 1) Under close pressure, offload to the safest option quickly.
  if (pressure < 3.2 && pass && Math.random() < 4.0 * dt) {
    doPassFor(owner, w, log, pass.target);
    return;
  }

  // 2) Assist pass: teammate is in a better scoring spot than we are.
  if (pass && pass.score > 18) {
    const receiverDepth = (pass.target.y - goalY) * -attackDir;
    if (receiverDepth < 22 && receiverDepth < attackDepth - 4 && Math.random() < 3.0 * dt) {
      doPassFor(owner, w, log, pass.target);
      return;
    }
  }

  // 3) Shot is on goal but blocked — recycle to an open teammate (often backward).
  if (shotBlocked && pass && Math.random() < 3.0 * dt) {
    doPassFor(owner, w, log, pass.target);
    return;
  }

  // 4) Shoot — tiered so close-range chances almost always get taken.
  if (distToGoal < 10 && shot.quality > 0.1 && Math.random() < 5.0 * dt) {
    doShootFor(owner, w, log); return;
  }
  if (distToGoal < 18 && shot.quality > 0.3 && Math.random() < 2.8 * dt) {
    doShootFor(owner, w, log); return;
  }
  if (distToGoal < 26 && shot.quality > 0.5 && Math.random() < 1.4 * dt) {
    doShootFor(owner, w, log); return;
  }

  // 4) A pass that meaningfully improves the attack — take it.
  if (pass && pass.score > 6 && Math.random() < 2.4 * dt) {
    doPassFor(owner, w, log, pass.target);
    return;
  }

  // 5) In our attacking half with a decent pass — keep play moving, don't just dribble.
  if (attackDepth < 70 && pass && pass.score > -5 && Math.random() < 1.2 * dt) {
    doPassFor(owner, w, log, pass.target);
    return;
  }

  // 6) Safety valve: hopeful pass if we've been holding too long.
  if (pass && Math.random() < 0.6 * dt) {
    doPassFor(owner, w, log, pass.target);
  }
}

// Find the best teammate to pass to, scoring forward progress, open space, and pass lane.
function bestPassTarget(owner, teammates, enemies, attackDir, pressure, shotBlocked) {
  // When we can't shoot or we're under pressure, don't insist on forward passes —
  // a backward pass to a teammate in space is much better than losing the ball.
  const underPressure = pressure < 3.5 || shotBlocked;
  let best = null, bestScore = -Infinity;
  for (const t of teammates) {
    if (t.pos === 'GK') continue;
    const d = dist(t, owner);
    if (d < 4 || d > 50) continue;
    const forward = (owner.y - t.y) * -attackDir;   // >0 means receiver is ahead
    const enemyD = enemies.reduce((m, e) => Math.min(m, dist(e, t)), Infinity);
    const lane = laneObstruction(owner, t, enemies);
    // "Openness": 0 if a defender is right on the receiver, 1 if they're 12+ units clear.
    const openness = clamp((enemyD - 4) / 8, 0, 1);
    // Amplify forward progress when the receiver is genuinely open; suppress it when
    // a defender is close enough to intercept or immediately tackle.
    const forwardScale = underPressure ? 0.4 : 0.6 + 1.2 * openness;
    const forwardBonus = forward * forwardScale;
    // Direct safety term: penalize receivers under 4 units of pressure, cap the reward at +12.
    const safety = clamp(enemyD - 4, -5, 10) * 1.2;
    const score = forwardBonus
                + safety
                - Math.abs(d - 22) * 0.2
                - lane * 18;
    if (score > bestScore) { bestScore = score; best = t; }
  }
  return best ? { target: best, score: bestScore } : null;
}

// Count enemies sitting on the line segment from → to (within a small corridor).
function laneObstruction(from, to, enemies) {
  const dx = to.x - from.x, dy = to.y - from.y;
  const len = Math.hypot(dx, dy) || 1;
  const nx = dx / len, ny = dy / len;
  let count = 0;
  for (const e of enemies) {
    const ex = e.x - from.x, ey = e.y - from.y;
    const along = ex * nx + ey * ny;
    if (along < 2 || along > len - 2) continue;
    const perp = Math.abs(ex * -ny + ey * nx);
    if (perp < 2.5) count++;
  }
  return count;
}

// Shot quality 0..1 — combines range, angle, lane obstruction, and shooter ability.
function evalShot(owner, enemies, goalY) {
  const dx = 50 - owner.x;
  const dy = goalY - owner.y;
  const range = Math.hypot(dx, dy);
  if (range > 40) return { quality: 0, range };
  const angle = Math.abs(Math.atan2(dx, Math.abs(dy) || 0.01));
  const angleFactor = Math.max(0, 1 - angle / (Math.PI / 2));  // 90° = unshootable
  const rangeFactor = Math.max(0, 1 - range / 40);
  const block = laneObstruction(owner, { x: 50, y: goalY }, enemies);
  // Floor the block penalty: a tight-angle shot through traffic is still a shot.
  const blockFactor = Math.max(0.3, 1 - block * 0.3);
  const shoFactor = 0.5 + (owner.card.sho / 200);
  return { quality: angleFactor * rangeFactor * blockFactor * shoFactor, range };
}

function nearestEnemyAhead(p, enemies, attackDir, aheadBand) {
  let best = null, bestD = Infinity;
  for (const e of enemies) {
    const ahead = (p.y - e.y) * -attackDir;  // >0 if enemy is between p and the goal
    if (ahead < 0 || ahead > aheadBand) continue;
    const d = dist(e, p);
    if (d < bestD) { bestD = d; best = e; }
  }
  return best;
}

// ============================================================
// Actions
// ============================================================
function doPass(w, idx, log) {
  const p = w.teamA[idx];
  if (!p || w.ball.owner !== p.id) return;
  doPassFor(p, w, log);
}
function doPassFor(p, w, log, forcedTarget) {
  let best = forcedTarget || null;
  if (!best) {
    const teammates = (p.team === 'A' ? w.teamA : w.teamB).filter(t => t !== p);
    const attackDir = p.team === 'A' ? -1 : 1;
    let bestScore = -Infinity;
    teammates.forEach(t => {
      const dy = (t.y - p.y) * attackDir;
      const d = dist(t, p);
      if (d < 4 || d > 45) return;
      const score = dy * 2 - d * 0.5;
      if (score > bestScore) { bestScore = score; best = t; }
    });
    if (!best) best = teammates.sort((a,b) => dist(a,p) - dist(b,p))[0];
  }
  if (!best) return;
  // Aim slightly ahead of a moving receiver so they run onto the ball.
  const leadX = best.x + (best.vx || 0) * 0.3;
  const leadY = best.y + (best.vy || 0) * 0.3;
  const dx = leadX - p.x, dy = leadY - p.y;
  const d = Math.hypot(dx, dy) || 1;
  // Scale power to target distance so the pass actually arrives (ball friction ≈ 1/2.4 per unit v0).
  const power = clamp(2.4 * d + 8, 25, 115);
  w.ball.owner = null;
  w.ball.ownerTeam = null;
  w.ball.vx = (dx / d) * power;
  w.ball.vy = (dy / d) * power;
  w.ball.lastTouchTeam = p.team;
  w.ball.lastTouchId = p.id;
  p.cooldown = 0.25;
  log({ kind: 'pass', text: `${p.card.name} → ${best.card.name}.`, team: p.team });
}

function doShoot(w, idx, log) {
  const p = w.teamA[idx];
  if (!p || w.ball.owner !== p.id) return;
  doShootFor(p, w, log);
}
function doShootFor(p, w, log) {
  const targetY = p.team === 'A' ? 0 : 140;
  const targetX = 50 + (Math.random() - 0.5) * 14;
  const dx = targetX - p.x, dy = targetY - p.y;
  const d = Math.hypot(dx, dy) || 1;
  // Shot has to cross the goal line with pace to spare — scale by range + SHO.
  const power = clamp(2.4 * d + 22 + (p.card.sho - 70) * 0.6, 70, 150);
  const spread = (100 - p.card.sho) * 0.015;
  const angle = Math.atan2(dy, dx) + (Math.random() - 0.5) * spread;
  w.ball.owner = null;
  w.ball.ownerTeam = null;
  w.ball.vx = Math.cos(angle) * power;
  w.ball.vy = Math.sin(angle) * power;
  w.ball.lastTouchTeam = p.team;
  w.ball.lastTouchId = p.id;
  p.cooldown = 0.3;
  log({ kind: 'shot', text: `${p.card.name} shoots!`, team: p.team, flash: true });
}

function resetKickoff(w, receivingTeam) {
  // Reset positions to home
  [...w.teamA, ...w.teamB].forEach(p => {
    p.x = p.home.x; p.y = p.home.y; p.vx = 0; p.vy = 0;
  });
  w.ball.x = 50; w.ball.y = 70;
  w.ball.vx = 0; w.ball.vy = 0;
  w.ball.owner = null;
  w.ball.ownerTeam = null;
  // Give ball to receiving team's closest MID
  const team = receivingTeam === 'A' ? w.teamA : w.teamB;
  const mid = team.find(p => p.pos === 'MID') || team[0];
  if (mid) {
    mid.x = 50; mid.y = 70;
    w.ball.owner = mid.id;
    w.ball.ownerTeam = mid.team;
  }
}

// ============================================================
// Helpers
// ============================================================
function dist(a, b) { return Math.hypot(a.x - b.x, a.y - b.y); }
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function getPlayer(w, id) { return w.teamA.find(p => p.id === id) || w.teamB.find(p => p.id === id); }
function findLastTouch(w, team) { if (w.ball.lastTouchTeam === team) return getPlayer(w, w.ball.lastTouchId); return null; }
function heading(p) {
  const m = Math.hypot(p.vx, p.vy);
  if (m < 0.5) return { x: 0, y: p.team === 'A' ? -1 : 1 };
  return { x: p.vx / m, y: p.vy / m };
}

// ============================================================
// UI Components
// ============================================================
function LiveScoreboard({ score, clock, opponent, paused }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 20,
      padding: '14px 24px',
      background: 'linear-gradient(180deg, #0e1114, #0a0d0e)',
      border: '1px solid #1a1f26', borderRadius: 12,
    }}>
      <div style={{ flex: 1, textAlign: 'right', display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 10 }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: '#fff', fontWeight: 600 }}>Your XI</div>
        <div style={{ width: 8, height: 24, background: '#ffc43d', borderRadius: 2, boxShadow: '0 0 12px #ffc43d66' }} />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '0 20px' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 42, color: '#fff', lineHeight: 1, fontWeight: 700, minWidth: 28, textAlign: 'center' }}>{score.a}</div>
        <div style={{ color: paused ? '#ff5a3c' : '#5a6169', fontFamily: 'var(--font-mono)', fontSize: 13, minWidth: 42, textAlign: 'center' }}>
          {paused ? 'PAUSE' : `${String(Math.floor(clock)).padStart(2,'0')}'`}
        </div>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 42, color: '#fff', lineHeight: 1, fontWeight: 700, minWidth: 28, textAlign: 'center' }}>{score.b}</div>
      </div>
      <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ width: 8, height: 24, background: window.getOpponentDisplayColor(opponent.teamKey, '#8a9099'), borderRadius: 2 }} />
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, color: '#fff', fontWeight: 600 }}>{opponent.name}</div>
      </div>
    </div>
  );
}

function LivePitch({ world, controlled, lastAction }) {
  // convert virtual (0..100 x, 0..140 y) to percent of container
  const sz = (v, max) => `${(v/max)*100}%`;
  const ball = world.ball;

  return (
    <div style={{
      position: 'relative',
      aspectRatio: '0.71',
      background: `
        repeating-linear-gradient(0deg, #0f1e15 0 34px, #0d1b13 34px 68px),
        linear-gradient(180deg, #0f1e15 0%, #0a140e 100%)
      `,
      borderRadius: 12,
      border: '1.5px solid #1a2820',
      overflow: 'hidden',
      boxShadow: 'inset 0 0 60px rgba(0,0,0,0.5)',
    }}>
      <svg viewBox="0 0 100 140" preserveAspectRatio="none" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
        <g fill="none" stroke="rgba(255,255,255,0.15)" strokeWidth="0.3">
          <rect x="2" y="2" width="96" height="136" />
          <line x1="2" y1="70" x2="98" y2="70" />
          <circle cx="50" cy="70" r="10" />
          <circle cx="50" cy="70" r="0.8" fill="rgba(255,255,255,0.3)" />
          <rect x="28" y="2" width="44" height="14" />
          <rect x="38" y="2" width="24" height="6" />
          <rect x="28" y="124" width="44" height="14" />
          <rect x="38" y="132" width="24" height="6" />
        </g>
        {/* goal mouths highlight */}
        <line x1="38" y1="2" x2="62" y2="2" stroke="rgba(255,196,61,0.3)" strokeWidth="0.6" />
        <line x1="38" y1="138" x2="62" y2="138" stroke="rgba(255,196,61,0.3)" strokeWidth="0.6" />
      </svg>

      {/* Players */}
      {world.teamA.map((p, i) => (
        <PlayerDot key={`A-${p.id}`} p={p} color="#ffc43d" isControlled={p === controlled} world={world} />
      ))}
      {world.teamB.map((p, i) => (
        <PlayerDot key={`B-${p.id}`} p={p} color={window.getOpponentDisplayColor(world.opponentTeamKey, '#3ea9ff')} world={world} />
      ))}

      {/* Ball */}
      <div style={{
        position: 'absolute',
        left: `${ball.x}%`, top: `${(ball.y/140)*100}%`,
        transform: 'translate(-50%,-50%)',
        zIndex: 10,
        pointerEvents: 'none',
      }}>
        <div style={{
          width: 11, height: 11, borderRadius: '50%',
          background: '#fff',
          boxShadow: '0 0 14px #fff, 0 2px 4px rgba(0,0,0,0.6)',
          border: '1px solid #0a0d0e',
        }} />
      </div>

      {/* Last action caption */}
      {lastAction && (
        <div style={{
          position: 'absolute', top: 16, left: '50%', transform: 'translateX(-50%)',
          padding: '8px 16px',
          background: lastAction.kind === 'goal' ? 'rgba(255,196,61,0.95)' : 'rgba(10,13,14,0.9)',
          color: lastAction.kind === 'goal' ? '#0a0d0e' : '#fff',
          borderRadius: 6,
          fontFamily: 'var(--font-display)',
          fontWeight: 700,
          fontSize: lastAction.kind === 'goal' ? 22 : 13,
          letterSpacing: '0.04em',
          boxShadow: lastAction.kind === 'goal' ? '0 0 40px rgba(255,196,61,0.8)' : '0 4px 12px rgba(0,0,0,0.5)',
          animation: 'goalFlash 0.3s ease',
        }}>
          {lastAction.text}
        </div>
      )}
    </div>
  );
}

function PlayerDot({ p, color, isControlled, world }) {
  const hasBall = world.ball.owner === p.id;
  return (
    <div style={{
      position: 'absolute',
      left: `${p.x}%`, top: `${(p.y/140)*100}%`,
      transform: 'translate(-50%,-50%)',
      pointerEvents: 'none',
      zIndex: isControlled ? 8 : hasBall ? 7 : 5,
    }}>
      {isControlled && (
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          transform: 'translate(-50%,-50%)',
          width: 28, height: 28, borderRadius: '50%',
          border: '2px solid #fff',
          boxShadow: '0 0 16px #fff',
          animation: 'pulseGlow 1.2s ease-in-out infinite',
        }} />
      )}
      <div style={{
        width: hasBall ? 16 : 13, height: hasBall ? 16 : 13, borderRadius: '50%',
        background: color,
        boxShadow: `0 0 ${hasBall ? 18 : 6}px ${color}`,
        border: '2px solid rgba(0,0,0,0.5)',
        position: 'relative',
      }} />
      <div style={{
        position: 'absolute', top: '100%', left: '50%', transform: 'translateX(-50%)',
        fontSize: 8, color: '#fff', marginTop: 3, whiteSpace: 'nowrap',
        textShadow: '0 1px 2px rgba(0,0,0,0.95)', fontWeight: 600,
        letterSpacing: '0.04em',
      }}>{p.card.name.split(' ').slice(-1)[0]}</div>
    </div>
  );
}

function ControlPanel({ world, controlledIdx, setControlledIdx, paused, setPaused }) {
  return (
    <div style={{
      marginTop: 12, padding: 14,
      background: '#0a0d0e', border: '1px solid #1a1f26', borderRadius: 10,
    }}>
      <div style={{
        fontSize: 10, color: '#ffc43d', letterSpacing: '0.14em',
        textTransform: 'uppercase', fontWeight: 700, marginBottom: 10,
      }}>◆ Control</div>
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 10 }}>
        <button onClick={() => setControlledIdx(-1)} style={{
          ...ctrlBtn,
          background: controlledIdx === -1 ? '#1e2329' : 'transparent',
          color: controlledIdx === -1 ? '#ffc43d' : '#8a9099',
          borderColor: controlledIdx === -1 ? '#ffc43d' : '#1e2329',
        }}>AI only</button>
        {world.teamA.map((p, i) => (
          <button key={p.id} onClick={() => setControlledIdx(i)} style={{
            ...ctrlBtn,
            background: controlledIdx === i ? '#ffc43d' : 'transparent',
            color: controlledIdx === i ? '#0a0d0e' : '#c4ccd4',
            borderColor: controlledIdx === i ? '#ffc43d' : '#1e2329',
            fontWeight: controlledIdx === i ? 700 : 500,
          }}>
            <span style={{ opacity: 0.55, marginRight: 4, fontSize: 9 }}>{i+1}</span>
            {p.card.name.split(' ').slice(-1)[0]}
            <span style={{ opacity: 0.55, marginLeft: 6, fontSize: 9 }}>{p.pos}</span>
          </button>
        ))}
      </div>
      <div style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        paddingTop: 10, borderTop: '1px solid #1a1f26',
      }}>
        <div style={{ fontSize: 10, color: '#6a7380', letterSpacing: '0.06em' }}>
          {controlledIdx === -1
            ? 'Press 1–9 to take control.'
            : <>
                <Kbd>WASD</Kbd>/<Kbd>←↑↓→</Kbd> move · <Kbd>Space</Kbd> pass · <Kbd>X</Kbd> shoot · <Kbd>0</Kbd>/<Kbd>Esc</Kbd> release
              </>
          }
        </div>
        <button onClick={() => setPaused(p=>!p)} style={{...ctrlBtn, color: '#fff', borderColor: paused ? '#ff5a3c' : '#1e2329'}}>
          {paused ? '▸ Resume' : '❚❚ Pause (P)'}
        </button>
      </div>
    </div>
  );
}

const ctrlBtn = {
  background: 'transparent', border: '1px solid #1e2329',
  color: '#c4ccd4', padding: '6px 10px', fontSize: 11,
  borderRadius: 5, cursor: 'pointer', fontFamily: 'var(--font-sans)',
  letterSpacing: '0.02em', transition: 'all 0.15s',
};

function Kbd({ children }) {
  return <span style={{
    display: 'inline-block', padding: '1px 6px', fontSize: 9,
    background: '#1a1f26', color: '#ffc43d', borderRadius: 3,
    fontFamily: 'var(--font-mono)', fontWeight: 600,
    border: '1px solid #2a3139',
    margin: '0 2px',
  }}>{children}</span>;
}

function LiveSidebar({ events, world, opponent }) {
  const feedRef = useRef_L(null);
  useEffect_L(() => {
    if (feedRef.current) feedRef.current.scrollTop = feedRef.current.scrollHeight;
  }, [events.length]);

  const totalPoss = world.possA + world.possB || 1;
  const possAPct = Math.round(world.possA / totalPoss * 100);
  const visible = events.filter(e => ['goal_a','goal_b','shot','save','kickoff','fulltime','pass'].includes(e.kind));

  return (
    <div style={{
      background: '#0a0d0e', border: '1px solid #1a1f26', borderRadius: 12,
      padding: 16, display: 'flex', flexDirection: 'column', maxHeight: 720,
    }}>
      {/* Possession bar */}
      <div style={{ marginBottom: 14 }}>
        <div style={{
          fontSize: 10, color: '#5a6169', letterSpacing: '0.14em',
          textTransform: 'uppercase', fontWeight: 700, marginBottom: 6,
        }}>◆ Possession</div>
        <div style={{
          display: 'flex', height: 8, borderRadius: 4, overflow: 'hidden',
          background: '#1a1f26',
        }}>
          <div style={{ width: `${possAPct}%`, background: '#ffc43d', transition: 'width 0.5s' }} />
          <div style={{ width: `${100-possAPct}%`, background: window.getOpponentDisplayColor(opponent.teamKey, '#3ea9ff'), transition: 'width 0.5s' }} />
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10, color: '#8a9099', fontFamily: 'var(--font-mono)', marginTop: 4 }}>
          <span style={{ color: '#ffc43d' }}>{possAPct}%</span>
          <span>{100-possAPct}%</span>
        </div>
      </div>

      {/* Feed */}
      <div style={{
        fontSize: 10, color: '#5a6169', letterSpacing: '0.14em',
        textTransform: 'uppercase', fontWeight: 700, marginBottom: 8,
      }}>◆ Highlights</div>
      <div ref={feedRef} style={{ flex: 1, overflowY: 'auto', minHeight: 0 }}>
        {visible.map((e, i) => (
          <div key={i} style={{
            padding: '8px 4px 8px 10px',
            borderLeft: `2px solid ${eventColorLive(e.kind)}`,
            marginBottom: 4,
          }}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 2 }}>
              <span style={{
                fontSize: 8.5, color: eventColorLive(e.kind),
                textTransform: 'uppercase', letterSpacing: '0.14em', fontWeight: 700,
              }}>{e.kind.replace('_a','').replace('_b','')}</span>
              <span style={{ fontSize: 9.5, color: '#5a6169', fontFamily: 'var(--font-mono)' }}>
                {Math.floor(e.t || 0)}'
              </span>
              {(e.kind === 'goal_a' || e.kind === 'goal_b') && (
                <span style={{ fontSize: 9.5, color: '#8a9099', fontFamily: 'var(--font-mono)', marginLeft: 'auto' }}>
                  {e.scoreA}–{e.scoreB}
                </span>
              )}
            </div>
            <div style={{
              color: (e.kind === 'goal_a' || e.kind === 'goal_b') ? '#ffc43d' : '#c4ccd4',
              fontSize: 12, lineHeight: 1.35,
              fontWeight: (e.kind === 'goal_a' || e.kind === 'goal_b') ? 600 : 400,
            }}>{e.text}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function eventColorLive(kind) {
  return ({
    goal_a: '#ffc43d', goal_b: '#ff5a3c',
    save: '#3ea9ff', shot: '#c69bff',
    pass: '#8a9099', kickoff: '#58d07a', fulltime: '#ffc43d',
  })[kind] || '#8a9099';
}

// Re-export PostMatch as PostMatchLive (same component)
window.PostMatchLive = function({ match, opponent, onFinish }) {
  const result = match.scoreA > match.scoreB ? 'VICTORY' : match.scoreA < match.scoreB ? 'DEFEAT' : 'DRAW';
  const resultCode = result === 'VICTORY' ? 'W' : result === 'DEFEAT' ? 'L' : 'D';
  const color = result === 'VICTORY' ? '#ffc43d' : result === 'DEFEAT' ? '#ff5a3c' : '#8a9099';
  const coins = result === 'VICTORY' ? 50 : result === 'DRAW' ? 25 : 10;
  return (
    <div style={{
      background: 'linear-gradient(180deg, #0e1114, #0a0d0e)',
      border: `1px solid ${color}44`, borderRadius: 14, padding: 40,
      textAlign: 'center',
      boxShadow: `0 0 60px ${color}22`,
    }}>
      <div style={{ fontSize: 11, color, letterSpacing: '0.25em', textTransform: 'uppercase', fontWeight: 700, marginBottom: 10 }}>◆ Full Time ◆</div>
      <div style={{
        fontFamily: 'var(--font-display)', fontSize: 80, fontWeight: 700,
        color, lineHeight: 1, marginBottom: 10,
        textShadow: `0 0 40px ${color}66`,
      }}>{result}</div>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 44, color: '#fff', fontWeight: 600, marginBottom: 24 }}>
        {match.scoreA} — {match.scoreB}
      </div>
      <div style={{ color: '#8a9099', marginBottom: 24 }}>+{coins} coins</div>
      <button onClick={() => onFinish(resultCode)} style={{
        background: 'linear-gradient(135deg, #ffc43d, #ff9500)',
        color: '#0a0d0e', border: 'none', padding: '14px 32px',
        borderRadius: 8, fontFamily: 'var(--font-display)', fontSize: 17,
        fontWeight: 700, cursor: 'pointer', letterSpacing: '0.04em',
      }}>Continue ▸</button>
    </div>
  );
};

window.LiveMatchScreen = LiveMatchScreen;
