/**
 * Session 64 Order 1.5 — bind a prop to its REAL game.
 *
 * The root bug: PropLine emits no commence_time, so ledgerService fell back to
 * dating rows by the GRADE timestamp. A 01:00/03:00 UTC snapshot is the
 * previous ET day, so tonight's props were filed under yesterday — which made
 * settlement impossible and then got 64 rows wrongly voided as DNP.
 */
const binder = require('../../src/services/gameBinder');

const GAME = (id, home, away, time) => ({ id, homeTeam: { abbreviation: home }, awayTeam: { abbreviation: away }, gameTime: time });

describe('candidateDates', () => {
  test('a 03:00 UTC grade (23:00 ET previous day) considers the NEXT ET day', () => {
    // 2026-07-19T03:00Z = 2026-07-18 23:00 ET
    const c = binder.candidateDates('2026-07-19T03:00:00Z');
    expect(c[0]).toBe('2026-07-18');
    expect(c).toContain('2026-07-19');   // the real game day
  });
  test('also looks one day back for an in-progress game', () => {
    expect(binder.candidateDates('2026-07-19T18:00:00Z')).toContain('2026-07-18');
  });
});

describe('bindGame', () => {
  const prop = { player: 'X', stat_type: 'hits', home_team: 'CLE', away_team: 'PIT' };

  test('trusts a real game_time when the feed provides one', async () => {
    const r = await binder.bindGame({
      sport: 'mlb', prop: { ...prop, game_time: '2026-07-19T23:10:00Z' },
      gradedAt: '2026-07-19T03:00:00Z', getSchedule: async () => [],
    });
    expect(r.game_date).toBe('2026-07-19');
  });

  test('binds by TEAMS to the real game when game_time is missing', async () => {
    const r = await binder.bindGame({
      sport: 'mlb', prop, gradedAt: '2026-07-19T03:00:00Z',
      getSchedule: async (sp, d) => (d === '2026-07-19'
        ? [GAME('401', 'CLE', 'PIT', '2026-07-19T23:10:00Z')] : []),
    });
    expect(r.game_date).toBe('2026-07-19');   // NOT the 07-18 grade date
    expect(r.game_id).toBe('mlb:2026-07-19:401');
  });

  test('unbindable prop returns NULL — never a guessed date', async () => {
    const r = await binder.bindGame({
      sport: 'mlb', prop, gradedAt: '2026-07-19T03:00:00Z', getSchedule: async () => [],
    });
    expect(r).toBeNull();
  });

  test('DOUBLEHEADER is flagged ambiguous, not silently picked', async () => {
    const r = await binder.bindGame({
      sport: 'mlb', prop, gradedAt: '2026-07-19T03:00:00Z',
      getSchedule: async (sp, d) => (d === '2026-07-19' ? [
        GAME('401', 'CLE', 'PIT', '2026-07-19T17:10:00Z'),
        GAME('402', 'CLE', 'PIT', '2026-07-19T23:10:00Z'),
      ] : []),
    });
    expect(r.ambiguous).toBe(true);
  });

  test('team orientation is tolerated (feeds disagree on home/away)', async () => {
    const r = await binder.bindGame({
      sport: 'mlb', prop, gradedAt: '2026-07-19T03:00:00Z',
      getSchedule: async (sp, d) => (d === '2026-07-19' ? [GAME('401', 'PIT', 'CLE', '2026-07-19T23:10:00Z')] : []),
    });
    expect(r).not.toBeNull();
  });
});

describe('attachGameTimes', () => {
  test('binds a slate and reports counts (a silent collapse must be visible)', async () => {
    const props = [
      { player: 'A', home_team: 'CLE', away_team: 'PIT' },
      { player: 'B', home_team: 'ZZZ', away_team: 'YYY' },     // unbindable
      { player: 'C', home_team: 'CLE', away_team: 'PIT', game_time: '2026-07-19T23:10:00Z' },
    ];
    const out = await binder.attachGameTimes('mlb', props, {
      gradedAt: '2026-07-19T03:00:00Z',
      getSchedule: async (sp, d) => (d === '2026-07-19' ? [GAME('401', 'CLE', 'PIT', '2026-07-19T23:10:00Z')] : []),
    });
    expect(out).toMatchObject({ total: 3, bound: 1, alreadyHad: 1, unresolved: 1 });
    expect(props[0].game_time).toBe('2026-07-19T23:10:00Z');
    expect(props[1].game_time).toBeUndefined();   // left unresolved, not invented
  });
});
