// Wave 4A — OUTLOOK MODE (never-empty slate grid, Step 3).
// The game grid must NEVER dead-end in a "NO SLATE" CTA. When there are no
// live games it falls back to REAL always-available data: yesterday's PROVEN
// A-tier receipts + tomorrow's date-pinned schedule preview. A network
// fetchError stays an ERROR state (a fetch failure is never a fake outlook).

const { buildOutlook, mapTomorrowPreview } = require('../../web/src/lib/outlook');

describe('mapTomorrowPreview — real schedule → preview rows', () => {
  const games = [
    { id: 'g1', awayTeam: { name: 'Yankees' }, homeTeam: { name: 'Red Sox' }, gameTime: '2026-07-14T23:00:00Z', status: 'pre', sport: 'mlb' },
    { id: 'g2', awayTeam: { abbreviation: 'LAD' }, homeTeam: { abbreviation: 'SF' }, status: 'in' }, // live → dropped (a preview is upcoming only)
    { id: 'g3', awayTeam: {}, homeTeam: { name: 'X' } }, // missing away team → dropped (never fabricate a matchup)
    { id: 'g4', awayTeam: { name: 'Cubs' }, homeTeam: { name: 'Cards' }, status: 'post' }, // finished → dropped
  ];

  it('maps upcoming games only, dropping live / finished / incomplete', () => {
    const out = mapTomorrowPreview(games, 8);
    expect(out).toHaveLength(1);
    expect(out[0]).toMatchObject({ id: 'g1', away: 'Yankees', home: 'Red Sox', sport: 'MLB' });
  });

  it('caps at the limit and never fabricates a matchup from bad input', () => {
    const many = Array.from({ length: 10 }, (_, i) => ({ id: `g${i}`, awayTeam: { name: `A${i}` }, homeTeam: { name: `H${i}` }, status: 'pre' }));
    expect(mapTomorrowPreview(many, 3)).toHaveLength(3);
    expect(mapTomorrowPreview(null, 5)).toEqual([]);
    expect(mapTomorrowPreview(undefined, 5)).toEqual([]);
  });

  it('accepts the flat {away,home} shape too (dashboard schedule mapping)', () => {
    const out = mapTomorrowPreview([{ id: 'z', away: 'Mets', home: 'Phillies', start_time: '2026-07-14T18:00:00Z' }], 8);
    expect(out[0]).toMatchObject({ away: 'Mets', home: 'Phillies' });
  });
});

describe('buildOutlook — never-empty selection', () => {
  const settled = [
    { player_name: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A+', outcome: 'hit', actual_value: 3, sport: 'mlb' },
    { player_name: 'Low Grade', stat: 'hits', line: 0.5, side: 'over', grade: 'C', outcome: 'hit' }, // not A-tier → not a receipt
    { player_name: 'The Miss', stat: 'hits', line: 1.5, side: 'over', grade: 'A', outcome: 'miss' }, // a miss never becomes proof
  ];
  const tomorrow = [{ id: 't1', awayTeam: { name: 'Mets' }, homeTeam: { name: 'Braves' }, status: 'pre', sport: 'mlb' }];

  it('a network fetchError stays an ERROR state — never a fabricated outlook', () => {
    expect(buildOutlook({ gamesCount: 0, fetchError: 'No games available right now.', settledRows: settled, tomorrow })).toEqual({ mode: 'error' });
  });

  it('live games on the board → live mode (outlook not shown)', () => {
    expect(buildOutlook({ gamesCount: 3, settledRows: settled, tomorrow })).toEqual({ mode: 'live' });
  });

  it('no games → outlook carrying PROVEN receipts AND tomorrow preview (never blank)', () => {
    const o = buildOutlook({ gamesCount: 0, settledRows: settled, tomorrow });
    expect(o.mode).toBe('outlook');
    expect(o.receipts).toHaveLength(1);
    expect(o.receipts[0].player).toBe('Aaron Judge');
    expect(o.receipts[0].outcome).toBe('hit');
    expect(o.tomorrow).toHaveLength(1);
    expect(o.tomorrow[0].home).toBe('Braves');
    expect(o.hasContent).toBe(true);
  });

  it('no games, no proof, no schedule → STILL an outlook surface (the header carries it), never error / never blank', () => {
    const o = buildOutlook({ gamesCount: 0, settledRows: [], tomorrow: [] });
    expect(o.mode).toBe('outlook');
    expect(o.receipts).toEqual([]);
    expect(o.tomorrow).toEqual([]);
    expect(o.hasContent).toBe(false);
  });

  it('receipts alone (no tomorrow schedule) still fills the grid', () => {
    const o = buildOutlook({ gamesCount: 0, settledRows: settled, tomorrow: [] });
    expect(o.mode).toBe('outlook');
    expect(o.receipts).toHaveLength(1);
    expect(o.tomorrow).toEqual([]);
    expect(o.hasContent).toBe(true);
  });
});

// The dead-end CTA cards must be gone: the grid now renders an Outlook surface.
describe('source: the slate + dashboard render the Outlook surface (not a NO-SLATE dead-end)', () => {
  const fs = require('fs');
  const path = require('path');
  const read = (rel) => fs.readFileSync(path.join(__dirname, '..', '..', 'web', 'src', rel), 'utf8');

  it('Slate.tsx wires buildOutlook / the Outlook surface into the empty branch', () => {
    const src = read('components/Slate.tsx');
    expect(src).toMatch(/OutlookSurface|buildOutlook/);
  });

  it('dashboard renders the Outlook surface instead of the "NO SLATE" CTA', () => {
    const src = read('app/dashboard/page.tsx');
    expect(src).toMatch(/OutlookSurface|buildOutlook|mapTomorrowPreview/);
  });
});
