// ─── Room Enchantments System Tests ────────────────────────────────────────────
// Tests for coverage computation, effect application, and equipment scanning.

import { describe, it, expect } from 'vitest';
import {
  computeCoveragePerTick,
  applyRoomEnchantmentTick,
  getEquippedRoomEnchantments,
  isRoomEnchantmentSpecialId,
  getRoomEnchantmentBaseMagnitude,
  getRoomEnchantmentName,
  getRoomEnchantmentColor,
  BASE_COVERAGE_RATE,
} from '../utils/room-enchantments-utils';
import type { EnemyState, EquipmentInstance } from '../types';

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

function makeEnemy(overrides: Partial<EnemyState> = {}): EnemyState {
  return {
    id: 'enemy-1',
    name: 'Test Enemy',
    hp: 100,
    maxHP: 100,
    armor: 0.1,
    dodgeChance: 0.05,
    element: 'fire',
    activeEffects: [],
    effectiveArmor: 0.1,
    ...overrides,
  };
}

function makeEquipmentInstance(enchantmentIds: string[]): EquipmentInstance {
  return {
    instanceId: 'test-instance',
    typeId: 'test-boots',
    name: 'Test Boots',
    enchantments: enchantmentIds.map(id => ({
      effectId: id,
      stacks: 1,
      capacityCost: 20,
    })),
    usedCapacity: 20,
    totalCapacity: 30,
    rarity: 'common',
    quality: 1,
  };
}

// ─── computeCoveragePerTick ────────────────────────────────────────────────────

describe('computeCoveragePerTick', () => {
  it('returns base rate (0.2) with no discipline bonus', () => {
    expect(computeCoveragePerTick(0)).toBe(BASE_COVERAGE_RATE);
    expect(computeCoveragePerTick(0)).toBe(0.2);
  });

  it('adds discipline bonus to base rate', () => {
    expect(computeCoveragePerTick(0.03)).toBe(0.23);
    expect(computeCoveragePerTick(0.12)).toBe(0.32);
  });
});

// ─── isRoomEnchantmentSpecialId ────────────────────────────────────────────────

describe('isRoomEnchantmentSpecialId', () => {
  it('returns true for valid room enchantment specialIds', () => {
    expect(isRoomEnchantmentSpecialId('room_fire_damage')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_frost_debuff')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_death_damage')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_lightning_damage')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_dark_dodge_debuff')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_earth_armor_debuff')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_transference_buff')).toBe(true);
    expect(isRoomEnchantmentSpecialId('room_transference_regen')).toBe(true);
  });

  it('returns false for non-room enchantment specialIds', () => {
    expect(isRoomEnchantmentSpecialId('spellEcho10')).toBe(false);
    expect(isRoomEnchantmentSpecialId('fireBlade')).toBe(false);
    expect(isRoomEnchantmentSpecialId('')).toBe(false);
  });
});

// ─── getRoomEnchantmentBaseMagnitude ───────────────────────────────────────────

describe('getRoomEnchantmentBaseMagnitude', () => {
  it('returns correct base magnitudes', () => {
    expect(getRoomEnchantmentBaseMagnitude('room_fire_damage')).toBe(5);
    expect(getRoomEnchantmentBaseMagnitude('room_frost_debuff')).toBe(0.10);
    expect(getRoomEnchantmentBaseMagnitude('room_death_damage')).toBe(3);
    expect(getRoomEnchantmentBaseMagnitude('room_lightning_damage')).toBe(3);
    expect(getRoomEnchantmentBaseMagnitude('room_dark_dodge_debuff')).toBe(0.10);
    expect(getRoomEnchantmentBaseMagnitude('room_earth_armor_debuff')).toBe(0.05);
    expect(getRoomEnchantmentBaseMagnitude('room_transference_buff')).toBe(0.05);
    expect(getRoomEnchantmentBaseMagnitude('room_transference_regen')).toBe(0.10);
  });

  it('returns 0 for unknown specialId', () => {
    expect(getRoomEnchantmentBaseMagnitude('unknown')).toBe(0);
  });
});

