import { GAMEPLAY_RULES } from "./game-rules.ts";

/** An observed, scripted mid-run state; this does not run or modify the game. */
export const STRATEGY_CASE = Object.freeze({
  width: 440, height: 700, fps: 60, frames: 138,
  initialY: 340, initialVelocity: 300, firstTreeDistance: 120,
  gapCenters: [360, 280] as readonly number[],
  // Half-open frame intervals: [0, 24) means hold for the first 0.400 seconds.
  holdFrames: [[0, 23], [40, 55], [73, 94], [121, 138]] as readonly (readonly number[])[],
});

export type StrategyFrame = {
  frame: number; seconds: number; held: boolean; y: number; velocity: number;
  treeXs: number[]; score: number; collision: number | null; clearance: number | null;
};

export function simulateStrategyCase(delayFrames = 0): StrategyFrame[] {
  const c = STRATEGY_CASE, dt = 1 / c.fps;
  const scale = Math.max(.8, Math.min(1.08, c.height / 680));
  const speed = 185 * scale, gap = 174 * scale, spacing = 248 * scale;
  const spiderX = c.width * .27, rules = GAMEPLAY_RULES;
  const treeXs = c.gapCenters.map((_, i) => spiderX + c.firstTreeDistance + i * spacing);
  let y = c.initialY, velocity = c.initialVelocity;
  const passed = new Set<number>();
  const frames: StrategyFrame[] = [{ frame: 0, seconds: 0, held: delayFrames === 0, y, velocity, treeXs: [...treeXs], score: 0, collision: null, clearance: null }];
  for (let tick = 0; tick < c.frames; tick++) {
    const held = tick >= delayFrames && c.holdFrames.some(([from, to]) => tick >= from && tick < to);
    // Same update order, speed caps, obstacle width and collision comparisons as Classic.
    velocity += (held ? rules.physics.heldAcceleration : rules.physics.releasedAcceleration) * dt;
    velocity = Math.max(rules.physics.upwardSpeedCap, Math.min(rules.physics.downwardSpeedCap, velocity));
    y += velocity * dt;
    let collision: number | null = null, clearance: number | null = null;
    for (let i = 0; i < treeXs.length; i++) {
      treeXs[i] -= speed * dt;
      if (treeXs[i] + 43 < spiderX - rules.collision.halfWidth) passed.add(i);
      const hitX = spiderX + rules.collision.halfWidth > treeXs[i] - 43 && spiderX - rules.collision.halfWidth < treeXs[i] + 43;
      if (hitX) {
        const margin = Math.min(y - rules.collision.halfHeight - (c.gapCenters[i] - gap / 2), c.gapCenters[i] + gap / 2 - y - rules.collision.halfHeight);
        clearance = clearance === null ? margin : Math.min(clearance, margin);
        if (margin < 0) collision = i + 1;
      }
    }
    if (y < rules.collision.ceiling || y > c.height - rules.collision.groundClearance) collision = 0;
    // Classic's crash handler resets the fall speed immediately on impact.
    if (collision !== null) velocity = Math.max(120, velocity);
    frames.push({ frame: tick + 1, seconds: (tick + 1) * dt, held, y, velocity, treeXs: [...treeXs], score: passed.size, collision, clearance });
    if (collision !== null) break;
  }
  return frames;
}
