import { describe, it, expect } from 'vitest';
import { computeGaugeLive, enclosGlobalState, calcLevelEtaLive } from '@presentation/helpers/gauge-live';
import { xpForLevel } from '@domain/value-objects/XpTable';
import { timeToGain } from '@domain/services/GaugeCalculator';
import { createDragodinde } from '@domain/entities/Dragodinde';
import type { Enclos } from '@domain/entities/Enclos';

function makeEnclos(overrides: Partial<Enclos> = {}): Enclos {
  return {
    id: 1,
    name: 'Test',
    activeGauges: ['mangeoire'],
    gaugeLevels: { baffeur: 0, caresseur: 0, foudroyeur: 0, abreuvoir: 0, dragofesse: 0, mangeoire: 50000 },
    dragodindes: [createDragodinde(1)],
    nextDdId: 2,
    timer: { running: false, startTime: null, pausedAt: null, pausedMs: 0, snapGauges: {}, snapStats: {}, gaugeRecharges: {} },
    alerted: {},
    ...overrides,
  } as Enclos;
}

describe('Regression: timer prend en compte levelTarget', () => {

  it('computeGaugeLive utilise levelTarget pour le countdown mangeoire', () => {
    const enc = makeEnclos();
    const dd = { ...enc.dragodindes[0]!, levelTarget: 10 };

    const r = computeGaugeLive(enc, dd, 'mangeoire', 0, false);

    // Avec levelTarget=10, xpNeeded = xpForLevel(10) - xpForLevel(1) = 809
    const xpNeeded = xpForLevel(10) - xpForLevel(1);
    expect(xpNeeded).toBe(809);

    // Le countdown devrait être timeToGain(50000, 809)
    const expectedSec = timeToGain(50000, 809);
    expect(r.cntDown).toBe(expectedSec);
    expect(r.done).toBe(false);
  });

  it('computeGaugeLive sans levelTarget utilise 200 par défaut', () => {
    const enc = makeEnclos();
    const dd = { ...enc.dragodindes[0]!, levelTarget: null };

    const r = computeGaugeLive(enc, dd, 'mangeoire', 0, false);

    // Avec levelTarget=null → 200, xpNeeded >> gauge level
    // cntDown = timeToGain(50000, 50000) = drain time
    const drainTime = timeToGain(50000, 50000);
    expect(r.cntDown).toBe(drainTime);
  });

  it('levelTarget=10 donne un countdown beaucoup plus court que levelTarget=null', () => {
    const enc = makeEnclos();
    const ddWithTarget = { ...enc.dragodindes[0]!, levelTarget: 10 };
    const ddWithoutTarget = { ...enc.dragodindes[0]!, levelTarget: null };

    const rWith = computeGaugeLive(enc, ddWithTarget, 'mangeoire', 0, false);
    const rWithout = computeGaugeLive(enc, ddWithoutTarget, 'mangeoire', 0, false);

    // Avec target 10 : ~410 sec. Sans target (200) : ~45000 sec.
    expect(rWith.cntDown).toBeLessThan(1000);      // < ~16 minutes
    expect(rWithout.cntDown).toBeGreaterThan(40000); // > ~11 heures
  });

  it('enclosGlobalState reflète le levelTarget dans globalMax', () => {
    const enc = makeEnclos();
    enc.dragodindes[0]!.levelTarget = 10;

    const state = enclosGlobalState(enc);
    // Avant démarrage, started=false → globalMax devrait refléter le target 10
    expect(state.globalMax).toBeLessThan(1000);
  });

  it('enclosGlobalState sans levelTarget a un globalMax plus élevé', () => {
    const enc = makeEnclos();
    enc.dragodindes[0]!.levelTarget = null;

    const state = enclosGlobalState(enc);
    expect(state.globalMax).toBeGreaterThan(40000);
  });

  it('calcLevelEtaLive utilise levelTarget', () => {
    const enc = makeEnclos();
    const dd = { ...enc.dragodindes[0]!, levelTarget: 5 };

    const eta = calcLevelEtaLive(enc, dd, 0, false);
    // Level 1→5 needs 161 XP, timeToGain(50000, 161) = 90 sec
    expect(eta).toContain('1m');
  });

  it('done=true quand estLevel >= levelTarget', () => {
    const enc = makeEnclos();
    // DD déjà au level 50, target 50
    const dd = { ...enc.dragodindes[0]!, stats: { ...enc.dragodindes[0]!.stats, xp: 50 }, levelTarget: 50 };

    const r = computeGaugeLive(enc, dd, 'mangeoire', 0, false);
    expect(r.done).toBe(true);
    expect(r.cntDown).toBe(0);
  });

  it('done=true quand estLevel > levelTarget', () => {
    const enc = makeEnclos();
    const dd = { ...enc.dragodindes[0]!, stats: { ...enc.dragodindes[0]!.stats, xp: 100 }, levelTarget: 50 };

    const r = computeGaugeLive(enc, dd, 'mangeoire', 0, false);
    expect(r.done).toBe(true);
  });

  it('timer en cours avec levelTarget=10 : countdown reflète le target', () => {
    const now = Date.now();
    const enc = makeEnclos({
      timer: {
        running: true,
        startTime: now - 100_000, // 100 sec écoulées
        pausedAt: null,
        pausedMs: 0,
        snapGauges: { mangeoire: 50000 },
        snapStats: { '1': { serenite: 0, endurance: 0, maturite: 0, amour: 0, xp: 1 } },
        gaugeRecharges: {},
      },
    });

    const ddWithTarget = { ...enc.dragodindes[0]!, levelTarget: 10 };
    const ddNoTarget = { ...enc.dragodindes[0]!, levelTarget: null };

    const rWith = computeGaugeLive(enc, ddWithTarget, 'mangeoire', 100, true);
    const rNo = computeGaugeLive(enc, ddNoTarget, 'mangeoire', 100, true);

    // Avec target 10 : cntDown devrait être < 1000 (beaucoup moins que sans target)
    // Sans target (200) : cntDown devrait être > 30000
    expect(rWith.cntDown).toBeLessThan(rNo.cntDown);
    // Plus spécifiquement, avec target 10 après 100sec, le target devrait presque être atteint
    // xpNeeded = 809, et en 100sec (10 ticks) on gagne 10*20 = 200xp (tier 2)
    // xpRestante = 809 - 200 = 609
    expect(rWith.cntDown).toBeLessThan(500); // < 500 sec restantes
  });

  it('changer levelTarget pendant le timer met à jour le globalMax', () => {
    const now = Date.now();
    const enc = makeEnclos({
      timer: {
        running: true,
        startTime: now - 50_000, // 50 sec écoulées
        pausedAt: null,
        pausedMs: 0,
        snapGauges: { mangeoire: 50000 },
        snapStats: { '1': { serenite: 0, endurance: 0, maturite: 0, amour: 0, xp: 1 } },
        gaugeRecharges: {},
      },
    });

    // D'abord sans target (level 200 par défaut)
    enc.dragodindes[0]!.levelTarget = null;
    const state1 = enclosGlobalState(enc);

    // Puis avec target 10
    enc.dragodindes[0]!.levelTarget = 10;
    const state2 = enclosGlobalState(enc);

    // Le globalMax devrait diminuer drastiquement
    expect(state2.globalMax).toBeLessThan(state1.globalMax);
    expect(state2.globalMax).toBeLessThan(1000);
  });
});
