import { describe, it, expect, beforeEach } from 'vitest';
import { useCombatStore } from '../stores/combatStore';
import { useManaStore } from '../stores/manaStore';
import { usePrestigeStore } from '../stores/prestigeStore';
import { getGuardianForFloor } from '../data/guardian-encounters';
import { getFloorMaxHP } from '../utils';
import { ErrorCode } from '../utils/result';

function resetCombatStore() {
  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,
  });
}

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

describe('CombatStore', () => {
  beforeEach(resetCombatStore);

  describe('setCurrentFloor', () => {
    it('should set floor and update HP', () => {
      useCombatStore.getState().setCurrentFloor(5);
      expect(useCombatStore.getState().currentFloor).toBe(5);
      expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(5));
      expect(useCombatStore.getState().floorMaxHP).toBe(getFloorMaxHP(5));
    });
  });

  describe('advanceFloor', () => {
    it('should increment floor and update HP', () => {
      useCombatStore.getState().advanceFloor();
      expect(useCombatStore.getState().currentFloor).toBe(2);
      expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(2));
    });

    it('should cap at floor 100', () => {
      useCombatStore.setState({ currentFloor: 100 });
      useCombatStore.getState().advanceFloor();
      expect(useCombatStore.getState().currentFloor).toBe(100);
    });

    it('should update maxFloorReached', () => {
      useCombatStore.getState().advanceFloor();
      expect(useCombatStore.getState().maxFloorReached).toBe(2);
    });

    it('should reset cast progress', () => {
      useCombatStore.setState({ castProgress: 0.5 });
      useCombatStore.getState().advanceFloor();
      expect(useCombatStore.getState().castProgress).toBe(0);
    });
  });

  describe('setFloorHP', () => {
    it('should set floor HP', () => {
      useCombatStore.getState().setFloorHP(50);
      expect(useCombatStore.getState().floorHP).toBe(50);
    });

    it('should clamp negative to 0', () => {
      useCombatStore.getState().setFloorHP(-10);
      expect(useCombatStore.getState().floorHP).toBe(0);
    });
  });

  describe('setMaxFloorReached', () => {
    it('should update max floor reached', () => {
      useCombatStore.getState().setMaxFloorReached(10);
      expect(useCombatStore.getState().maxFloorReached).toBe(10);
    });

    it('should only increase, never decrease', () => {
      useCombatStore.setState({ maxFloorReached: 10 });
      useCombatStore.getState().setMaxFloorReached(5);
      expect(useCombatStore.getState().maxFloorReached).toBe(10);
    });
  });

  describe('setAction / setSpell', () => {
    it('should set current action', () => {
      useCombatStore.getState().setAction('climb');
      expect(useCombatStore.getState().currentAction).toBe('climb');
    });

    it('should set active spell when learned', () => {
      useCombatStore.getState().setSpell('manaBolt');
      expect(useCombatStore.getState().activeSpell).toBe('manaBolt');
    });

    it('should not set spell when not learned', () => {
      useCombatStore.getState().setSpell('fireball');
      expect(useCombatStore.getState().activeSpell).toBe('manaBolt');
    });
  });

  describe('learnSpell', () => {
    it('should add a new learned spell', () => {
      useCombatStore.getState().learnSpell('fireball');
      expect(useCombatStore.getState().spells.fireball.learned).toBe(true);
    });
  });

  describe('resetCombat', () => {
    it('should reset to starting floor', () => {
      useCombatStore.setState({ currentFloor: 50, maxFloorReached: 50 });
      useCombatStore.getState().resetCombat(1);
      expect(useCombatStore.getState().currentFloor).toBe(1);
      expect(useCombatStore.getState().maxFloorReached).toBe(1);
    });
  });

  describe('climbDownFloor', () => {
    it('should decrement floor', () => {
      useCombatStore.setState({ currentFloor: 5 });
      useCombatStore.getState().climbDownFloor();
      expect(useCombatStore.getState().currentFloor).toBe(4);
    });

    it('should not go below floor 1', () => {
      useCombatStore.setState({ currentFloor: 1 });
      useCombatStore.getState().climbDownFloor();
      expect(useCombatStore.getState().currentFloor).toBe(1);
    });
  });

  describe('exitSpireMode', () => {
    it('should reset spire state', () => {
      useCombatStore.setState({ spireMode: true, climbDirection: 'up', currentAction: 'climb' });
      useCombatStore.getState().exitSpireMode();
      expect(useCombatStore.getState().spireMode).toBe(false);
      expect(useCombatStore.getState().climbDirection).toBeNull();
      expect(useCombatStore.getState().currentAction).toBe('meditate');
    });
  });
});

