import { describe, it, expect } from 'vitest';
import { computeGaugeState } from '@domain/services/GaugeCalculator';
import type { GaugeRecharge } from '@domain/services/GaugeCalculator';
import { gainedIn, timeToGain } from '@domain/services/GaugeCalculator';
import { xpForLevel, levelFromXp } from '@domain/value-objects/XpTable';

/**
 * Tests de régression pour la fonctionnalité de recharge de jauge en cours de session.
 *
 * Scénario typique : le joueur recharge la mangeoire pendant le timer car elle s'est vidée.
 * Sans recharge prise en compte, le countdown reste bloqué à ∞ ou 0 au lieu de recalculer.
 */
describe('Régression: recharge de jauge en cours de timer', () => {

  describe('Mangeoire rechargée une fois (scénario XP principal)', () => {
    // Contexte :
    //   - Mangeoire départ = 50 000 (tier 2, rate 20/tick)
    //   - DD level 1, cible level 50
    //   - Après 1h (3600 sec), la mangeoire est vide → XP gagnée jusqu'ici
    //   - Le joueur recharge à 80 000 (tier 3)
    //   - On simule 1h supplémentaire après recharge

    const startGl = 50000;
    const xpCible = xpForLevel(50) - xpForLevel(1); // 34 365 XP

    it('sans recharge : XP limité par la jauge qui se vide', () => {
      // 50 000 pts de jauge → max 50 000 XP disponible (tier 2 → tier 1)
      // Mais XP cible = 34 365 → la jauge suffira-t-elle ?
      // timeToGain(50000, 34365) :
      //   tier2 (40000-50000) : 10000 pts, s += ceil(10000/20)*10=5000, rem=24365
      //   tier1 (0-40000)     : 40000 pts, s += ceil(24365/10)*10=24370, rem=0
      //   total = 29370 sec ≈ 8h 9m
      const sec = timeToGain(startGl, xpCible);
      expect(sec).toBe(29370);
    });

    it('avec recharge à t=5000 sec : XP total = seg1 + seg2', () => {
      // Seg1 : 50000 (tier2→tier1), 5000 sec → gainedIn(50000, 5000) = ?
      //   tier2 (40000-50000) : a=10000, m=500, u=500 ticks → 10000 pts, tl=0
      //   → gained seg1 = 10000
      // Recharge à 80000 à t=5000
      // Seg2 : 80000 (tier3), 5000 sec → gainedIn(80000, 5000) = ?
      //   tier3 (70000-80000) : a=10000, m=333, u=333 ticks → 9990, tl=167
      //   tier2 (40000-70000) : a=30000, m=1500, u=167 ticks → 3340
      //   → gained seg2 = 13330
      // Total = 23330 XP
      const recharges: GaugeRecharge[] = [{ atSec: 5000, level: 80000 }];
      const { gained } = computeGaugeState(startGl, recharges, Infinity, 10000);
      const expectedSeg1 = gainedIn(50000, 5000);
      const expectedSeg2 = gainedIn(80000, 5000);
      expect(gained).toBe(expectedSeg1 + expectedSeg2);
    });

    it('la recharge fait redémarrer le countdown vers la cible', () => {
      // Après recharge : XP restante = xpCible - gained_so_far
      // Le countdown doit utiliser curGl (niveau après recharge et dépletion seg2)
      const rechargeEl = 5000; // 5000 sec = quand le joueur recharge
      const totalEl = 10000;
      const recharges: GaugeRecharge[] = [{ atSec: rechargeEl, level: 80000 }];

      const { gained: gainedSoFar, curGl } = computeGaugeState(startGl, recharges, Infinity, totalEl);
      const xpRestante = Math.max(0, xpCible - gainedSoFar);

      // curGl doit refléter la jauge après seg2 (pas celle d'origine à 50000)
      expect(curGl).toBeGreaterThan(0);
      expect(curGl).toBeLessThan(80000); // la jauge a déjà décru depuis la recharge

      // timeToGain depuis curGl doit être < timeToGain depuis startGl (grâce à la recharge)
      const secAvecRecharge = timeToGain(curGl, xpRestante);
      const secSansRecharge = timeToGain(gainedIn(startGl, 5000) > 0 ? 0 : startGl, xpRestante);
      expect(secAvecRecharge).toBeLessThan(secSansRecharge === Infinity ? Infinity : secSansRecharge + 1);
    });
  });

  describe('Baffeur rechargé (sérenité)', () => {
    // Baffeur 70000 (tier2 → cap à -5000 séren.)
    // Séren. départ = 0, cap absolu = 5000 pts à gagner
    // À t=3000 sec, cap atteint → gel. Recharge à t=4000 → ne change rien (déjà gelé).

    const startGl = 70000;
    const ptsToAbsCap = 5000; // 0 - (-5000)

    it('gel au cap absolu même avec recharge après le gel', () => {
      const recharges: GaugeRecharge[] = [{ atSec: 4000, level: 90000 }]; // recharge après gel
      const { gained, effectiveEl } = computeGaugeState(startGl, recharges, ptsToAbsCap, 9999);

      // Gel = timeToGain(70000, 5000) :
      //   tier2 (40000-70000) : 5000 pts → ceil(5000/20)*10 = 2500 sec
      expect(gained).toBe(5000);
      expect(effectiveEl).toBe(2500);
      // La recharge à t=4000 est APRÈS le gel à t=2500 → ignorée
    });

    it('recharge AVANT le gel allonge le temps de traitement', () => {
      // Recharge à t=1000 (avant gel naturel à t=2500)
      // Seg1 : 70000, 1000 sec → gainedIn(70000, 1000) = 100 ticks
      //   tier3 (70000-90000): a=0 (70000 non > 70000... wait 70000>70000=false)
      //   Hmm: 70000 > 70000 = false, donc tier3 skip
      //   tier2 (40000-70000): a=30000, m=1500, u=min(1500,100)=100, out=2000
      //   gained seg1 = 2000, jauge = 70000 - 100*20 = 68000
      // Recharge à 90000 à t=1000
      // Seg2 : 90000, besoin encore 3000 pts, timeToGain(90000,3000) :
      //   tier3 (70000-90000): 20000 pts, 3000/30=100 ticks=1000 sec
      // effectiveEl = 1000 + 1000 = 2000
      const recharges: GaugeRecharge[] = [{ atSec: 1000, level: 90000 }];
      const { gained, effectiveEl } = computeGaugeState(70000, recharges, 5000, 9999);
      expect(gained).toBe(5000);
      expect(effectiveEl).toBe(2000); // plus rapide qu'à 2500 grâce au tier3
    });
  });

  describe('Deux recharges successives (mangeoire longue session)', () => {
    it('accumule correctement trois segments', () => {
      // Seg1 : 30000 (tier1), 500 sec → 50 ticks × 10 = 500 pts
      // Recharge 1 à t=500 → 80000
      // Seg2 : 80000 (tier2→tier3), 500 sec → gainedIn(80000, 500)
      // Recharge 2 à t=1000 → 95000
      // Seg3 : 95000 (tier4), 500 sec → gainedIn(95000, 500)
      const recharges: GaugeRecharge[] = [
        { atSec: 500, level: 80000 },
        { atSec: 1000, level: 95000 },
      ];
      const { gained } = computeGaugeState(30000, recharges, Infinity, 1500);
      const expected = gainedIn(30000, 500) + gainedIn(80000, 500) + gainedIn(95000, 500);
      expect(gained).toBe(expected);
    });

    it('le niveau XP estimé est cohérent avec les points accumulés', () => {
      const startXp = 1; // level 1
      const startGl = 30000;
      const recharges: GaugeRecharge[] = [
        { atSec: 500, level: 80000 },
        { atSec: 1000, level: 95000 },
      ];
      const { gained } = computeGaugeState(startGl, recharges, Infinity, 1500);
      const estLevel = levelFromXp(xpForLevel(startXp) + gained);
      // Avec 3 segments : ~500 + ~gainedIn(80000,500) + ~gainedIn(95000,500) pts d'XP
      // ça devrait faire progresser significativement le niveau
      expect(estLevel).toBeGreaterThan(startXp);
    });
  });
});
