/* ============================================================
   LAUNCHPAD.FAIL — game engine (pure logic, no React)
   window.Engine: curves, crash model, checkpoints, boost, format
   ------------------------------------------------------------
   Two-phase model:
     BOOST  phase  0.0x -> 1.0x   fast, ~4.5s, boosters attached
     <detach at 1.0x: pause + speed drop>
     CRUISE phase  1.0x -> ...    slow exponential, space~1.8x moon~3.0x
   ============================================================ */
window.Engine = (function () {
  const T_BOOST = 4.5;            // seconds to reach 1.0x (boost phase)
  const G_CRUISE = 0.137;         // cruise growth rate
  const DETACH_PAUSE = 0.55;      // seconds of near-pause at detach
  const MAX_MULT = 25;

  /* ---- multiplier as a function of flight time (seconds since blastoff) ---- */
  function multAt(t) {
    if (t <= 0) return 0;
    if (t < T_BOOST) {
      // ease-out to 1.0 — quick off the pad, settling into detach
      const p = t / T_BOOST;
      return +(1 - Math.pow(1 - p, 1.7)).toFixed(4);
    }
    const td = t - T_BOOST;
    if (td < DETACH_PAUSE) return 1.0;                 // brief hold at break-even
    const tc = td - DETACH_PAUSE;
    return +(1 + (Math.exp(G_CRUISE * tc) - 1)).toFixed(4);
  }

  /* inverse: time to reach a given multiplier (for placing landmarks) */
  function tForMult(m) {
    if (m <= 0) return 0;
    if (m < 1) return T_BOOST * (1 - Math.pow(1 - m, 1 / 1.7));
    return T_BOOST + DETACH_PAUSE + Math.log(m) / G_CRUISE;
  }

  /* instantaneous "speed" = dMult/dt, for the speed readout + scroll rate */
  function speedAt(t) {
    const dt = 0.05;
    return Math.max(0, (multAt(t + dt) - multAt(t)) / dt);
  }

  /* ---- altitude (purely visual, always accelerating) in "km" ---- */
  function altAt(t) {
    // exponential-ish; detaches feel like a kick. km units for readout.
    return +(0.6 * (Math.exp(0.165 * t) - 1)).toFixed(2);
  }

  /* ---- checkpoints: multiplier landmarks ---- */
  // type: 'minor' just ticks payout; 'major' is a named world landmark
  const CHECKPOINTS = [
    { m: 0.25, type: 'minor' },
    { m: 0.50, type: 'minor' },
    { m: 0.75, type: 'minor' },
    { m: 1.00, type: 'major', label: 'BOOSTERS DETACH', world: 'detach' },
    { m: 1.25, type: 'minor' },
    { m: 1.50, type: 'minor' },
    { m: 1.80, type: 'major', label: 'EDGE OF SPACE', world: 'space' },
    { m: 2.10, type: 'minor' },
    { m: 2.40, type: 'minor' },
    { m: 2.70, type: 'minor' },
    { m: 3.00, type: 'major', label: 'THE MOON', world: 'moon' },
    { m: 3.50, type: 'minor' },
    { m: 4.00, type: 'minor' },
    { m: 5.00, type: 'major', label: 'MARS', world: 'mars' },
    { m: 7.00, type: 'minor' },
    { m: 10.0, type: 'major', label: 'JUPITER', world: 'jupiter' },
    { m: 15.0, type: 'minor' },
    { m: 25.0, type: 'major', label: 'DEEP SPACE', world: 'deep' },
  ];
  function nextCheckpoint(m) { return CHECKPOINTS.find(c => c.m > m + 1e-6) || null; }
  function lastMajor(m) {
    let r = null;
    for (const c of CHECKPOINTS) { if (c.type === 'major' && c.m <= m + 1e-6) r = c; }
    return r;
  }

  /* ---- crash model: two distinct curves, split at the 1.0x detach ---- */
  // ~12% fail in boost phase (rare, brutal). Rest distribute over cruise, median ~2.2x.
  function genCrash() {
    if (Math.random() < 0.12) {
      // booster failure: weight toward the middle of the boost phase
      const u = (Math.random() + Math.random()) / 2; // triangular
      return +(0.08 + u * 0.9).toFixed(2);
    }
    const v = Math.random();
    const c = 1.0 + (-Math.log(1 - v)) / 0.6; // exp tail, median ~2.16
    return +Math.min(MAX_MULT, c).toFixed(2);
  }

  /* ---- payout ---- */
  const SOL_TO_MOON = 800000;
  function moonPayout(wagerSol, mult) { return Math.round(wagerSol * SOL_TO_MOON * mult); }

  /* ---- formatting ---- */
  function fmtMult(m) { return (m < 0 ? 0 : m).toFixed(2); }
  function fmtMoon(n) {
    if (n >= 1e6) return (n / 1e6).toFixed(2) + 'M';
    if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + 'K';
    return String(Math.round(n));
  }
  function fmtMoonFull(n) { return Math.round(n).toLocaleString(); }
  function fmtAlt(km) {
    if (km < 1) return { num: Math.round(km * 1000), unit: 'm' };
    if (km < 1000) return { num: km.toFixed(1), unit: 'km' };
    return { num: Math.round(km).toLocaleString(), unit: 'km' };
  }

  /* color ramp for a multiplier value (blue->green->gold->red-hot) */
  function multColor(m) {
    if (m < 1) return '#9fd0ff';
    if (m < 1.8) return '#1bd760';
    if (m < 3) return '#7CFF9E';
    if (m < 5) return '#ffd23e';
    if (m < 10) return '#ffa53d';
    return '#ff5630';
  }

  /* ---- boost gauge tuning ---- */
  const BOOST = {
    decayPerSec: 46,      // gauge cools at this rate
    baseStep: 9,          // first-tap bump
    comboWindow: 360,     // ms; taps quicker than this build combo
    comboMax: 2.4,        // step multiplier cap (1 -> 1.5 -> 2 -> 2.4)
    comboGain: 0.5,       // added per quick tap
    greenLo: 58, greenHi: 74,  // optimal band (tight on purpose)
    max: 100,
  };

  /* ---- cinematic timing (all phase durations live here) ---- */
  const TIMING = {
    countdown: 10,        // seconds — the launch ritual (tap to skip)
    igniteAt: 4,          // seconds-remaining when smoke/fire begins building
    blastoffMs: 1300,     // "BLAST OFF!" hold before flight
    ejectAnimMs: 1500,    // rocket away + the fall, before celebration
    winBigMs: 950,        // eject-anim -> show big multiplier
    winPayMs: 2200,       // -> payout count-up
    winCoinsMs: 3500,     // -> coins fly + balance bump
    countUpMs: 1200,      // odometer duration
    fallMs: 1800,         // world-reverse "falling" descent
    loseBtnMs: 1400,      // delay before Try again appears
  };

  /* a near escape: ejected within this much of the crash point */
  function isNearMiss(endMult, crash) { return crash - endMult <= Math.max(0.12, endMult * 0.05); }

  return {
    T_BOOST, G_CRUISE, DETACH_PAUSE, MAX_MULT, SOL_TO_MOON, BOOST, TIMING,
    multAt, tForMult, speedAt, altAt, isNearMiss,
    CHECKPOINTS, nextCheckpoint, lastMajor,
    genCrash, moonPayout,
    fmtMult, fmtMoon, fmtMoonFull, fmtAlt, multColor,
  };
})();
