import { describe, it, expect } from 'vitest';
import {
  computeMaxMana,
  computeRegen,
  computeClickMana,
  getMeditationBonus,
  computeEffectiveRegenForDisplay,
} from '../utils/mana-utils';
import { HOURS_PER_TICK } from '../constants';

// ─── computeMaxMana ───────────────────────────────────────────────────────────

describe('computeMaxMana', () => {
  const baseState = {
    skills: {},
    prestigeUpgrades: {},
    skillUpgrades: {},
    skillTiers: {},
  };

  it('should return base 100 with no skills or upgrades', () => {
    const result = computeMaxMana(baseState);
    expect(result).toBe(100);
  });

  it('should add 100 per manaWell skill level', () => {
    const result = computeMaxMana({
      ...baseState,
      skills: { manaWell: 3 },
    });
    expect(result).toBe(100 + 3 * 100);
  });

  it('should add 500 per manaWell prestige upgrade', () => {
    const result = computeMaxMana({
      ...baseState,
      prestigeUpgrades: { manaWell: 2 },
    });
    expect(result).toBe(100 + 2 * 500);
  });

  it('should apply maxManaBonus from effects', () => {
    const result = computeMaxMana(baseState, {
      maxManaBonus: 50,
      maxManaMultiplier: 1,
    } as any);
    expect(result).toBe(150);
  });

  it('should apply maxManaMultiplier from effects', () => {
    const result = computeMaxMana(baseState, {
      maxManaBonus: 0,
      maxManaMultiplier: 2,
    } as any);
    expect(result).toBe(200);
  });

  it('should apply both bonus and multiplier', () => {
    // (100 + 50) * 1.5 = 225
    const result = computeMaxMana(baseState, {
      maxManaBonus: 50,
      maxManaMultiplier: 1.5,
    } as any);
    expect(result).toBe(225);
  });

  it('should add discipline bonus', () => {
    const result = computeMaxMana(baseState, undefined, {
      bonuses: { maxManaBonus: 200 },
      multipliers: {},
    });
    expect(result).toBe(300);
  });

  it('should floor the result', () => {
    const result = computeMaxMana(baseState, {
      maxManaBonus: 0,
      maxManaMultiplier: 1.33,
    } as any);
    expect(result).toBe(133); // floor(133)
  });
});

// ─── computeRegen ─────────────────────────────────────────────────────────────

describe('computeRegen', () => {
  const baseState = {
    skills: {},
    prestigeUpgrades: {},
    skillUpgrades: {},
    skillTiers: {},
    attunements: {},
  };

  it('should return base regen of 2 with no skills', () => {
    const result = computeRegen(baseState);
    expect(result).toBe(2);
  });

  it('should add 1 per manaFlow skill level', () => {
    const result = computeRegen({
      ...baseState,
      skills: { manaFlow: 3 },
    });
    expect(result).toBe(5); // 2 + 3
  });

  it('should add 2 per manaSpring skill level', () => {
    const result = computeRegen({
      ...baseState,
      skills: { manaSpring: 2 },
    });
    expect(result).toBe(6); // 2 + 4
  });

  it('should add 0.5 per manaFlow prestige upgrade', () => {
    const result = computeRegen({
      ...baseState,
      prestigeUpgrades: { manaFlow: 4 },
    });
    expect(result).toBe(4); // 2 + 2
  });

  it('should apply temporalEcho bonus', () => {
    // temporalEcho=1 → 1.1x multiplier → 2 * 1.1 = 2.2
    const result = computeRegen({
      ...baseState,
      prestigeUpgrades: { temporalEcho: 1 },
    });
    expect(result).toBeCloseTo(2.2, 5);
  });

  it('should apply regenBonus from effects', () => {
    const result = computeRegen(baseState, {
      regenBonus: 3,
      regenMultiplier: 1,
      permanentRegenBonus: 0,
    } as any);
    expect(result).toBe(5);
  });

  it('should apply regenMultiplier from effects', () => {
    const result = computeRegen(baseState, {
      regenBonus: 0,
      regenMultiplier: 2,
      permanentRegenBonus: 0,
    } as any);
    expect(result).toBe(4);
  });

  it('should apply permanentRegenBonus from effects', () => {
    const result = computeRegen(baseState, {
      regenBonus: 0,
      regenMultiplier: 1,
      permanentRegenBonus: 5,
    } as any);
    expect(result).toBe(7);
  });

  it('should add discipline regen bonus', () => {
    const result = computeRegen(baseState, undefined, {
      bonuses: { regenBonus: 10 },
      multipliers: {},
    });
    expect(result).toBe(12);
  });
});

// ─── computeClickMana ─────────────────────────────────────────────────────────

