import { describe, it, expect } from 'vitest';
import {
  getRoomsForFloor,
  generateSpireRoomType,
  generateSpireFloorState,
  getSpireEnemyArmor,
  getSpireEnemyBarrier,
  calcInsight,
  getSpireRoomTypeDisplay,
  SPIRE_CONFIG,
} from '../utils/spire-utils';
import { isGuardianFloor, getExtendedGuardian, getGuardianHP, generateGuardianName, generateComboGuardianName, ALL_GUARDIAN_FLOORS } from '../data/guardian-encounters';

// ─── Spire Utils ─────────────────────────────────────────────────────────────

describe('getRoomsForFloor', () => {
  it('should return at least minRoomsPerFloor for non-guardian floors', () => {
    for (let floor = 1; floor <= 50; floor++) {
      if (floor % 10 === 0) continue; // Skip guardian floors
      const rooms = getRoomsForFloor(floor);
      expect(rooms).toBeGreaterThanOrEqual(SPIRE_CONFIG.minRoomsPerFloor);
      expect(rooms).toBeLessThanOrEqual(SPIRE_CONFIG.maxRoomsPerFloor + 5);
    }
  });

  it('should return 1 room for guardian floors', () => {
    expect(getRoomsForFloor(10)).toBe(1);
    expect(getRoomsForFloor(20)).toBe(1);
    expect(getRoomsForFloor(100)).toBe(1);
  });

  it('should return more rooms for higher non-guardian floors', () => {
    const lowFloor = getRoomsForFloor(3);
    const highFloor = getRoomsForFloor(79);
    expect(highFloor).toBeGreaterThanOrEqual(lowFloor);
  });
});

describe('generateSpireRoomType', () => {
  it('should return guardian for last room on guardian floors', () => {
    const totalRooms = getRoomsForFloor(10);
    const roomType = generateSpireRoomType(10, totalRooms - 1, totalRooms);
    expect(roomType).toBe('guardian');
  });

  it('should return combat for first room on non-guardian floors', () => {
    for (const floor of [1, 5, 15, 25]) {
      const roomType = generateSpireRoomType(floor, 0, 10);
      // First room may be combat, swarm, or speed depending on random
      expect(['combat', 'swarm', 'speed']).toContain(roomType);
    }
  });

  it('should return combat for first room on guardian floors (not last room)', () => {
    // Floor 50 is a guardian floor, but first room should still be combat
    const roomType = generateSpireRoomType(50, 0, 10);
    // First room on guardian floor should not be 'guardian' (last room) and may be combat or swarm depending on random
    expect(['combat', 'swarm']).toContain(roomType);
  });

  it('should return valid room types', () => {
    const validTypes = ['combat', 'swarm', 'speed', 'guardian', 'puzzle', 'recovery', 'library', 'treasure'];
    for (let i = 0; i < 100; i++) {
      const roomType = generateSpireRoomType(25, 3, 10);
      expect(validTypes).toContain(roomType);
    }
  });
});

describe('generateSpireFloorState', () => {
  it('should generate guardian floor for floor 10', () => {
    const state = generateSpireFloorState(10, 0, 1);
    expect(state.roomType).toBe('guardian');
    expect(state.enemies.length).toBe(1);
    expect(state.enemies[0].name).toBeTruthy();
  });

  it('should generate combat floor with enemies', () => {
    const state = generateSpireFloorState(5, 0, 8);
    expect(state.enemies.length).toBeGreaterThan(0);
    expect(state.enemies[0].hp).toBeGreaterThan(0);
    expect(state.enemies[0].maxHP).toBeGreaterThan(0);
  });

  it('should generate swarm floor with multiple enemies', () => {
    // Force swarm by using a non-special room index
    const state = generateSpireFloorState(20, 1, 10);
    // Room type depends on random, but enemies should be valid
    if (state.roomType === 'swarm') {
      expect(state.enemies.length).toBeGreaterThanOrEqual(3);
    }
  });
});

describe('getSpireEnemyArmor', () => {
  it('should return 0 for floors below 10', () => {
    for (let i = 0; i < 20; i++) {
      const armor = getSpireEnemyArmor(5);
      expect(armor).toBeGreaterThanOrEqual(0);
      expect(armor).toBeLessThanOrEqual(0.3);
    }
  });

  it('should return values between 0 and 0.3', () => {
    for (let floor = 1; floor <= 100; floor++) {
      const armor = getSpireEnemyArmor(floor);
      expect(armor).toBeGreaterThanOrEqual(0);
      expect(armor).toBeLessThanOrEqual(0.3);
    }
  });
});

describe('getSpireEnemyBarrier', () => {
  it('should return 0 for floors below 15', () => {
    for (let i = 0; i < 10; i++) {
      const barrier = getSpireEnemyBarrier(10, 'fire');
      expect(barrier).toBeGreaterThanOrEqual(0);
    }
  });

  it('should return values between 0 and 0.3', () => {
    for (let floor = 15; floor <= 100; floor++) {
      const barrier = getSpireEnemyBarrier(floor, 'fire');
      expect(barrier).toBeGreaterThanOrEqual(0);
      // Use toBeLessThan with a small tolerance for floating point precision
      expect(barrier).toBeLessThanOrEqual(0.3000000001);
    }
  });
});

