export interface TimerState {
  readonly startTime: number | null;
  readonly running: boolean;
  readonly pausedAt: number | null;
  readonly pausedMs: number;
}

/**
 * Points gained when a gauge of given level runs for `sec` seconds.
 * Gauge level decreases through tiers: 90k→40, 70k→30, 40k→20, 0→10 pts per 10sec tick.
 */
export function gainedIn(lvl: number, sec: number): number {
  const T = [
    { lo: 90000, r: 40 },
    { lo: 70000, r: 30 },
    { lo: 40000, r: 20 },
    { lo: 0,     r: 10 },
  ];
  let g = Math.min(Math.max(lvl, 0), 100000);
  let tl = Math.floor(sec / 10);
  let out = 0;
  for (const { lo, r } of T) {
    if (g <= lo || tl <= 0) continue;
    const a = g - lo;
    const m = Math.floor(a / r);
    const u = Math.min(m, tl);
    out += u * r;
    tl -= u;
    g = lo;
  }
  return out;
}

/**
 * Seconds needed to gain `pts` points starting from gauge level `lvl`.
 * Returns Infinity if impossible (level too low).
 */
export function timeToGain(lvl: number, pts: number): number {
  if (pts <= 0) return 0;
  const T = [
    { lo: 90000, r: 40 },
    { lo: 70000, r: 30 },
    { lo: 40000, r: 20 },
    { lo: 0,     r: 10 },
  ];
  let s = 0, rem = pts;
  let g = Math.min(Math.max(lvl, 0), 100000);
  for (const { lo, r } of T) {
    if (g <= lo || rem <= 0) continue;
    const a = g - lo;
    const d = Math.min(rem, a);
    s += Math.ceil(d / r) * 10;
    rem -= d;
    g = lo;
  }
  return rem > 0 ? Infinity : s;
}

/**
 * Gauge level after `sec` seconds of depletion.
 */
export function gaugeAfter(lvl: number, sec: number): number {
  const T = [
    { lo: 90000, r: 40 },
    { lo: 70000, r: 30 },
    { lo: 40000, r: 20 },
    { lo: 0,     r: 10 },
  ];
  let g = Math.min(Math.max(lvl, 0), 100000);
  let tl = Math.floor(sec / 10);
  for (const { lo, r } of T) {
    if (g <= lo || tl <= 0) continue;
    const a = g - lo;
    const m = Math.floor(a / r);
    const u = Math.min(m, tl);
    g -= u * r;
    tl -= u;
  }
  return g;
}

export interface GaugeRecharge {
  readonly atSec: number;
  readonly level: number;
}

export interface GaugeState {
  gained: number;       // points accumulés depuis le snapshot
  curGl: number;        // niveau de jauge actuel (après dépletion et recharges)
  effectiveEl: number;  // elapsed effectif (gelé au cap si atteint)
}

/**
 * Calcule les points accumulés et le niveau de jauge actuel en tenant compte
 * des recharges intermédiaires et du cap absolu (ptsAllowed).
 *
 * Algorithme segment par segment :
 *   Pour chaque segment [prevEl → recharge.atSec] :
 *     - gainedIn(prevGl, segDur) points gagnés
 *     - Si le cap est atteint dans ce segment → freeze ici
 *     - Sinon, continuer avec le nouveau niveau après recharge
 */
export function computeGaugeState(
  startGl: number,
  recharges: readonly GaugeRecharge[],
  ptsAllowed: number,
  el: number,
): GaugeState {
  let gained = 0;
  let prevEl = 0;
  let prevGl = startGl;

  const sorted = recharges.filter(r => r.atSec < el).sort((a, b) => a.atSec - b.atSec);

  for (const r of sorted) {
    const segDur = r.atSec - prevEl;
    const segGained = gainedIn(prevGl, segDur);

    if (isFinite(ptsAllowed) && gained + segGained >= ptsAllowed) {
      const ptsNeeded = ptsAllowed - gained;
      const secInSeg = timeToGain(prevGl, ptsNeeded);
      return { gained: ptsAllowed, curGl: Math.max(0, gaugeAfter(prevGl, secInSeg)), effectiveEl: prevEl + secInSeg };
    }

    gained += segGained;
    prevEl = r.atSec;
    prevGl = r.level;
  }

  // Dernier segment (de la dernière recharge jusqu'à el)
  const lastDur = el - prevEl;
  const lastGained = gainedIn(prevGl, lastDur);

  if (isFinite(ptsAllowed) && gained + lastGained >= ptsAllowed) {
    const ptsNeeded = ptsAllowed - gained;
    const secInSeg = timeToGain(prevGl, ptsNeeded);
    return { gained: ptsAllowed, curGl: Math.max(0, gaugeAfter(prevGl, secInSeg)), effectiveEl: prevEl + secInSeg };
  }

  return { gained: gained + lastGained, curGl: Math.max(0, gaugeAfter(prevGl, lastDur)), effectiveEl: el };
}

/**
 * Elapsed seconds for a timer state.
 */
export function elapsed(timer: TimerState): number {
  if (!timer.startTime) return 0;
  if (timer.running) return (Date.now() - timer.startTime - timer.pausedMs) / 1000;
  if (timer.pausedAt) return (timer.pausedAt - timer.startTime - timer.pausedMs) / 1000;
  return 0;
}