describe('PrestigeStore', () => {
  beforeEach(resetPrestigeStore);

  describe('doPrestige', () => {
    it('should purchase upgrade when affordable', () => {
      usePrestigeStore.setState({ insight: 2000 });
      const result = usePrestigeStore.getState().doPrestige('insightAmp');
      expect(result.success).toBe(true);
      expect(usePrestigeStore.getState().prestigeUpgrades.insightAmp).toBe(1);
      expect(usePrestigeStore.getState().insight).toBeLessThan(2000);
    });

    it('should return false when cannot afford', () => {
      usePrestigeStore.setState({ insight: 0 });
      const result = usePrestigeStore.getState().doPrestige('insightAmp');
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INSUFFICIENT_INSIGHT);
      }
    });

    it('should fail for invalid upgrade id', () => {
      const result = usePrestigeStore.getState().doPrestige('nonexistent');
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INVALID_PRESTIGE_ID);
      }
    });

  });

  describe('defeatGuardian / signedPacts', () => {
    it('should add defeated guardian', () => {
      usePrestigeStore.getState().defeatGuardian(10);
      expect(usePrestigeStore.getState().defeatedGuardians).toContain(10);
    });

    it('should not duplicate defeated guardian', () => {
      usePrestigeStore.getState().defeatGuardian(10);
      usePrestigeStore.getState().defeatGuardian(10);
      expect(usePrestigeStore.getState().defeatedGuardians.filter(f => f === 10).length).toBe(1);
    });

    it('should not defeat already signed guardian', () => {
      usePrestigeStore.setState({ signedPacts: [10] });
      usePrestigeStore.getState().defeatGuardian(10);
      expect(usePrestigeStore.getState().defeatedGuardians).not.toContain(10);
    });

    it('should add signed pact', () => {
      usePrestigeStore.getState().addSignedPact(10);
      expect(usePrestigeStore.getState().signedPacts).toContain(10);
    });

    it('should remove pact', () => {
      usePrestigeStore.setState({ signedPacts: [10, 20] });
      usePrestigeStore.getState().removePact(10);
      expect(usePrestigeStore.getState().signedPacts).not.toContain(10);
      expect(usePrestigeStore.getState().signedPacts).toContain(20);
    });
  });

  describe('startPactRitual', () => {
    it('should start ritual when conditions met', () => {
      useManaStore.setState({ rawMana: 100000 });
      usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [], insight: 10000 });
      const result = usePrestigeStore.getState().startPactRitual(10, 100000);
      expect(result.success).toBe(true);
      expect(usePrestigeStore.getState().pactRitualFloor).toBe(10);
    });

    it('should return false when guardian not defeated', () => {
      const result = usePrestigeStore.getState().startPactRitual(10, 10000);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.GUARDIAN_NOT_DEFEATED);
      }
    });

    it('should fail when already signed', () => {
      usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [10] });
      const result = usePrestigeStore.getState().startPactRitual(10, 10000);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.PACT_ALREADY_SIGNED);
      }
    });

    it('should fail when pact slots full', () => {
      usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [20], pactSlots: 1 });
      const result = usePrestigeStore.getState().startPactRitual(10, 10000);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.PACT_SLOTS_FULL);
      }
    });

    it('should fail when insufficient mana', () => {
      usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [] });
      const result = usePrestigeStore.getState().startPactRitual(10, 0);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INSUFFICIENT_MANA);
      }
    });

    it('should deduct raw mana cost when starting ritual', () => {
      // Get the guardian to know the exact pact cost
      const guardian = getGuardianForFloor(10);
      expect(guardian).not.toBeNull();
      const cost = guardian!.pactCost;

      // Set up: give mana store enough raw mana
      useManaStore.setState({ rawMana: cost + 500 });
      usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [], pactSlots: 1 });

      const manaBefore = useManaStore.getState().rawMana;
      const result = usePrestigeStore.getState().startPactRitual(10, manaBefore);

      expect(result.success).toBe(true);
      expect(usePrestigeStore.getState().pactRitualFloor).toBe(10);
      // Raw mana cost must be deducted
      expect(useManaStore.getState().rawMana).toBe(manaBefore - cost);
    });

    it('should NOT deduct raw mana when ritual fails (e.g. not defeated)', () => {
      useManaStore.setState({ rawMana: 100000 });
      usePrestigeStore.setState({ defeatedGuardians: [], signedPacts: [], pactSlots: 1 });

      const manaBefore = useManaStore.getState().rawMana;
      const result = usePrestigeStore.getState().startPactRitual(10, manaBefore);

      expect(result.success).toBe(false);
      // Mana must NOT be deducted on failure
      expect(useManaStore.getState().rawMana).toBe(manaBefore);
    });
  });

  describe('cancelPactRitual', () => {
    it('should cancel active ritual', () => {
      usePrestigeStore.setState({ pactRitualFloor: 10, pactRitualProgress: 1 });
      usePrestigeStore.getState().cancelPactRitual();
      expect(usePrestigeStore.getState().pactRitualFloor).toBeNull();
      expect(usePrestigeStore.getState().pactRitualProgress).toBe(0);
    });
  });

  describe('startNewLoop', () => {
    it('should increment loop count and add insight', () => {
      usePrestigeStore.setState({ insight: 100, totalInsight: 100 });
      usePrestigeStore.getState().startNewLoop(50);
      expect(usePrestigeStore.getState().loopCount).toBe(1);
      expect(usePrestigeStore.getState().insight).toBe(150);
      expect(usePrestigeStore.getState().totalInsight).toBe(150);
    });

    it('should reset loop-specific state', () => {
      usePrestigeStore.setState({
        defeatedGuardians: [10],
        signedPacts: [20],
        pactRitualFloor: 10,
        pactRitualProgress: 5,
      });
      usePrestigeStore.getState().startNewLoop(0);
      expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]);
      expect(usePrestigeStore.getState().signedPacts).toEqual([]);
      expect(usePrestigeStore.getState().pactRitualFloor).toBeNull();
    });
  });

  describe('resetPrestigeForNewLoop', () => {
    it('should preserve insight and upgrades, reset loop state', () => {
      usePrestigeStore.getState().resetPrestigeForNewLoop(200, { insightAmp: 2 });
      expect(usePrestigeStore.getState().insight).toBe(200);
      expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({ insightAmp: 2 });

      expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]);
    });
  });

  describe('resetPrestige', () => {
    it('should reset everything to initial state', () => {
      usePrestigeStore.setState({ insight: 1000, loopCount: 5, prestigeUpgrades: { insightAmp: 3 } });
      usePrestigeStore.getState().resetPrestige();
      expect(usePrestigeStore.getState().insight).toBe(0);
      expect(usePrestigeStore.getState().loopCount).toBe(0);
      expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({});
    });
  });
});
