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 = {
    prestigeUpgrades: {},
  };

  it('should return base 100 with no upgrades', () => {
    const result = computeMaxMana(baseState);
    expect(result).toBe(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 = {
    prestigeUpgrades: {},
    attunements: {},
  };

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

  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 discipline bonus', () => {
    const result = computeClickMana();
    expect(result).toBe(1);
  });

  it('should add discipline click bonus', () => {
    const result = computeClickMana({
      bonuses: { clickManaBonus: 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 follow continuous ramp: 1 + (hours/8)*4', () => {
    // At 2 hours: 1 + (2/8)*4 = 2.0
    const ticksFor2Hours = 2 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor2Hours);
    expect(result).toBeCloseTo(2.0, 5);
  });

  it('should cap at 2.5 with no discipline bonus', () => {
    // At 3+ hours: cap at 2.5
    const ticksFor8Hours = 8 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor8Hours);
    expect(result).toBeCloseTo(2.5, 5);
  });

  it('should ramp linearly: 1 hour → 1.5', () => {
    const ticksFor1Hour = 1 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor1Hour);
    expect(result).toBeCloseTo(1.5, 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 respect discipline meditation cap bonus', () => {
    // With +3.5 discipline cap, max = 6.0
    // Need more than 3 hours for the higher cap to matter
    // At 16 hours without cap: 1 + (16/8)*4 = 9.0, capped to 6.0
    const ticksFor16Hours = 16 / HOURS_PER_TICK;
    const result = getMeditationBonus(ticksFor16Hours, 1, 3.5);
    expect(result).toBeCloseTo(6.0, 5);
  });
});

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

describe('computeEffectiveRegenForDisplay', () => {
  const baseState = {
    prestigeUpgrades: {},
    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 have effectiveRegen equal to rawRegen since conversion drain is always 0', () => {
    const result = computeEffectiveRegenForDisplay(baseState);
    expect(result.conversionDrain).toBe(0);
    expect(result.effectiveRegen).toBe(result.rawRegen);
  });
});
