// ─── Regression Test: Spell casting must stop when all enemies are dead ────────
// Issue #346: Spells continue casting and draining mana after all enemies are dead
//
// This test verifies that the primary spell casting while loop and the equipment
// spell casting while loop both respect the floorHP > 0 guard, matching the
// pattern already used by the melee, golem, and DoT loops.

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { processCombatTick } from '../stores/combat-actions';
import { useCombatStore } from '../stores/combatStore';
import { useManaStore, makeInitialElements } from '../stores/manaStore';
import { useGameStore } from '../stores/gameStore';
import { usePrestigeStore } from '../stores/prestigeStore';
import { useUIStore } from '../stores/uiStore';
import { useDisciplineStore } from '../stores/discipline-slice';
import { getFloorMaxHP } from '../utils';
import type { CombatTickResult } from '../stores/combat-actions';
import type { CombatState } from '../stores/combat-state.types';

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

function makeStoreInitialSpells() {
  return { manaBolt: { learned: true, level: 1, studyProgress: 0 } };
}

function makeDefaultRoom(enemyHP: number): CombatState['currentRoom'] {
  return {
    roomType: 'combat',
    enemies: [{
      id: 'enemy', name: 'Test Enemy', hp: enemyHP, maxHP: Math.max(enemyHP, 100),
      armor: 0, dodgeChance: 0, barrier: 0, element: 'fire', activeEffects: [], effectiveArmor: 0,
    }],
  };
}

function resetStores() {
  useUIStore.setState({ paused: false, gameOver: false, victory: false, logs: [] });
  useGameStore.setState({ day: 1, hour: 0, incursionStrength: 0, containmentWards: 0, initialized: true });
  useManaStore.setState({ rawMana: 1000, meditateTicks: 0, totalManaGathered: 0, elements: makeInitialElements(500, {}) });
  useCombatStore.setState({
    currentFloor: 1,
    floorHP: getFloorMaxHP(1),
    floorMaxHP: getFloorMaxHP(1),
    maxFloorReached: 1,
    activeSpell: 'manaBolt',
    currentAction: 'climb',
    castProgress: 0,
    spireMode: false,
    currentRoom: makeDefaultRoom(getFloorMaxHP(1)),
    clearedFloors: {},
    climbDirection: 'up',
    isDescending: false,
    startFloor: 1,
    exitFloor: 1,
    currentRoomIndex: 0,
    roomsPerFloor: 5,
    descentPeak: null,
    roomResetState: {},
    clearedRooms: {},
    isDescentComplete: false,
    golemancy: { activeGolems: [], lastSummonFloor: 0, golemDesigns: {}, golemLoadout: [] },
    equipmentSpellStates: [],
    comboHitCount: 0,
    floorHitCount: 0,
    weaponCastProgress: {},
    meleeSwordProgress: {},
    guardianShield: 0,
    guardianShieldMax: 0,
    guardianBarrier: 0,
    guardianBarrierMax: 0,
    spells: makeStoreInitialSpells(),
    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, processedPerks: [] });
}

function golemApplyDamageToRoom(dmg: number): { floorHP: number; floorMaxHP: number; roomCleared: boolean } {
  const cs = useCombatStore.getState();
  const newFloorHP = Math.max(0, cs.floorHP - dmg);
  return { floorHP: newFloorHP, floorMaxHP: cs.floorMaxHP, roomCleared: newFloorHP <= 0 };
}

