import { describe, it, expect } from 'vitest';
import {
  fmt,
  fmtDec,
  formatSpellCost,
  getSpellCostColor,
  formatStudyTime,
  formatHour,
} from '../utils/formatting';

// ─── fmt ──────────────────────────────────────────────────────────────────────

describe('fmt', () => {
  it('should format zero as "0"', () => {
    expect(fmt(0)).toBe('0');
  });

  it('should format numbers < 1000 as integers', () => {
    expect(fmt(1)).toBe('1');
    expect(fmt(500)).toBe('500');
    expect(fmt(999)).toBe('999');
  });

  it('should format thousands with K suffix', () => {
    expect(fmt(1000)).toBe('1.0K');
    expect(fmt(1500)).toBe('1.5K');
    expect(fmt(9999)).toBe('10.0K');
    expect(fmt(10000)).toBe('10.0K');
    expect(fmt(999999)).toBe('1000.0K');
  });

  it('should format millions with M suffix', () => {
    expect(fmt(1000000)).toBe('1.00M');
    expect(fmt(1500000)).toBe('1.50M');
    expect(fmt(999999999)).toBe('1000.00M');
  });

  it('should format billions with B suffix', () => {
    expect(fmt(1000000000)).toBe('1.00B');
    expect(fmt(1500000000)).toBe('1.50B');
    expect(fmt(2500000000)).toBe('2.50B');
  });

  it('should handle negative numbers', () => {
    expect(fmt(-500)).toBe('-500');
    // fmt uses Math.floor which doesn't add suffixes for negatives
    expect(fmt(-1500)).toBe('-1500');
  });

  it('should handle NaN', () => {
    expect(fmt(NaN)).toBe('0');
  });

  it('should handle Infinity', () => {
    expect(fmt(Infinity)).toBe('0');
    expect(fmt(-Infinity)).toBe('0');
  });

  it('should handle very large numbers', () => {
    expect(fmt(1e12)).toBe('1000.00B');
    expect(fmt(1e15)).toBe('1000000.00B');
  });
});

// ─── fmtDec ───────────────────────────────────────────────────────────────────

describe('fmtDec', () => {
  it('should format with default 1 decimal', () => {
    expect(fmtDec(1.5)).toBe('1.5');
    expect(fmtDec(10.0)).toBe('10.0');
  });

  it('should format with specified decimals', () => {
    expect(fmtDec(1.234, 2)).toBe('1.23');
    expect(fmtDec(1.235, 2)).toBe('1.24'); // rounds
    expect(fmtDec(1.999, 0)).toBe('2');
  });

  it('should handle zero', () => {
    expect(fmtDec(0, 1)).toBe('0.0');
  });

  it('should handle negative numbers', () => {
    expect(fmtDec(-1.5, 1)).toBe('-1.5');
  });

  it('should handle NaN', () => {
    expect(fmtDec(NaN, 1)).toBe('0');
  });

  it('should handle Infinity', () => {
    expect(fmtDec(Infinity, 1)).toBe('0');
  });

  it('should handle large numbers', () => {
    expect(fmtDec(1234567.89, 2)).toBe('1234567.89');
  });
});

// ─── formatSpellCost ──────────────────────────────────────────────────────────

describe('formatSpellCost', () => {
  it('should format raw cost', () => {
    expect(formatSpellCost({ type: 'raw', amount: 10 })).toBe('10 raw');
    expect(formatSpellCost({ type: 'raw', amount: 0 })).toBe('0 raw');
  });

  it('should format elemental cost with symbol', () => {
    expect(formatSpellCost({ type: 'element', element: 'fire', amount: 5 })).toBe('5 🔥');
    expect(formatSpellCost({ type: 'element', element: 'water', amount: 3 })).toBe('3 💧');
    expect(formatSpellCost({ type: 'element', element: 'earth', amount: 10 })).toBe('10 ⛰️');
  });

  it('should handle unknown element', () => {
    expect(formatSpellCost({ type: 'element', element: 'unknown', amount: 5 })).toBe('5 ?');
  });

  it('should handle empty element', () => {
    expect(formatSpellCost({ type: 'element', element: '', amount: 5 })).toBe('5 ?');
  });
});

// ─── getSpellCostColor ────────────────────────────────────────────────────────

describe('getSpellCostColor', () => {
  it('should return blue for raw cost', () => {
    expect(getSpellCostColor({ type: 'raw', amount: 10 })).toBe('#60A5FA');
  });

  it('should return element color for elemental cost', () => {
    expect(getSpellCostColor({ type: 'element', element: 'fire', amount: 5 })).toBe('#FF6B35');
    expect(getSpellCostColor({ type: 'element', element: 'water', amount: 5 })).toBe('#4ECDC4');
    expect(getSpellCostColor({ type: 'element', element: 'light', amount: 5 })).toBe('#FFD700');
  });

  it('should return gray for unknown element', () => {
    expect(getSpellCostColor({ type: 'element', element: 'unknown', amount: 5 })).toBe('#9CA3AF');
  });

  it('should return gray for empty element', () => {
    expect(getSpellCostColor({ type: 'element', element: '', amount: 5 })).toBe('#9CA3AF');
  });
});

// ─── formatStudyTime ──────────────────────────────────────────────────────────

describe('formatStudyTime', () => {
  it('should format hours when >= 1', () => {
    expect(formatStudyTime(1)).toBe('1.0h');
    expect(formatStudyTime(2.5)).toBe('2.5h');
    expect(formatStudyTime(10)).toBe('10.0h');
  });

  it('should format minutes when < 1 hour', () => {
    expect(formatStudyTime(0.5)).toBe('30m');
    expect(formatStudyTime(0.25)).toBe('15m');
    expect(formatStudyTime(0.0167)).toBe('1m'); // ~1 minute
  });

  it('should handle zero', () => {
    expect(formatStudyTime(0)).toBe('0m');
  });

  it('should handle very small values', () => {
    // 0.001 hours * 60 = 0.06 min, Math.round(0.06) = 0
    expect(formatStudyTime(0.001)).toBe('0m');
  });
});

// ─── formatHour ───────────────────────────────────────────────────────────────

describe('formatHour', () => {
  it('should format midnight', () => {
    expect(formatHour(0)).toBe('00:00');
  });

  it('should format noon', () => {
    expect(formatHour(12)).toBe('12:00');
  });

  it('should format morning hours', () => {
    expect(formatHour(8)).toBe('08:00');
    expect(formatHour(9.5)).toBe('09:30');
  });

  it('should format evening hours', () => {
    expect(formatHour(18)).toBe('18:00');
    expect(formatHour(23.75)).toBe('23:45');
  });

  it('should pad single-digit hours', () => {
    expect(formatHour(1)).toBe('01:00');
    expect(formatHour(5.25)).toBe('05:15');
  });

  it('should pad minutes', () => {
    // 12.01: 0.01*60=0.6, floor=0 → '12:00'
    expect(formatHour(12.01)).toBe('12:00');
    // 12.1: 0.1*60=5.999... due to floating point, floor=5 → '12:05'
    expect(formatHour(12.1)).toBe('12:05');
  });

  it('should handle fractional hours', () => {
    expect(formatHour(14.5)).toBe('14:30');
    expect(formatHour(14.25)).toBe('14:15');
    expect(formatHour(14.75)).toBe('14:45');
  });
});
