// A1 Session 3 — best available price detection. Honesty rules: a best
// price only exists when ≥2 books post the SAME line for the side and
// their prices differ. Absent beats wrong.

const { detectBestBook, buildPlayerStripsFromProps } = require('../../web/src/lib/slateAdapter');

describe('detectBestBook', () => {
  const rows = [
    { book: 'draftkings', line: 1.5, over_odds: -115, under_odds: -105 },
    { book: 'fanduel', line: 1.5, over_odds: -105, under_odds: -115 },
    { book: 'betmgm', line: 1.5, over_odds: -120, under_odds: 100 },
  ];

  it('picks the best over price at the shared line', () => {
    const out = detectBestBook(rows, 'over', 1.5);
    expect(out).toEqual({ book: 'fanduel', odds: -105 });
  });

  it('picks the best under price independently', () => {
    const out = detectBestBook(rows, 'under', 1.5);
    expect(out).toEqual({ book: 'betmgm', odds: 100 });
  });

  it('returns null for a single book (a lone price is not "best")', () => {
    expect(detectBestBook([rows[0]], 'over', 1.5)).toBeNull();
    expect(detectBestBook([], 'over', 1.5)).toBeNull();
    expect(detectBestBook(null, 'over', 1.5)).toBeNull();
  });

  it('returns null when all books post the identical price', () => {
    const flat = [
      { book: 'draftkings', line: 1.5, over_odds: -110 },
      { book: 'fanduel', line: 1.5, over_odds: -110 },
    ];
    expect(detectBestBook(flat, 'over', 1.5)).toBeNull();
  });

  it('returns null when books only post DIFFERENT lines (odds are not comparable)', () => {
    const split = [
      { book: 'draftkings', line: 1.5, over_odds: -200 },
      { book: 'fanduel', line: 2.5, over_odds: 120 },
    ];
    expect(detectBestBook(split, 'over', 1.5)).toBeNull();
    expect(detectBestBook(split, 'over', 2.5)).toBeNull();
  });

  it('without a reference line, compares at the modal line', () => {
    const mixed = [
      { book: 'draftkings', line: 1.5, over_odds: -115 },
      { book: 'fanduel', line: 1.5, over_odds: -105 },
      { book: 'betmgm', line: 2.5, over_odds: 200 },
    ];
    expect(detectBestBook(mixed, 'over')).toEqual({ book: 'fanduel', odds: -105 });
  });

  it('ignores rows with missing book / line / side odds', () => {
    const dirty = [
      { book: 'draftkings', line: 1.5, over_odds: -115 },
      { book: null, line: 1.5, over_odds: -100 },
      { book: 'fanduel', line: 1.5, over_odds: null },
      { book: 'betmgm', over_odds: -105 },
    ];
    expect(detectBestBook(dirty, 'over', 1.5)).toBeNull();
  });
});

describe('buildPlayerStripsFromProps — bestBook attachment', () => {
  const books = [
    { book: 'draftkings', line: 1.5, over_odds: -115, under_odds: -105 },
    { book: 'fanduel', line: 1.5, over_odds: -105, under_odds: -115 },
  ];

  it('attaches bestBook + book to a graded prop (graded side)', () => {
    const props = [{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over', book: 'draftkings', books }];
    const gradeIndex = {
      'aaron judge|total_bases': {
        player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5,
        direction: 'over', grade: 'A', gradedAt: { line: 1.5, odds: -115, timestamp: new Date().toISOString() },
      },
    };
    const strips = buildPlayerStripsFromProps(props, gradeIndex, {});
    expect(strips).toHaveLength(1);
    const p = strips[0].props[0];
    expect(p.book).toBe('draftkings');
    expect(p.bestBook).toEqual({ book: 'fanduel', odds: -105 });
  });

  it('attaches bestBook on awaiting props too (still real market data)', () => {
    const props = [{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'under', book: 'draftkings', books }];
    const strips = buildPlayerStripsFromProps(props, {}, {});
    const p = strips[0].props[0];
    expect(p.awaiting).toBe(true);
    expect(p.bestBook).toEqual({ book: 'draftkings', odds: -105 });
  });

  it('leaves bestBook null when only one book posted the prop', () => {
    const props = [{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over', book: 'draftkings', books: [books[0]] }];
    const strips = buildPlayerStripsFromProps(props, {}, {});
    expect(strips[0].props[0].bestBook).toBeNull();
  });

  it('leaves bestBook null when props carry no books array (legacy shape)', () => {
    const props = [{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over', book: 'draftkings' }];
    const strips = buildPlayerStripsFromProps(props, {}, {});
    expect(strips[0].props[0].bestBook).toBeNull();
  });
});
