import { describe, it, expect, beforeEach } from 'vitest';
import { useManaStore, makeInitialElements } from '../stores/manaStore';
import { useCombatStore } from '../stores/combatStore';
import { getFloorMaxHP } from '../utils';
import { ErrorCode } from '../utils/result';

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

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

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,
  });
}

describe('ManaStore', () => {
  beforeEach(resetManaStore);

  describe('setRawMana', () => {
    it('should set raw mana to a positive value', () => {
      useManaStore.getState().setRawMana(50);
      expect(useManaStore.getState().rawMana).toBe(50);
    });

    it('should clamp negative values to 0', () => {
      useManaStore.getState().setRawMana(-10);
      expect(useManaStore.getState().rawMana).toBe(0);
    });
  });

  describe('addRawMana', () => {
    it('should add raw mana up to max', () => {
      useManaStore.getState().addRawMana(50, 200);
      expect(useManaStore.getState().rawMana).toBe(150);
    });

    it('should cap at max mana', () => {
      useManaStore.getState().addRawMana(200, 150);
      expect(useManaStore.getState().rawMana).toBe(150);
    });

    it('should increment totalManaGathered', () => {
      useManaStore.getState().addRawMana(50, 200);
      expect(useManaStore.getState().totalManaGathered).toBe(50);
    });
  });

  describe('spendRawMana', () => {
    it('should return true and deduct when sufficient', () => {
      const result = useManaStore.getState().spendRawMana(30);
      expect(result).toBe(true);
      expect(useManaStore.getState().rawMana).toBe(70);
    });

    it('should return false when insufficient', () => {
      const result = useManaStore.getState().spendRawMana(200);
      expect(result).toBe(false);
      expect(useManaStore.getState().rawMana).toBe(100);
    });
  });

  describe('gatherMana', () => {
    it('should add mana and track total gathered', () => {
      useManaStore.getState().gatherMana(5, 200);
      expect(useManaStore.getState().rawMana).toBe(105);
      expect(useManaStore.getState().totalManaGathered).toBe(5);
    });

    it('should cap at max mana', () => {
      useManaStore.getState().gatherMana(200, 150);
      expect(useManaStore.getState().rawMana).toBe(150);
    });
  });

  describe('unlockElement', () => {
    it('should unlock element and deduct cost', () => {
      const result = useManaStore.getState().unlockElement('fire', 50);
      expect(result.success).toBe(true);
      expect(useManaStore.getState().rawMana).toBe(50);
      expect(useManaStore.getState().elements.fire.unlocked).toBe(true);
    });

    it('should fail when already unlocked', () => {
      const result = useManaStore.getState().unlockElement('transference', 0);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INVALID_INPUT);
      }
    });

    it('should fail when insufficient mana', () => {
      const result = useManaStore.getState().unlockElement('fire', 200);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INSUFFICIENT_MANA);
      }
    });
  });

  describe('addElementMana / spendElementMana', () => {
    it('should add element mana up to max', () => {
      useManaStore.getState().addElementMana('transference', 10, 50);
      expect(useManaStore.getState().elements.transference.current).toBe(10);
    });

    it('should cap at max', () => {
      useManaStore.getState().addElementMana('transference', 100, 50);
      expect(useManaStore.getState().elements.transference.current).toBe(50);
    });

    it('should deduct element mana when sufficient', () => {
      useManaStore.getState().addElementMana('transference', 20, 50);
      const result = useManaStore.getState().spendElementMana('transference', 10);
      expect(result.success).toBe(true);
      expect(useManaStore.getState().elements.transference.current).toBe(10);
    });

    it('should fail when insufficient element mana', () => {
      const result = useManaStore.getState().spendElementMana('transference', 10);
      expect(result.success).toBe(false);
      if (!result.success) {
        expect(result.code).toBe(ErrorCode.INSUFFICIENT_MANA);
      }
    });
  });

  describe('resetMana', () => {
    it('should reset to initial state for new loop', () => {
      useManaStore.getState().resetMana({});
      expect(useManaStore.getState().rawMana).toBe(10);
      expect(useManaStore.getState().meditateTicks).toBe(0);
      expect(useManaStore.getState().totalManaGathered).toBe(0);
    });

    it('should apply prestige upgrades for starting mana', () => {
      useManaStore.getState().resetMana({ quickStart: 5 });
      expect(useManaStore.getState().rawMana).toBe(10 + 5 * 10);
    });
  });
});

// ═══════════════════════════════════════════════════════════════════════════════
// COMBAT STORE
// ═══════════════════════════════════════════════════════════════════════════════

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');
    });
  });
});