// ─── getRoomEnchantmentName ────────────────────────────────────────────────────

describe('getRoomEnchantmentName', () => {
  it('returns correct display names', () => {
    expect(getRoomEnchantmentName('room_fire_damage')).toBe('Blazing Footsteps');
    expect(getRoomEnchantmentName('room_frost_debuff')).toBe('Frozen Trail');
    expect(getRoomEnchantmentName('room_death_damage')).toBe('Necrotic Tread');
    expect(getRoomEnchantmentName('room_lightning_damage')).toBe('Shocking Stride');
    expect(getRoomEnchantmentName('room_dark_dodge_debuff')).toBe('Shadow Patch');
    expect(getRoomEnchantmentName('room_earth_armor_debuff')).toBe('Scoured Earth');
    expect(getRoomEnchantmentName('room_transference_buff')).toBe('Transference Grounds');
    expect(getRoomEnchantmentName('room_transference_regen')).toBe('Conductive Path');
  });
});

// ─── getEquippedRoomEnchantments ───────────────────────────────────────────────

describe('getEquippedRoomEnchantments', () => {
  it('returns empty array when no feet equipped', () => {
    const result = getEquippedRoomEnchantments({}, {});
    expect(result).toEqual([]);
  });

  it('returns empty array when feet has no room enchantments', () => {
    const instances = { 'inst-1': makeEquipmentInstance(['spell_echo_10']) };
    const result = getEquippedRoomEnchantments({ feet: 'inst-1' }, instances);
    expect(result).toEqual([]);
  });

  it('returns room enchantments from equipped footwear', () => {
    const instances = { 'inst-1': makeEquipmentInstance(['boots_sigil_fire']) };
    const result = getEquippedRoomEnchantments({ feet: 'inst-1' }, instances);
    expect(result).toHaveLength(1);
    expect(result[0].specialId).toBe('room_fire_damage');
    expect(result[0].baseMagnitude).toBe(5);
  });

  it('returns multiple room enchantments from equipped footwear', () => {
    const instances = { 'inst-1': makeEquipmentInstance(['boots_sigil_fire', 'boots_sigil_frost']) };
    const result = getEquippedRoomEnchantments({ feet: 'inst-1' }, instances);
    expect(result).toHaveLength(2);
    expect(result[0].specialId).toBe('room_fire_damage');
    expect(result[1].specialId).toBe('room_frost_debuff');
  });
});

// ─── applyRoomEnchantmentTick ──────────────────────────────────────────────────

