import { describe, it, expect, beforeEach } from 'vitest';
import { useGameStore } from '../stores/gameStore';
import { useManaStore, makeInitialElements } from '../stores/manaStore';
import { useCombatStore } from '../stores/combatStore';
import { usePrestigeStore } from '../stores/prestigeStore';
import { useUIStore } from '../stores/uiStore';
import { useDisciplineStore } from '../stores/discipline-slice';
import { HOURS_PER_TICK, MAX_DAY, INCURSION_START_DAY } from '../constants';
import { getFloorMaxHP } from '../utils';

// ─── Helpers ──────────────────────────────────────────────────────────────────

function resetAllStores() {
  useUIStore.setState({
    paused: false,
    gameOver: false,
    victory: false,
    logs: [],
  });

  useGameStore.setState({
    day: 1,
    hour: 0,
    incursionStrength: 0,
    containmentWards: 0,
    initialized: true,
  });

  useManaStore.setState({
    rawMana: 100,
    meditateTicks: 0,
    totalManaGathered: 0,
    elements: makeInitialElements(50, {}),
  });

  useCombatStore.setState({
    currentFloor: 1,
    floorHP: getFloorMaxHP(1),
    floorMaxHP: getFloorMaxHP(1),
    maxFloorReached: 1,
    activeSpell: 'manaBolt',
    currentAction: 'meditate',
    castProgress: 0,
    spireMode: false,
    currentRoom: { roomType: 'combat', enemies: [] },
    clearedFloors: {},
    climbDirection: null,
    isDescending: false,
    golemancy: { activeGolems: [], lastSummonFloor: 0, golemDesigns: {}, golemLoadout: [] },
    equipmentSpellStates: [],
    comboHitCount: 0,
    floorHitCount: 0,
    spells: { manaBolt: { learned: true, level: 1, studyProgress: 0 } },
    activityLog: [],
    achievements: { unlocked: [], progress: {} },
    totalSpellsCast: 0,
    totalDamageDealt: 0,
    totalCraftsCompleted: 0,
  });

  usePrestigeStore.setState({
    loopCount: 0,
    insight: 0,
    totalInsight: 0,
    loopInsight: 0,
    prestigeUpgrades: {},
    pactSlots: 1,
    defeatedGuardians: [],
    signedPacts: [],
    signedPactDetails: {},
    pactRitualFloor: null,
    pactRitualProgress: 0,
  });

  useDisciplineStore.setState({
    disciplines: {},
    activeIds: [],
    concurrentLimit: 1,
    totalXP: 0,
  });
}

// ═══════════════════════════════════════════════════════════════════════════════
// TICK INTEGRATION TESTS
// ═══════════════════════════════════════════════════════════════════════════════