describe('computeClickMana', () => {
  it('should return 1 with no skills', () => {
    const result = computeClickMana({});
    expect(result).toBe(1);
  });

  it('should add 1 per manaTap skill level', () => {
    const result = computeClickMana({ manaTap: 3 });
    expect(result).toBe(4); // 1 + 3
  });

  it('should add 3 per manaSurge skill level', () => {
    const result = computeClickMana({ manaSurge: 2 });
    expect(result).toBe(7); // 1 + 6
  });

  it('should combine manaTap and manaSurge', () => {
    const result = computeClickMana({ manaTap: 2, manaSurge: 1 });
    expect(result).toBe(6); // 1 + 2 + 3
  });

  it('should add discipline click multiplier', () => {
    const result = computeClickMana({}, {
      bonuses: { clickManaMultiplier: 5 },
      multipliers: {},
    });
    expect(result).toBe(6);
  });
});

// ─── getMeditationBonus ───────────────────────────────────────────────────────

describe('getMeditationBonus', () => {
  it('should return 1.0 with zero ticks', () => {
    expect(getMeditationBonus(0, {})).toBe(1.0);
  });

  it('should increase with more ticks', () => {
    const low = getMeditationBonus(10, {});
    const high = getMeditationBonus(100, {});
    expect(high).toBeGreaterThan(low);
  });

  it('should cap at 1.5x without meditation skill', () => {
    // After 4 hours: 1 + min(4/4, 0.5) = 1.5
    const ticksFor4Hours = 4 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor4Hours, {});
    expect(result).toBeCloseTo(1.5, 5);
  });

  it('should reach 2.5x with meditation skill after 4 hours', () => {
    const ticksFor4Hours = 4 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor4Hours, { meditation: 1 });
    expect(result).toBeCloseTo(2.5, 5);
  });

  it('should reach 3.0x with deep trance after 6 hours', () => {
    const ticksFor6Hours = 6 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor6Hours, {
      meditation: 1,
      deepTrance: 1,
    });
    expect(result).toBeCloseTo(3.0, 5);
  });

  it('should reach 5.0x with void meditation after 8 hours', () => {
    const ticksFor8Hours = 8 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor8Hours, {
      meditation: 1,
      deepTrance: 1,
      voidMeditation: 1,
    });
    expect(result).toBeCloseTo(5.0, 5);
  });

  it('should reach cap at 2 hours (min(2/4, 0.5) = 0.5)', () => {
    // At 2 hours: min(2/4, 0.5) = min(0.5, 0.5) = 0.5 → 1.5x
    const ticksFor2Hours = 2 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor2Hours, {});
    expect(result).toBeCloseTo(1.5, 5);
  });

  it('should be below cap at 1 hour', () => {
    const ticksFor1Hour = 1 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor1Hour, {});
    expect(result).toBeLessThan(1.5);
  });

  it('should apply meditation efficiency multiplier', () => {
    const ticksFor4Hours = 4 / HOURS_PER_TICK;
    const withoutEff = getMeditationBonus(ticksFor4Hours, {}, 1);
    const withEff = getMeditationBonus(ticksFor4Hours, {}, 2);
    expect(withEff).toBeCloseTo(withoutEff * 2, 5);
  });

  it('should ramp linearly before cap', () => {
    // At 1 hour: 1 + min(1/4, 0.5) = 1 + 0.25 = 1.25
    const ticksFor1Hour = 1 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor1Hour, {});
    expect(result).toBeCloseTo(1.25, 5);
  });
});

// ─── computeEffectiveRegenForDisplay ──────────────────────────────────────────

describe('computeEffectiveRegenForDisplay', () => {
  const baseState = {
    skills: {},
    prestigeUpgrades: {},
    skillUpgrades: {},
    skillTiers: {},
    attunements: {},
  };

  it('should return raw, conversion, and effective regen', () => {
    const result = computeEffectiveRegenForDisplay(baseState);
    expect(result).toHaveProperty('rawRegen');
    expect(result).toHaveProperty('conversionDrain');
    expect(result).toHaveProperty('effectiveRegen');
  });

  it('should have effectiveRegen equal to rawRegen with no conversion drain', () => {
    const result = computeEffectiveRegenForDisplay(baseState);
    expect(result.rawRegen).toBe(2);
    expect(result.conversionDrain).toBe(0);
    expect(result.effectiveRegen).toBe(2);
  });

  it('should not let effectiveRegen go below zero', () => {
    // This would require a state with high conversion drain
    // We can't easily test this without attunement data, but we can verify the Math.max behavior
    const result = computeEffectiveRegenForDisplay(baseState);
    expect(result.effectiveRegen).toBeGreaterThanOrEqual(0);
  });

  it('should calculate effective as raw minus conversion', () => {
    const result = computeEffectiveRegenForDisplay(baseState);
    expect(result.effectiveRegen).toBe(Math.max(0, result.rawRegen - result.conversionDrain));
  });
});