describe('calcInsight', () => {
  it('should return positive insight for any floor', () => {
    expect(calcInsight(1, false)).toBeGreaterThan(0);
    expect(calcInsight(10, true)).toBeGreaterThan(0);
    expect(calcInsight(50, false)).toBeGreaterThan(0);
  });

  it('should give more insight for guardian floors', () => {
    const normal = calcInsight(10, false);
    const guardian = calcInsight(10, true);
    expect(guardian).toBeGreaterThan(normal);
  });

  it('should scale with floor number', () => {
    const low = calcInsight(5, false);
    const high = calcInsight(50, false);
    expect(high).toBeGreaterThan(low);
  });
});

describe('getSpireRoomTypeDisplay', () => {
  it('should return display info for all room types', () => {
    const types = ['combat', 'swarm', 'speed', 'guardian', 'puzzle', 'recovery', 'library', 'treasure'];
    for (const type of types) {
      const display = getSpireRoomTypeDisplay(type as any);
      expect(display.label).toBeTruthy();
      expect(display.icon).toBeTruthy();
      expect(display.color).toBeTruthy();
    }
  });

  it('should return unknown for invalid room type', () => {
    const display = getSpireRoomTypeDisplay('invalid' as any);
    expect(display.label).toBe('Unknown');
  });
});

// ─── Guardian Encounters ─────────────────────────────────────────────────────

describe('isGuardianFloor', () => {
  it('should return true for every 10th floor', () => {
    expect(isGuardianFloor(10)).toBe(true);
    expect(isGuardianFloor(20)).toBe(true);
    expect(isGuardianFloor(100)).toBe(true);
    expect(isGuardianFloor(150)).toBe(true);
  });

  it('should return false for non-10th floors', () => {
    expect(isGuardianFloor(1)).toBe(false);
    expect(isGuardianFloor(15)).toBe(false);
    expect(isGuardianFloor(99)).toBe(false);
  });
});

describe('getExtendedGuardian', () => {
  it('should return compound guardians for floors 90, 110', () => {
    const g90 = getExtendedGuardian(90);
    expect(g90).not.toBeNull();
    expect(g90!.element).toBe('metal');
    expect(g90!.name).toBeTruthy();

    const g110 = getExtendedGuardian(110);
    expect(g110).not.toBeNull();
    expect(g110!.element).toBe('lightning');
  });

  it('should return exotic guardians for floors 120, 130, 140', () => {
    const g120 = getExtendedGuardian(120);
    expect(g120).not.toBeNull();
    expect(g120!.element).toBe('crystal');

    const g130 = getExtendedGuardian(130);
    expect(g130).not.toBeNull();
    expect(g130!.element).toBe('stellar');

    const g140 = getExtendedGuardian(140);
    expect(g140).not.toBeNull();
    expect(g140!.element).toBe('void');
  });

  it('should return combo guardians for floors 150+', () => {
    const g150 = getExtendedGuardian(150);
    expect(g150).not.toBeNull();
    expect(g150!.element).toContain('+');
  });

  it('should return null for non-guardian floors', () => {
    expect(getExtendedGuardian(1)).toBeNull();
    expect(getExtendedGuardian(15)).toBeNull();
    expect(getExtendedGuardian(95)).toBeNull();
  });
});

describe('getGuardianHP', () => {
  it('should return positive HP', () => {
    expect(getGuardianHP(10)).toBeGreaterThan(0);
    expect(getGuardianHP(100)).toBeGreaterThan(0);
    expect(getGuardianHP(200)).toBeGreaterThan(0);
  });

  it('should scale with floor', () => {
    const low = getGuardianHP(10);
    const high = getGuardianHP(100);
    expect(high).toBeGreaterThan(low);
  });
});

describe('generateGuardianName', () => {
  it('should generate non-empty names', () => {
    for (const element of ['fire', 'water', 'air', 'earth', 'light', 'dark', 'death']) {
      const name = generateGuardianName(element);
      expect(name).toBeTruthy();
      expect(name.length).toBeGreaterThan(0);
    }
  });

  it('should include a title', () => {
    const name = generateGuardianName('fire');
    expect(name).toContain(' the ');
  });
});

describe('generateComboGuardianName', () => {
  it('should combine two element prefixes', () => {
    const name = generateComboGuardianName(['fire', 'water']);
    expect(name).toContain(' the ');
    expect(name.length).toBeGreaterThan(0);
  });
});

describe('ALL_GUARDIAN_FLOORS', () => {
  it('should include base guardian floors', () => {
    expect(ALL_GUARDIAN_FLOORS).toContain(10);
    expect(ALL_GUARDIAN_FLOORS).toContain(20);
    expect(ALL_GUARDIAN_FLOORS).toContain(100);
  });

  it('should be sorted', () => {
    for (let i = 1; i < ALL_GUARDIAN_FLOORS.length; i++) {
      expect(ALL_GUARDIAN_FLOORS[i]).toBeGreaterThan(ALL_GUARDIAN_FLOORS[i - 1]);
    }
  });
});