describe('applyRoomEnchantmentTick', () => {
  it('does nothing when no enemies', () => {
    const result = applyRoomEnchantmentTick({
      coverage: 50,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies: [],
      rawMana: 100,
    });
    expect(result.enemies).toEqual([]);
    expect(result.playerBuffs.castSpeedMultiplier).toBe(0);
  });

  it('does nothing when no equipped room enchantments', () => {
    const enemies = [makeEnemy()];
    const result = applyRoomEnchantmentTick({
      coverage: 50,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [],
      enemies,
      rawMana: 100,
    });
    expect(result.enemies[0].hp).toBe(100);
  });

  it('AC-11: fire DoT scales linearly with coverage', () => {
    const enemies = [makeEnemy()];

    // At 0% coverage, no damage
    const r0 = applyRoomEnchantmentTick({
      coverage: 0,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(r0.enemies[0].hp).toBe(100);

    // At 50% coverage: 5 * 0.5 = 2.5 damage
    const r50 = applyRoomEnchantmentTick({
      coverage: 50,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(r50.enemies[0].hp).toBeCloseTo(97.5, 1);

    // At 100% coverage: 5 * 1.0 = 5 damage
    const r100 = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(r100.enemies[0].hp).toBe(95);
  });

  it('AC-8: fire DoT hits all living enemies', () => {
    const enemies = [makeEnemy({ id: 'e1' }), makeEnemy({ id: 'e2' }), makeEnemy({ id: 'e3' })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies,
      rawMana: 100,
    });
    expect(result.enemies[0].hp).toBe(95);
    expect(result.enemies[1].hp).toBe(95);
    expect(result.enemies[2].hp).toBe(95);
  });

  it('AC-8: fire DoT does not damage dead enemies', () => {
    const enemies = [makeEnemy({ id: 'e1', hp: 0 }), makeEnemy({ id: 'e2' })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies,
      rawMana: 100,
    });
    expect(result.enemies[0].hp).toBe(0);
    expect(result.enemies[1].hp).toBe(95);
  });

  it('AC-11: aura magnitude multiplier increases effect', () => {
    // At 100% coverage with 1.5x multiplier: 5 * 1.0 * 1.5 = 7.5 damage
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1.5,
      equippedRoomEnchantments: [{ specialId: 'room_fire_damage', baseMagnitude: 5 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(result.enemies[0].hp).toBeCloseTo(92.5, 1);
  });

  it('AC-10: transference buff returns cast speed multiplier', () => {
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_transference_buff', baseMagnitude: 0.05 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(result.playerBuffs.castSpeedMultiplier).toBe(0.05);
  });

  it('AC-10: transference regen returns regen per hour', () => {
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_transference_regen', baseMagnitude: 0.10 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(result.playerBuffs.transferenceRegenPerHour).toBe(0.10);
  });

  it('AC-9: frost debuff reduces enemy dodge chance', () => {
    const enemies = [makeEnemy({ dodgeChance: 0.3 })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_frost_debuff', baseMagnitude: 0.10 }],
      enemies,
      rawMana: 100,
    });
    // dodgeChance reduced by 0.10 (10% of 100% coverage)
    expect(result.enemies[0].dodgeChance).toBeCloseTo(0.20, 2);
  });

  it('AC-9: earth debuff reduces enemy effective armor', () => {
    const enemies = [makeEnemy({ effectiveArmor: 0.3 })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_earth_armor_debuff', baseMagnitude: 0.05 }],
      enemies,
      rawMana: 100,
    });
    expect(result.enemies[0].effectiveArmor).toBeCloseTo(0.25, 2);
  });

  it('AC-12: multiple enchantments share coverage and apply independently', () => {
    const enemies = [makeEnemy({ dodgeChance: 0.3 })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [
        { specialId: 'room_fire_damage', baseMagnitude: 5 },
        { specialId: 'room_frost_debuff', baseMagnitude: 0.10 },
      ],
      enemies,
      rawMana: 100,
    });
    // Fire damage applied
    expect(result.enemies[0].hp).toBe(95);
    // Frost debuff applied
    expect(result.enemies[0].dodgeChance).toBeCloseTo(0.20, 2);
  });

  it('death DoT deals 3 dmg/tick at 100% coverage', () => {
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_death_damage', baseMagnitude: 3 }],
      enemies: [makeEnemy()],
      rawMana: 100,
    });
    expect(result.enemies[0].hp).toBe(97);
  });

  it('dark dodge debuff reduces dodge chance', () => {
    const enemies = [makeEnemy({ dodgeChance: 0.5 })];
    const result = applyRoomEnchantmentTick({
      coverage: 100,
      auraMagnitudeMultiplier: 1,
      equippedRoomEnchantments: [{ specialId: 'room_dark_dodge_debuff', baseMagnitude: 0.10 }],
      enemies,
      rawMana: 100,
    });
    expect(result.enemies[0].dodgeChance).toBeCloseTo(0.40, 2);
  });
});

// ─── Coverage cap ──────────────────────────────────────────────────────────────

describe('coverage cap at 100', () => {
  it('coverage cannot exceed 100 in computeCoveragePerTick usage', () => {
    // Simulate what the combat tick does: min(100, coverage + rate)
    const rate = computeCoveragePerTick(0.12); // max tier rate
    const newCoverage = Math.min(100, 99.9 + rate);
    expect(newCoverage).toBe(100);
  });
});