function runCombatTick(
  rawMana: number,
  elements: Record<string, { current: number; max: number; unlocked: boolean }>,
  onDamageDealt?: (dmg: number) => { rawMana: number; elements: Record<string, { current: number; max: number; unlocked: boolean }>; modifiedDamage?: number },
): CombatTickResult {
  return processCombatTick(
    () => useCombatStore.getState(),
    (partial) => useCombatStore.setState(partial),
    rawMana,
    elements,
    1000, // maxMana
    1,    // attackSpeedMult
    vi.fn(), // onFloorCleared
    onDamageDealt || ((dmg) => ({ rawMana, elements, modifiedDamage: dmg })),
    [],   // signedPacts
    { activeGolems: [] },
    golemApplyDamageToRoom,
    (dmg: number) => dmg, // applyEnemyDefenses passthrough
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// REGRESSION: floorHP > 0 guard on spell casting loops
// ═══════════════════════════════════════════════════════════════════════════════

describe('Issue #346: Spell casting stops when enemies are dead', () => {
  beforeEach(resetStores);

  it('should NOT cast spells or drain mana when floorHP is 0 (all enemies dead)', () => {
    // Set floorHP to 0 (all enemies dead) and queue multiple casts
    useCombatStore.setState({
      floorHP: 0,
      floorMaxHP: 100,
      currentRoom: {
        roomType: 'combat',
        enemies: [{
          id: 'enemy', name: 'Dead Enemy', hp: 0, maxHP: 100,
          armor: 0, dodgeChance: 0, barrier: 0, element: 'fire', activeEffects: [], effectiveArmor: 0,
        }],
      },
      weaponCastProgress: { primary: 5 },
    });

    // Track how many times onDamageDealt is called (each call = one spell cast)
    let castCount = 0;
    const elements = makeInitialElements(500, {});
    const startMana = 1000;
    const result = runCombatTick(startMana, elements, (_dmg) => {
      castCount++;
      return { rawMana: startMana, elements, modifiedDamage: _dmg };
    });

    // No spells should have fired — onDamageDealt should never be called
    expect(castCount).toBe(0);
    // floorHP should remain 0
    expect(result.floorHP).toBe(0);
  });

  it('should NOT cast spells or drain mana when room has empty enemies array', () => {
    useCombatStore.setState({
      floorHP: 0,
      floorMaxHP: 100,
      currentRoom: { roomType: 'combat', enemies: [] },
      weaponCastProgress: { primary: 3 },
    });

    let castCount = 0;
    const elements = makeInitialElements(500, {});
    const startMana = 1000;
    runCombatTick(startMana, elements, (_dmg) => {
      castCount++;
      return { rawMana: startMana, elements, modifiedDamage: _dmg };
    });

    expect(castCount).toBe(0);
  });

  it('should cast spells normally when floorHP > 0 (enemies alive)', () => {
    const floorHP = getFloorMaxHP(1);
    useCombatStore.setState({
      floorHP,
      floorMaxHP: floorHP,
      currentRoom: makeDefaultRoom(floorHP),
      weaponCastProgress: { primary: 2 },
    });

    let castCount = 0;
    const elements = makeInitialElements(500, {});
    const startMana = 1000;
    const result = runCombatTick(startMana, elements, (_dmg) => {
      castCount++;
      return { rawMana: startMana, elements, modifiedDamage: _dmg };
    });

    // Spells SHOULD have fired when enemies are alive
    expect(castCount).toBeGreaterThan(0);
    // floorHP should have decreased (damage was dealt)
    expect(result.floorHP).toBeLessThan(floorHP);
  });

  it('should stop casting mid-loop when floorHP reaches 0 during the tick', () => {
    // First cast will kill the enemy (floorHP → 0), remaining queued casts should NOT fire
    useCombatStore.setState({
      floorHP: 1,
      floorMaxHP: 100,
      currentRoom: makeDefaultRoom(1),
      weaponCastProgress: { primary: 5 },
    });

    let castCount = 0;
    const elements = makeInitialElements(500, {});
    const startMana = 1000;
    runCombatTick(startMana, elements, (_dmg) => {
      castCount++;
      return { rawMana: startMana, elements, modifiedDamage: _dmg };
    });

    // Only 1 cast should fire (killing the enemy), not all 5
    expect(castCount).toBe(1);
  });

  it('should NOT drain mana from equipment spells when floorHP is 0', () => {
    useCombatStore.setState({
      floorHP: 0,
      floorMaxHP: 100,
      currentRoom: { roomType: 'combat', enemies: [] },
      weaponCastProgress: { primary: 0 },
      equipmentSpellStates: [
        { spellId: 'manaBolt', castProgress: 3, sourceInstanceId: 'test-staff' },
      ],
    });

    let equipmentCastCount = 0;
    const elements = makeInitialElements(500, {});
    const startMana = 1000;
    runCombatTick(startMana, elements, (dmg) => {
      // Track if equipment spells fire by checking if cast progress was consumed
      const cs = useCombatStore.getState();
      if (cs.equipmentSpellStates.length > 0 && cs.equipmentSpellStates[0].castProgress < 3) {
        equipmentCastCount++;
      }
      return { rawMana: startMana, elements, modifiedDamage: dmg };
    });

    // Equipment spells should not have changed cast progress
    const finalState = useCombatStore.getState();
    expect(finalState.equipmentSpellStates[0].castProgress).toBe(3);
  });
});
