// levels/difficulty.js (Lane C) — the single difficulty table. GDD §Difficulty & assists.
//
// LAW (charter): no per-level hand-tuned forks. A level is authored once, at `normal`, and
// every difficulty is this table applied to that one authored level. If you ever find
// yourself wanting "but on hard, level 4 should...", the answer is a new event in the level,
// not a branch in here.
//
// Pure data + pure functions, no imports, node-runnable.

/**
 *   coatDrain   x ambient mucus-coat drain rate (the pH pressure)
 *   density     x enemy counts (rounded; deterministic)
 *   window      x boss/hazard timing windows — the one that actually moves the skill floor
 *   surgeSpeed  x chase-hazard speed (reflux surges)
 *   contact     x contact/hull damage taken
 *   assists     defaults for the assist toggles (player can always override — GDD)
 */
export const DIFFICULTIES = Object.freeze({
  easy: {
    label: 'Endoscopy', coatDrain: 0.7, density: 0.75, window: 1.3, surgeSpeed: 0.92, contact: 0.8,
    assists: { flowAutoCenter: true, aimMagnetism: true },
    note: 'For players here to see the body, not to beat it. Windows are the big lever: 1.3x turns the hiatus ring gate from a skill check into a rhythm.',
  },
  normal: {
    label: 'Clinical', coatDrain: 1.0, density: 1.0, window: 1.0, surgeSpeed: 1.0, contact: 1.0,
    assists: { flowAutoCenter: false, aimMagnetism: true },
    note: 'The authored experience. Every number in every level JSON is a normal-mode number.',
  },
  hard: {
    label: 'Terminal', coatDrain: 1.35, density: 1.25, window: 0.8, surgeSpeed: 1.1, contact: 1.25,
    assists: { flowAutoCenter: false, aimMagnetism: false },
    note: 'surgeSpeed 1.1 puts the L2 finale at 23.1 u/s against a 20 u/s current: the chase stops being a sprint and becomes a route-planning problem (antacid or die).',
  },
});

export const DEFAULT_DIFFICULTY = 'normal';

export function getDifficulty(id = DEFAULT_DIFFICULTY) {
  return DIFFICULTIES[id] ?? DIFFICULTIES[DEFAULT_DIFFICULTY];
}

/**
 * → a NEW level object with difficulty applied. Never mutates. Deterministic (same input =>
 * same output, no RNG), so it is safe to run before world generation.
 * Only scales what the table says it scales; everything else passes through untouched.
 */
export function applyDifficulty(level, id = DEFAULT_DIFFICULTY) {
  const d = getDifficulty(id);
  if (id === 'normal') return level;
  const events = level.events.map((e) => {
    if (e.type === 'spawn' && typeof e.count === 'number') {
      const count = Math.max(1, Math.round(e.count * d.density));
      // wall enemies carry one theta per instance — keep the arrays consistent.
      if (Array.isArray(e.theta)) {
        const theta = [];
        for (let i = 0; i < count; i++) theta.push(e.theta[i % e.theta.length]);
        return { ...e, count, theta };
      }
      return { ...e, count };
    }
    if (e.type === 'hazard' && e.kind === 'reflux_surge' && typeof e.speed === 'number') {
      return { ...e, speed: Math.round(e.speed * d.surgeSpeed * 10) / 10 };
    }
    return e;
  });
  return { ...level, events, difficulty: id };
}
