/**
 * LANE-SIM's tuning constants — the single source of truth.
 *
 * These were private to index.ts until round 4. They're exported because other lanes'
 * tests were hand-copying the numbers and going stale in silence: a copy of 0.7 doesn't
 * fail when the sim moves to 0.65, it just quietly asserts the wrong thing forever.
 * Import these rather than retyping them.
 *
 * Anything DATA can already set per-machine (heatPerTick, coolPerTick, bufferCap,
 * beltSpeed, coolRadius) lives in data/machines.json, not here. These are the sim-wide
 * rules that no machine overrides.
 */

// ------------------------------------------------------------------ belts

/** Minimum gap between two items, in belt-tiles. Enforced across the tile seam too. */
export const BELT_SPACING = 0.45;
/** Items a single belt tile may hold. */
export const BELT_CAPACITY = 2;
/** Items a splitter holds while it waits for an output to open. */
export const SPLITTER_CAPACITY = 2;

// -------------------------------------------------------------- machines

/** A machine stalls ('output full') once any output piles to this multiple of its yield. */
export const OUTPUT_STALL_FACTOR = 4;
/** A machine accepts an input up to this multiple of the recipe requirement. */
export const INPUT_BUFFER_FACTOR = 2;

// ------------------------------------------------------------------ heat
//
// heatPerTick is GROSS (contracts v4): net climb = heatPerTick - coolPerTick, so a
// machine whose heatPerTick never clears its cooling can never overheat.

/** Below this heat, throttling is purely cosmetic. */
export const HEAT_THROTTLE_START = 0.7;
/** Speed multiplier at heat 1.0 — the codex's "tick rate visibly halves". */
export const HEAT_THROTTLE_FLOOR = 0.5;
/** Heat at which a machine scrams. */
export const HEAT_SCRAM = 1;
/** Cool back below this and a scrammed machine restarts. */
export const HEAT_RESTART = 0.5;
/** Per-tick cooling when a machine's def doesn't specify `coolPerTick`. */
export const HEAT_COOL_DEFAULT = 0.004;
/**
 * Ceiling on a machine's total cooling once auras stack (own rate + every cooler in
 * range). At 0.05 a machine sheds a full 1.0 of heat in 20 ticks (~0.67s), which is
 * already effectively instant — past this, extra coolers would buy nothing but would
 * still cost bandwidth and floor space, and stacking them would look broken rather than
 * generous. See NOTES round 4.
 */
export const HEAT_COOL_MAX = 0.05;

// ---------------------------------------------------------------- research

/**
 * Ticks a lab spends turning ONE delivered science pack into one unit of progress.
 * Research is an investment, not a purchase: at 30 tps this is ~2s per pack, so a
 * 10-pack tech is ~20s of lab time on one lab, or ~10s on two (labs parallelize).
 * Stepped, not fractional — a pack is consumed and progress ticks up by a whole 1 only
 * when the timer completes, which keeps `research.progress` integer and the determinism
 * trivial to reason about.
 */
export const RESEARCH_TICKS_PER_PACK = 60;

// ---------------------------------------------------------------- wildlife
//
// The dust→swarm loop, M2's living hazard. These item ids are the coupling to DATA: the
// pile grows from the dust a machine is HOLDING (so a factory that belts its dust away
// never hatches), and the trap is any machine whose recipe cans the swarm.

/** The item whose accumulation breeds swarms (codex: HF DUST "attracts and hatches"). */
export const DUST_ITEM = 'hf-dust';
/** The canned live-swarm item — a trap's output, and what commissions ask for. */
export const SWARM_ITEM = 'mosquito-swarm';

/**
 * Pile growth per tick per unit of dust BACKED UP in a machine's output buffer — where
 * "backed up" means beyond one craft's worth (see DUST_SPILL_GRACE). A line that keeps its
 * dust moving never crosses the grace threshold and never breeds; a line that ignores it
 * climbs to the stall cap and hatches in roughly 400 ticks (~14s). That gap is the mechanic.
 */
export const DUST_GROWTH_PER_HELD = 0.00025;
/**
 * A machine may hold this multiple of its own dust yield without shedding any on the floor.
 * Normal buffering between belt pushes is not neglect, and punishing it would make the
 * hazard feel like weather rather than a consequence.
 */
export const DUST_SPILL_GRACE = 1;
/** A pile at or above this hatches a swarm and is consumed. */
export const DUST_PILE_MAX = 1;
/** Tiles from the emitter a new pile appears (seeded RNG picks the exact offset). */
export const DUST_PILE_SPREAD = 2;

/** Swarm drift toward its target, tiles/sec. */
export const SWARM_DRIFT_TILES_PER_SEC = 1.5;
/** Within this chebyshev range of its target's footprint, a swarm is attached and biting. */
export const SWARM_ATTACH_RANGE = 1;
/** Speed multiplier a fully-severe attached swarm inflicts (0.5 = half speed). */
export const SWARM_SLOW_FLOOR = 0.5;
/** Severity gained per tick while attached, up to 1.0 — the longer it sits, the worse. */
export const SWARM_SEVERITY_GROWTH = 0.004;
/** A trap catches swarms within this chebyshev range of its footprint. */
export const TRAP_RADIUS = 4;
/**
 * Live swarms allowed at once. A machine that is permanently jammed on dust would
 * otherwise breed without limit — measured 29 of them in a 20k demo run, which is a
 * plague rather than a hazard. At the cap, full piles simply sit there waiting for room.
 */
export const SWARM_MAX = 8;

// ------------------------------------------------------------------ relic

/** Nearest a buried relic sits to origin (tiles, euclidean) — well past the starter area. */
export const RELIC_MIN_DIST = 18;
/** Farthest a buried relic sits from origin. */
export const RELIC_MAX_DIST = 28;
/**
 * Keep-outs for relic placement, sized to enclose the reference factory's two works — the
 * western melt line and the eastern research annex — so the demo can always dig its relic
 * up. A real world has no factory here yet; this just keeps the secret clear of the ground
 * everyone builds on. relic.test.ts checks, against the layout's real occupied tiles, that
 * a spread of seeds never buries it. When DATA supplies a `relicReserve` the sim prefers
 * that corner instead and these become belt-and-braces.
 */
export const RELIC_KEEPOUT = [
  { x0: -33, y0: -16, x1: 2, y1: 16 },  // the melt line
  { x0: 6, y0: -9, x1: 29, y1: 12 },    // the research annex
];
/** Chebyshev range within which an `excavate` uncovers the relic. */
export const RELIC_DIG_RANGE = 1;

// ---------------------------------------------------------------- tracing

/** Belt graphs bigger than this are treated as unterminated (cheap loop guard). */
export const MAX_TRACE = 512;
