// Session 57 (Phase 0) — GET /api/snapshot/summary: the HeartbeatBar's honest
// data source. Real graded counts + latest pipeline run time from cache-only
// Redis reads; must never invent numbers on a cold cache.

const express = require('express');
const request = require('supertest');

const store = {};
jest.mock('../../src/utils/redis', () => ({
  cacheGet: jest.fn(async (k) => (k in store ? store[k] : null)),
  cacheSet: jest.fn(async (k, v) => { store[k] = v; return true; }),
}));

function mountApp() {
  delete require.cache[require.resolve('../../src/routes/snapshot')];
  const snapshotRoutes = require('../../src/routes/snapshot');
  const app = express();
  app.use('/api/snapshot', snapshotRoutes);
  return app;
}

beforeEach(() => {
  for (const k of Object.keys(store)) delete store[k];
  jest.clearAllMocks();
});

describe('GET /api/snapshot/summary', () => {
  test('sums real graded counts across sports and reports the latest run', async () => {
    store['snapshot:mlb:latest'] = {
      updated_at: '2026-07-10T18:00:00.000Z',
      grades: [{ player: 'A' }, { player: 'B' }, { player: 'C' }],
    };
    store['snapshot:wnba:latest'] = {
      updated_at: '2026-07-10T19:00:00.000Z',
      grades: [{ player: 'D' }],
    };
    const res = await request(mountApp()).get('/api/snapshot/summary');
    expect(res.status).toBe(200);
    expect(res.body.graded).toBe(4);
    expect(res.body.updated_at).toBe('2026-07-10T19:00:00.000Z'); // latest run
    expect(res.body.sports.mlb).toBe(3);
    expect(res.body.sports.wnba).toBe(1);
    expect(res.body.sports.nba).toBe(0); // off-season → honest zero
  });

  test('refreshed_at is the freshness heartbeat, distinct from the grade-lock updated_at', async () => {
    // updated_at = grade LOCK (5×/day, stable). refreshed_at = last intraday
    // line refresh (~20 min). The SYNC badge measures against refreshed_at —
    // measuring the 20-min cadence against the lock field caused "STALE 8h".
    store['snapshot:mlb:latest'] = {
      updated_at: '2026-07-10T18:00:00.000Z',       // graded at 2pm ET
      refreshed_at: '2026-07-10T21:40:00.000Z',     // lines refreshed at 5:40pm ET
      grades: [{ player: 'A' }],
    };
    const res = await request(mountApp()).get('/api/snapshot/summary');
    expect(res.body.updated_at).toBe('2026-07-10T18:00:00.000Z');
    expect(res.body.refreshed_at).toBe('2026-07-10T21:40:00.000Z');
  });

  test('refreshed_at falls back to updated_at when a snapshot predates the field', async () => {
    // Old snapshots written before this fix have no refreshed_at — the freshness
    // signal must still resolve (never null while a grade lock exists).
    store['snapshot:mlb:latest'] = { updated_at: '2026-07-10T18:00:00.000Z', grades: [{ player: 'A' }] };
    const res = await request(mountApp()).get('/api/snapshot/summary');
    expect(res.body.refreshed_at).toBe('2026-07-10T18:00:00.000Z');
  });

  test('falls back to the grades:{sport} envelope when no snapshot exists', async () => {
    store['grades:mlb'] = { updated_at: '2026-07-10T16:00:00.000Z', grades: [{ player: 'A' }, { player: 'B' }] };
    const res = await request(mountApp()).get('/api/snapshot/summary');
    expect(res.body.graded).toBe(2);
    expect(res.body.updated_at).toBe('2026-07-10T16:00:00.000Z');
  });

  test('cold cache → zeros and null timestamp, never invented numbers', async () => {
    const res = await request(mountApp()).get('/api/snapshot/summary');
    expect(res.status).toBe(200);
    expect(res.body.graded).toBe(0);
    expect(res.body.updated_at).toBeNull();
  });

  test('is not captured by the /:sport route (summary ≠ a sport)', async () => {
    store['snapshot:mlb:latest'] = { updated_at: 'x', grades: [{ player: 'A' }] };
    const res = await request(mountApp()).get('/api/snapshot/summary');
    // The /:sport handler would return { sport, grades, deltas }.
    expect(res.body.sport).toBeUndefined();
    expect(res.body.grades).toBeUndefined();
    expect(typeof res.body.graded).toBe('number');
  });
});
