import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { getAttunementLevelMultiplier, getAttunementConversionRate, ATTUNEMENTS_DEF } from '../data/attunements';
import { computeConversionRates } from '../utils/conversion-rates';
import type { DisciplineEffectsResult } from '../effects/discipline-effects';

// ─── Test 1: getAttunementLevelMultiplier uses exponential formula ────────────
describe('getAttunementLevelMultiplier', () => {
  it('should return 1.0 at level 1 (1.5^0 = 1)', () => {
    expect(getAttunementLevelMultiplier(1)).toBeCloseTo(1.0, 5);
  });

  it('should return 1.5 at level 2 (1.5^1 = 1.5)', () => {
    expect(getAttunementLevelMultiplier(2)).toBeCloseTo(1.5, 5);
  });

  it('should return ~2.25 at level 3 (1.5^2 = 2.25)', () => {
    expect(getAttunementLevelMultiplier(3)).toBeCloseTo(2.25, 5);
  });

  it('should return ~19.22 at level 10 (1.5^9)', () => {
    expect(getAttunementLevelMultiplier(10)).toBeCloseTo(Math.pow(1.5, 9), 5);
  });

  it('should NOT use linear formula (1 + level * 0.5)', () => {
    // At level 10, linear would give 6.0, exponential gives ~19.22
    const result = getAttunementLevelMultiplier(10);
    expect(result).not.toBeCloseTo(6.0, 1);
    expect(result).toBeGreaterThan(10);
  });
});

// ─── Test 2: computeConversionRates uses exponential attunement scaling ────────
describe('computeConversionRates attunement scaling', () => {
  const baseDisciplineEffects: DisciplineEffectsResult = {
    bonuses: {},
    conversions: {},
    regen: {},
    combat: { damageMult: 1, attackSpeedMult: 1, critChance: 0, critDamage: 1.5 },
    defense: { armor: 0, barrier: 0, dodgeChance: 0, damageReduction: 0 },
    utility: { capacityBonus: 0, efficiencyBonus: 0, speedBonus: 0, insightBonus: 0 },
  };

  const baseParams = {
    disciplineEffects: baseDisciplineEffects,
    attunements: {},
    signedPacts: [],
    pactElementMap: {},
    invokerLevel: 1,
    meditationMultiplier: 1,
    grossRegen: {},
    rawGrossRegen: 1000,
  };

  it('should apply exponential attunement multiplier to conversion rates', () => {
    // Enchanter level 10 should give ~19.22x multiplier to transference conversion
    const result = computeConversionRates({
      ...baseParams,
      attunements: {
        enchanter: { active: true, level: 10 },
      },
    });

    const transferenceRate = result.rates['transference'];
    expect(transferenceRate).toBeDefined();

    // Base rate is 0.2, attunementMult should be ~19.22 at level 10
    // finalRate = 0.2 * 19.22 * 1 * 1 ≈ 3.844
    expect(transferenceRate!.attunementMult).toBeCloseTo(Math.pow(1.5, 9), 3);
    expect(transferenceRate!.finalRate).toBeCloseTo(0.2 * Math.pow(1.5, 9), 2);
  });

  it('should apply exponential attunement multiplier for fabricator (earth)', () => {
    const result = computeConversionRates({
      ...baseParams,
      attunements: {
        fabricator: { active: true, level: 5 },
      },
    });

    const earthRate = result.rates['earth'];
    expect(earthRate).toBeDefined();

    // Base rate is 0.25, attunementMult should be 1.5^4 = 5.0625 at level 5
    expect(earthRate!.attunementMult).toBeCloseTo(Math.pow(1.5, 4), 3);
    expect(earthRate!.finalRate).toBeCloseTo(0.25 * Math.pow(1.5, 4), 2);
  });

  it('should NOT use linear attunement multiplier', () => {
    // At level 10, linear would give 1 + 10*0.5 = 6, exponential gives ~19.22
    const result = computeConversionRates({
      ...baseParams,
      attunements: {
        enchanter: { active: true, level: 10 },
      },
    });

    const transferenceRate = result.rates['transference'];
    // Linear would give attMult = 6, exponential gives ~19.22
    expect(transferenceRate!.attunementMult).not.toBeCloseTo(6.0, 0);
    expect(transferenceRate!.attunementMult).toBeGreaterThan(10);
  });

  it('should give level 1 attunement a 1.0x multiplier (no bonus)', () => {
    const result = computeConversionRates({
      ...baseParams,
      attunements: {
        enchanter: { active: true, level: 1 },
      },
    });

    const transferenceRate = result.rates['transference'];
    expect(transferenceRate!.attunementMult).toBeCloseTo(1.0, 5);
  });
});

// ─── Test 3: getAttunementConversionRate returns flat base rate ────────────────
describe('getAttunementConversionRate', () => {
  it('should return base rate for enchanter (0.2)', () => {
    expect(getAttunementConversionRate('enchanter', 1)).toBe(0.2);
  });

  it('should return same base rate regardless of level (level scaling applied separately)', () => {
    const level1 = getAttunementConversionRate('enchanter', 1);
    const level5 = getAttunementConversionRate('enchanter', 5);
    const level10 = getAttunementConversionRate('enchanter', 10);
    expect(level1).toBe(level5);
    expect(level5).toBe(level10);
    expect(level1).toBe(0.2);
  });

  it('should return base rate for fabricator (0.25)', () => {
    expect(getAttunementConversionRate('fabricator', 1)).toBe(0.25);
  });

  it('should return 0 for invoker (no conversion)', () => {
    expect(getAttunementConversionRate('invoker', 1)).toBe(0);
    expect(getAttunementConversionRate('invoker', 10)).toBe(0);
  });

  it('should return 0 for unknown attunement', () => {
    expect(getAttunementConversionRate('nonexistent', 5)).toBe(0);
  });
});

// ─── Test 4: Spec table verification ──────────────────────────────────────────
describe('Spec table verification (§4.3)', () => {
  it('should match spec conversion rate table for enchanter', () => {
    // Spec: Level 10 Enchanter converts at 7.689/hr (0.2 * 1.5^9)
    const level10Rate = 0.2 * Math.pow(1.5, 9);
    expect(level10Rate).toBeCloseTo(7.689, 1);
  });

  it('should match spec conversion rate table for fabricator', () => {
    // Spec: Level 10 Fabricator converts at 9.610/hr (0.25 * 1.5^9)
    const level10Rate = 0.25 * Math.pow(1.5, 9);
    expect(level10Rate).toBeCloseTo(9.610, 1);
  });

  it('should match spec regen table for enchanter', () => {
    // Spec: Level 10 Enchanter regen = 19.221/hr (0.5 * 1.5^9)
    const level10Regen = 0.5 * Math.pow(1.5, 9);
    expect(level10Regen).toBeCloseTo(19.221, 1);
  });
});
