const { oddsToImplied, impliedToAmerican, devig, clv } = require('../../src/utils/odds');

describe('odds utility', () => {
  describe('oddsToImplied', () => {
    test('+150 → ~0.40', () => {
      expect(oddsToImplied(150)).toBeCloseTo(0.4, 3);
    });

    test('-110 → ~0.524', () => {
      expect(oddsToImplied(-110)).toBeCloseTo(0.5238, 3);
    });

    test('invalid input returns null', () => {
      expect(oddsToImplied(null)).toBeNull();
      expect(oddsToImplied('abc')).toBeNull();
      expect(oddsToImplied(0)).toBeNull();
    });
  });

  describe('impliedToAmerican', () => {
    test('0.5 → -100', () => {
      expect(impliedToAmerican(0.5)).toBe(-100);
    });

    test('0.4 → +150', () => {
      expect(impliedToAmerican(0.4)).toBe(150);
    });

    test('round-trips with oddsToImplied within 1 cent', () => {
      // +100 and -100 both encode p=0.5 (even money) so we skip the boundary.
      for (const odds of [-200, -150, -110, +150, +250]) {
        const back = impliedToAmerican(oddsToImplied(odds));
        expect(Math.abs(back - odds)).toBeLessThanOrEqual(1);
      }
    });

    test('out-of-range prob returns null', () => {
      expect(impliedToAmerican(0)).toBeNull();
      expect(impliedToAmerican(1)).toBeNull();
      expect(impliedToAmerican(NaN)).toBeNull();
    });
  });

  describe('devig', () => {
    test('symmetric -110/-110 normalizes to 0.5/0.5', () => {
      const { fairOver, fairUnder } = devig(-110, -110);
      expect(fairOver).toBeCloseTo(0.5, 5);
      expect(fairUnder).toBeCloseTo(0.5, 5);
    });

    test('asymmetric -130/+110 — over more likely, both sum to 1.0', () => {
      const { fairOver, fairUnder } = devig(-130, +110);
      expect(fairOver + fairUnder).toBeCloseTo(1.0, 5);
      expect(fairOver).toBeGreaterThan(0.5);
    });

    test('invalid odds returns null', () => {
      expect(devig(0, -110)).toBeNull();
      expect(devig(-110, null)).toBeNull();
    });
  });

  describe('clv', () => {
    test('positive when closing > graded', () => {
      expect(clv(0.50, 0.55)).toBeCloseTo(0.05, 5);
    });

    test('negative when closing < graded', () => {
      expect(clv(0.55, 0.50)).toBeCloseTo(-0.05, 5);
    });

    test('invalid input returns null', () => {
      expect(clv('x', 0.5)).toBeNull();
      expect(clv(0.5, undefined)).toBeNull();
    });
  });
});