describe('Tick Integration', () => {
  beforeEach(resetAllStores);

  describe('time progression', () => {
    it('should advance hour by HOURS_PER_TICK', () => {
      useGameStore.getState().tick();
      expect(useGameStore.getState().hour).toBeCloseTo(HOURS_PER_TICK, 5);
    });

    it('should advance day when hour wraps past 24', () => {
      // Set hour close to 24
      useGameStore.setState({ hour: 23.99 });
      useGameStore.getState().tick();
      expect(useGameStore.getState().day).toBe(2);
      expect(useGameStore.getState().hour).toBeCloseTo(23.99 + HOURS_PER_TICK - 24, 5);
    });

    it('should advance multiple hours over many ticks', () => {
      for (let i = 0; i < 100; i++) {
        useGameStore.getState().tick();
      }
      const expectedHour = (100 * HOURS_PER_TICK) % 24;
      const expectedDay = 1 + Math.floor((100 * HOURS_PER_TICK) / 24);
      expect(useGameStore.getState().day).toBe(expectedDay);
      expect(useGameStore.getState().hour).toBeCloseTo(expectedHour, 5);
    });
  });

  describe('mana regeneration', () => {
    it('should increase raw mana on tick (base regen)', () => {
      useManaStore.setState({ rawMana: 50 });
      useGameStore.getState().tick();
      expect(useManaStore.getState().rawMana).toBeGreaterThan(50);
    });

    it('should cap raw mana at max', () => {
      useManaStore.setState({ rawMana: 9999 });
      useGameStore.getState().tick();
      // Max mana with no skills/upgrades is 100
      expect(useManaStore.getState().rawMana).toBeLessThanOrEqual(100);
    });

    it('should increase totalManaGathered from meditation regen', () => {
      // Passive meditation regen should count toward totalManaGathered
      useManaStore.setState({ rawMana: 50, totalManaGathered: 5 });
      useGameStore.getState().tick();
      expect(useManaStore.getState().totalManaGathered).toBeGreaterThan(5);
    });

    it('should not count regen toward totalManaGathered when at cap', () => {
      // Regen that would exceed max mana should not count toward totalManaGathered
      useManaStore.setState({ rawMana: 100, totalManaGathered: 50 });
      useGameStore.getState().tick();
      // When rawMana equals or exceeds maxMana, no regen is added to totalManaGathered
      // Use toBeCloseTo with tolerance for floating point drift from base regen calculations
      expect(useManaStore.getState().totalManaGathered).toBeLessThanOrEqual(50.05);
    });
  });

  describe('incursion penalty', () => {
    it('should have no incursion before INCURSION_START_DAY', () => {
      useGameStore.setState({ day: INCURSION_START_DAY - 1, hour: 23 });
      useGameStore.getState().tick();
      expect(useGameStore.getState().incursionStrength).toBe(0);
    });

    it('should apply incursion after INCURSION_START_DAY', () => {
      useGameStore.setState({ day: INCURSION_START_DAY, hour: 1 });
      useGameStore.getState().tick();
      expect(useGameStore.getState().incursionStrength).toBeGreaterThan(0);
    });

    it('should reduce mana regen during incursion', () => {
      // No incursion: day 1
      resetAllStores();
      useGameStore.setState({ day: 1, hour: 0 });
      useManaStore.setState({ rawMana: 50 });
      useGameStore.getState().tick();
      const regenNoIncursion = useManaStore.getState().rawMana - 50;

      // With incursion: day 25
      resetAllStores();
      useGameStore.setState({ day: 25, hour: 12 });
      useManaStore.setState({ rawMana: 50 });
      useGameStore.getState().tick();
      const regenWithIncursion = useManaStore.getState().rawMana - 50;

      expect(regenWithIncursion).toBeLessThan(regenNoIncursion);
    });
  });

  describe('meditation', () => {
    it('should increment meditateTicks when action is meditate', () => {
      useCombatStore.setState({ currentAction: 'meditate' });
      useGameStore.getState().tick();
      expect(useManaStore.getState().meditateTicks).toBe(1);
    });

    it('should reset meditateTicks when action changes', () => {
      useCombatStore.setState({ currentAction: 'meditate' });
      useGameStore.getState().tick();
      useGameStore.getState().tick();
      expect(useManaStore.getState().meditateTicks).toBe(2);

      useCombatStore.setState({ currentAction: 'climb' });
      useGameStore.getState().tick();
      expect(useManaStore.getState().meditateTicks).toBe(0);
    });

    it('should boost regen with meditation', () => {
      // Without meditation
      resetAllStores();
      useCombatStore.setState({ currentAction: 'climb' });
      useManaStore.setState({ rawMana: 50, meditateTicks: 100 });
      useGameStore.getState().tick();
      const regenNoMeditate = useManaStore.getState().rawMana - 50;

      // With meditation (same ticks)
      resetAllStores();
      useCombatStore.setState({ currentAction: 'meditate' });
      useManaStore.setState({ rawMana: 50, meditateTicks: 100 });
      useGameStore.getState().tick();
      const regenMeditate = useManaStore.getState().rawMana - 50;

      expect(regenMeditate).toBeGreaterThan(regenNoMeditate);
    });
  });

  describe('paused / game over', () => {
    it('should not advance time when paused', () => {
      useUIStore.setState({ paused: true });
      const before = useGameStore.getState().hour;
      useGameStore.getState().tick();
      expect(useGameStore.getState().hour).toBe(before);
    });

    it('should not advance time when game over', () => {
      useUIStore.setState({ gameOver: true });
      const before = useGameStore.getState().hour;
      useGameStore.getState().tick();
      expect(useGameStore.getState().hour).toBe(before);
    });

    it('should not regenerate mana when paused', () => {
      useUIStore.setState({ paused: true });
      useManaStore.setState({ rawMana: 50 });
      useGameStore.getState().tick();
      expect(useManaStore.getState().rawMana).toBe(50);
    });
  });

  describe('loop end', () => {
    it('should trigger game over when day > MAX_DAY', () => {
      useGameStore.setState({ day: MAX_DAY, hour: 23.99 });
      useGameStore.getState().tick();
      expect(useUIStore.getState().gameOver).toBe(true);
    });

    it('should set loopInsight when loop ends', () => {
      useGameStore.setState({ day: MAX_DAY, hour: 23.99 });
      useGameStore.getState().tick();
      expect(usePrestigeStore.getState().loopInsight).toBeGreaterThan(0);
    });

    it('should not be victory when loop ends normally', () => {
      useGameStore.setState({ day: MAX_DAY, hour: 23.99 });
      useGameStore.getState().tick();
      expect(useUIStore.getState().victory).toBe(false);
    });
  });

  describe('victory condition', () => {
    it('should trigger victory when floor 100 reached with signed pact', () => {
      useCombatStore.setState({ maxFloorReached: 100 });
      usePrestigeStore.setState({ signedPacts: [100] });
      useGameStore.getState().tick();
      expect(useUIStore.getState().gameOver).toBe(true);
      expect(useUIStore.getState().victory).toBe(true);
    });

    it('should not trigger victory without signed pact', () => {
      useCombatStore.setState({ maxFloorReached: 100 });
      usePrestigeStore.setState({ signedPacts: [] });
      useGameStore.getState().tick();
      expect(useUIStore.getState().victory).toBe(false);
    });

    it('should not trigger victory without floor 100', () => {
      useCombatStore.setState({ maxFloorReached: 99 });
      usePrestigeStore.setState({ signedPacts: [100] });
      useGameStore.getState().tick();
      expect(useUIStore.getState().victory).toBe(false);
    });
  });

  describe('pact ritual progress', () => {
    it('should advance pact ritual progress on tick', () => {
      usePrestigeStore.setState({ pactRitualFloor: 10, pactRitualProgress: 0 });
      useGameStore.getState().tick();
      expect(usePrestigeStore.getState().pactRitualProgress).toBeGreaterThan(0);
    });

    it('should not advance pact ritual when not active', () => {
      usePrestigeStore.setState({ pactRitualFloor: null });
      useGameStore.getState().tick();
      expect(usePrestigeStore.getState().pactRitualProgress).toBe(0);
    });
  });

  describe('multiple ticks', () => {
    it('should accumulate mana over multiple ticks', () => {
      useManaStore.setState({ rawMana: 10 });
      for (let i = 0; i < 50; i++) {
        useGameStore.getState().tick();
      }
      expect(useManaStore.getState().rawMana).toBeGreaterThan(10);
    });

    it('should advance time correctly over many ticks', () => {
      const numTicks = 625; // 625 * 0.04 = 25 hours = 1 day + 1 hour
      for (let i = 0; i < numTicks; i++) {
        useGameStore.getState().tick();
      }
      expect(useGameStore.getState().day).toBe(2);
      expect(useGameStore.getState().hour).toBeCloseTo(1, 5);
    });

    it('should accumulate totalManaGathered from meditation over ticks', () => {
      useManaStore.setState({ rawMana: 10, totalManaGathered: 42 });
      for (let i = 0; i < 10; i++) {
        useGameStore.getState().tick();
      }
      // Passive meditation regen should now count toward totalManaGathered
      expect(useManaStore.getState().totalManaGathered).toBeGreaterThan(42);
    });
  });

  describe('incursion strength progression', () => {
    it('should be near 0 at start of incursion day', () => {
      // At INCURSION_START_DAY hour 0, incursion is 0, but tick advances hour first
      // so after tick, hour=0.04 and incursion is very small but > 0
      useGameStore.setState({ day: INCURSION_START_DAY, hour: 0 });
      useGameStore.getState().tick();
      // After tick, hour advanced by HOURS_PER_TICK, so incursion is small
      expect(useGameStore.getState().incursionStrength).toBeGreaterThanOrEqual(0);
      expect(useGameStore.getState().incursionStrength).toBeLessThan(0.01);
    });

    it('should increase over the course of the incursion', () => {
      // Early incursion
      resetAllStores();
      useGameStore.setState({ day: INCURSION_START_DAY, hour: 1 });
      useGameStore.getState().tick();
      const early = useGameStore.getState().incursionStrength;

      // Late incursion
      resetAllStores();
      useGameStore.setState({ day: MAX_DAY, hour: 23 });
      useGameStore.getState().tick();
      const late = useGameStore.getState().incursionStrength;

      expect(late).toBeGreaterThan(early);
    });

    it('should cap at 0.95', () => {
      useGameStore.setState({ day: MAX_DAY, hour: 23.99 });
      useGameStore.getState().tick();
      expect(useGameStore.getState().incursionStrength).toBeLessThanOrEqual(0.95);
    });
  });
});
