import express from 'express';
import request from 'supertest';
import { beforeEach, describe, expect, it, vi } from 'vitest';

const { poolQuery, getTeamAbbr, formatScoreAbbr } = vi.hoisted(() => ({
  poolQuery: vi.fn(),
  getTeamAbbr: vi.fn(),
  formatScoreAbbr: vi.fn(),
}));

vi.mock('../../db', () => ({ default: { query: poolQuery } }));
vi.mock('../../lib/team-abbreviations', () => ({ getTeamAbbr, formatScoreAbbr }));

async function loadRouter() {
  vi.resetModules();
  return (await import('../stats')).default;
}

describe('/stats contract', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    delete process.env.GRADE_TOTALS_ENABLED;
    getTeamAbbr.mockImplementation((team: string) => team.slice(0, 3).toUpperCase());
    formatScoreAbbr.mockReturnValue('PHX 120 - UTA 101');
  });

  it('returns the public model stats payload with flag off and preserves legacy query shape', async () => {
    process.env.GRADE_TOTALS_ENABLED = 'false';
    const router = await loadRouter();

    poolQuery
      .mockResolvedValueOnce({ rows: [{ total: '10', avg_conf: '0.712' }] })
      .mockResolvedValueOnce({ rows: [{ total_graded: '6', wins: '4', pushes: '1', avg_accuracy: '78.5' }] })
      .mockResolvedValueOnce({ rows: [{ total: '2' }] })
      .mockResolvedValueOnce({ rows: [{ total: '2', wins: '1', losses: '1', pushes: '0' }] })
      .mockResolvedValueOnce({ rows: [{ avg_clv_pct: '1.2', pos_clv_rate: '55.5', first_graded: '2026-03-01T00:00:00.000Z' }] })
      .mockResolvedValueOnce({ rows: [{ league: 'nba', total: '10', avg_conf: '0.712' }] });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/model');

    expect(res.status).toBe(200);
    expect(res.body).toMatchObject({
      totalForecasts: 10,
      totalGraded: 6,
      winRate: 80,
      ungradedResolved: 2,
      roiAvailable: false,
      clvSource: 'player_props',
      clvSourceLabel: 'Closing-line tracked player props',
      benchmarkGames: {
        graded: 6,
        wins: 4,
        losses: 1,
        pushes: 1,
        winRate: 80,
        resolvedAwaitingGrade: 2,
      },
      playerProps: {
        graded: 2,
        wins: 1,
        losses: 1,
        pushes: 0,
        winRate: 50,
        sourceLabel: 'Native graded player props',
      },
      byLeague: { nba: { totalForecasts: 10 } },
    });
    expect(String(poolQuery.mock.calls[1]?.[0])).toContain('FROM rm_forecast_accuracy_v2');
    expect(String(poolQuery.mock.calls[1]?.[0])).not.toContain('forecast_type = \'spread\'');
  });

  it('filters stats queries to spread rows when total grading is enabled', async () => {
    process.env.GRADE_TOTALS_ENABLED = 'true';
    const router = await loadRouter();

    poolQuery
      .mockResolvedValueOnce({ rows: [{ total: '10', avg_conf: '0.712' }] })
      .mockResolvedValueOnce({ rows: [{ total_graded: '6', wins: '4', pushes: '1', avg_accuracy: '78.5' }] })
      .mockResolvedValueOnce({ rows: [{ total: '2' }] })
      .mockResolvedValueOnce({ rows: [{ total: '2', wins: '1', losses: '1', pushes: '0' }] })
      .mockResolvedValueOnce({ rows: [{ avg_clv_pct: '1.2', pos_clv_rate: '55.5', first_graded: '2026-03-01T00:00:00.000Z' }] })
      .mockResolvedValueOnce({ rows: [{ league: 'nba', total: '10', avg_conf: '0.712' }] });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/model');

    expect(res.status).toBe(200);
    expect(String(poolQuery.mock.calls[1]?.[0])).toContain('FROM rm_forecast_accuracy_v2');
    expect(String(poolQuery.mock.calls[1]?.[0])).toContain("(forecast_type = 'spread' OR forecast_type IS NULL)");
  });

  it('includes Rainmaker player props in performance ledger and tier stats', async () => {
    const router = await loadRouter();

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_pick_clv') && query.includes('COUNT(*) as total') && query.includes('clv_sample')) {
        return { rows: [{ total: '2', wins: '1', pushes: '0', avg_clv: '0.5', clv_pos_rate: '50.0', clv_sample: '2', resolved_dates: '1', first_resolved: '2026-03-28', last_resolved: '2026-03-28' }] };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1, 2')) {
        return { rows: [{ league: 'nba', tier: 'B+', total: '2', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1') && query.includes('ORDER BY 2 DESC')) {
        return { rows: [{ league: 'nba', total: '2', wins: '1', pushes: '0', avg_clv: '0.5', clv_pos_rate: '50.0' }] };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1') && query.includes('ORDER BY 1')) {
        return { rows: [{ tier: 'B+', total: '2', wins: '1', pushes: '0' }] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return {
          rows: [{
            event_date: '2026-03-28',
            league: 'nba',
            source_kind: 'props',
            entity_type: 'PLAYER',
            entity_label: 'Devin Booker',
            action_type: 'POINTS',
            action_value: 29.5,
            direction: 'over',
            confidence_tier: 'B+',
            result_value: 31,
            grade: 'W',
            clv_value: 0.5,
            sort_date: '2026-03-28',
            home_team: 'Phoenix Suns',
            away_team: 'Utah Jazz',
            home_score: null,
            away_score: null,
            actual_winner: null,
            odds_data_raw: null,
            forecast_data_raw: null,
            trace_event_id: 'nba-uta-phx-20260328',
            total_rows: 1,
          }],
        };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=props&window=7');

    expect(res.status).toBe(200);
    expect(res.body.summary).toMatchObject({
      totalForecasts: 2,
      winRate: 50,
      pushes: 0,
      avgClv: 0.5,
      clvPosRate: 50,
      sourceLabel: 'Native graded player props',
      roiAvailable: false,
    });
    expect(String(res.body.summary.sourceDetail)).toContain('Uses only rm_pick_clv player props');
    expect(String(res.body.summary.sourceDetail)).toContain('CLV coverage: 2 of 2 graded props.');
    expect(res.body.ledger).toEqual([
      expect.objectContaining({
        entityLabel: 'Devin Booker',
        forecastLine: 'POINTS Over 29.5',
        finalScore: '31 POINTS ✅ (O29.5)',
        grade: 'W',
        league: 'nba',
      }),
    ]);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('FROM rm_pick_clv'))).toBe(true);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('model_confidence'))).toBe(true);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('GROUP BY 1, 2'))).toBe(true);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('FROM "DailyPick" dp'))).toBe(false);
  });

  it('scopes performance summary aggregates to the requested league filter', async () => {
    const router = await loadRouter();

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_pick_clv') && query.includes('COUNT(*) as total') && query.includes('clv_sample')) {
        return { rows: [{ total: '10', wins: '6', pushes: '1', avg_clv: '0.2', clv_pos_rate: '55.0', clv_sample: '8', resolved_dates: '3', first_resolved: '2026-04-01', last_resolved: '2026-04-03' }] };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1, 2')) {
        return {
          rows: [
            { league: 'mlb', tier: 'B+', total: '4', wins: '3', pushes: '0' },
            { league: 'nba', tier: 'B+', total: '6', wins: '3', pushes: '1' },
          ],
        };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1') && query.includes('ORDER BY 2 DESC')) {
        return {
          rows: [
            { league: 'nba', total: '6', wins: '3', pushes: '1', avg_clv: '0.1', clv_pos_rate: '50.0' },
            { league: 'mlb', total: '4', wins: '3', pushes: '0', avg_clv: '0.4', clv_pos_rate: '75.0' },
          ],
        };
      }
      if (query.includes('FROM rm_pick_clv') && query.includes('GROUP BY 1') && query.includes('ORDER BY 1')) {
        return { rows: [{ tier: 'B+', total: '10', wins: '6', pushes: '1' }] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return {
          rows: [{
            event_date: '2026-04-03',
            league: 'mlb',
            source_kind: 'props',
            entity_type: 'PLAYER',
            entity_label: 'Test MLB Prop',
            action_type: 'POINTS',
            action_value: 0.5,
            direction: 'over',
            confidence_tier: 'B+',
            result_value: 1,
            grade: 'W',
            clv_value: 0.4,
            sort_date: '2026-04-03',
            home_team: 'Boston Red Sox',
            away_team: 'New York Yankees',
            home_score: null,
            away_score: null,
            actual_winner: null,
            odds_data_raw: null,
            forecast_data_raw: null,
            trace_event_id: 'mlb-nyy-bos-20260403',
            total_rows: 1,
          }],
        };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=props&window=7&league=mlb');

    expect(res.status).toBe(200);
    expect(res.body.summary).toMatchObject({
      totalForecasts: 4,
      winRate: 75,
      pushes: 0,
      avgClv: 0.4,
      clvPosRate: 75,
    });
    expect(res.body.byTier).toEqual([
      expect.objectContaining({
        tier: 'B+',
        total: 4,
        wins: 3,
        losses: 1,
      }),
    ]);
    expect(res.body.byLeague).toEqual([
      expect.objectContaining({
        league: 'mlb',
        total: 4,
        winRate: 75,
      }),
    ]);
  });

  it('marks ROI unavailable and removes CLV from game-only performance summaries', async () => {
    const router = await loadRouter();

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('COUNT(*) as total') && query.includes('SUM(CASE WHEN final_grade = \'W\'')) {
        return { rows: [{ total: '4', wins: '3', pushes: '1' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1')) {
        return { rows: [{ tier: 'A', total: '4', wins: '3', pushes: '1' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1, 2')) {
        return { rows: [{ league: 'nba', tier: 'A', total: '4', wins: '3', pushes: '1' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('GROUP BY league')) {
        return { rows: [{ league: 'nba', total: '4', wins: '3', pushes: '1' }] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return { rows: [] };
      }
      if (query.includes('SELECT slug FROM rm_blog_posts')) {
        return { rows: [] };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=games&window=7');

    expect(res.status).toBe(200);
    expect(res.body.summary).toMatchObject({
      totalForecasts: 4,
      winRate: 100,
      pushes: 1,
      avgClv: null,
      clvPosRate: null,
      sourceLabel: 'Benchmark-graded team forecasts',
      roiAvailable: false,
    });
    expect(res.body.summary.sourceDetail).toBe('Uses benchmark-graded rm_forecast_accuracy_v2 rows with archived forecast snapshots frozen at publish time. Confidence tiers currently cover all graded games.');
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('COALESCE(fc.confidence_score, 0) >= 0.55'))).toBe(false);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes('COALESCE(af.composite_confidence, af.confidence_score)'))).toBe(true);
    expect(poolQuery.mock.calls.some(([sql]) => String(sql).includes("COALESCE(resolved_at, event_date::timestamp) >= NOW() - ($1::int || ' days')::interval"))).toBe(true);
  });

  it('uses archived team confidence snapshots instead of current cache confidence for game tiers', async () => {
    const router = await loadRouter();

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('COUNT(*) as total') && query.includes('SUM(CASE WHEN final_grade = \'W\'')) {
        return { rows: [{ total: '2', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1') && !query.includes('fa.league')) {
        return { rows: [{ tier: 'A', total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('GROUP BY league')) {
        return { rows: [{ league: 'mlb', total: '2', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1, 2')) {
        return { rows: [{ league: 'mlb', tier: 'A', total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return { rows: [] };
      }
      if (query.includes('SELECT slug FROM rm_blog_posts')) {
        return { rows: [] };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=games&window=30');

    expect(res.status).toBe(200);
    expect(res.body.summary).toMatchObject({
      totalForecasts: 2,
      tieredForecasts: 1,
      untieredForecasts: 1,
      tierCoveragePct: 50,
    });
    expect(String(poolQuery.mock.calls.find(([sql]) => String(sql).includes('FROM rm_forecast_accuracy_v2 fa') && String(sql).includes('GROUP BY 1'))?.[0] || '')).toContain('LEFT JOIN rm_archived_forecasts af ON fa.event_id = af.event_id');
    expect(String(poolQuery.mock.calls.find(([sql]) => String(sql).includes('WITH combined AS ('))?.[0] || '')).toContain("COALESCE(af.winner_pick, fa.predicted_winner) as predicted_winner");
  });

  it('preserves stored legacy grades in the game ledger instead of recomputing them from cache JSON', async () => {
    const router = await loadRouter();

    formatScoreAbbr.mockReturnValue('TOR 6 - ANA 4');

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('COUNT(*) as total') && query.includes('SUM(CASE WHEN final_grade = \'W\'')) {
        return { rows: [{ total: '1', wins: '0', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1')) {
        return { rows: [] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('GROUP BY league')) {
        return { rows: [] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1, 2')) {
        return { rows: [] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return {
          rows: [{
            event_date: '2026-03-30',
            league: 'nhl',
            source_kind: 'games',
            entity_type: 'TEAM',
            entity_label: 'Ducks vs Leafs',
            action_type: 'SPRD',
            action_value: -1.5,
            direction: null,
            confidence_tier: 'A+',
            result_value: -2,
            grade: 'L',
            clv_value: null,
            sort_date: '2026-03-30',
            home_team: 'Anaheim Ducks',
            away_team: 'Toronto Maple Leafs',
            home_score: 4,
            away_score: 6,
            actual_winner: 'Toronto Maple Leafs',
            benchmark_source: 'legacy',
            odds_data_raw: JSON.stringify({
              spread: { home: { line: -1.5, odds: 130 }, away: { line: 1.5, odds: -154 } },
              moneyline: { home: -185, away: 155 },
            }),
            forecast_data_raw: JSON.stringify({
              forecast_side: 'Toronto Maple Leafs',
              winner_pick: 'Anaheim Ducks',
              projected_lines: {
                spread: { home: -1.5, away: 1.5 },
                moneyline: { home: -175, away: 150 },
              },
            }),
            trace_event_id: 'nhl-tor-ana-20260330',
            total_rows: 1,
          }],
        };
      }
      if (query.includes('SELECT slug FROM rm_blog_posts')) {
        return { rows: [] };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=games&window=7');

    expect(res.status).toBe(200);
    expect(res.body.ledger).toEqual([
      expect.objectContaining({
        eventId: 'nhl-tor-ana-20260330',
        grade: 'L',
        forecastLine: 'TOR +1.5 / ML +150',
        closingLine: 'TOR +1.5 (-154) / ML +155',
        finalScore: 'TOR 6 - ANA 4',
      }),
    ]);
  });

  it('falls back to event identity for missing-cache game rows and strips corrupt public moneylines', async () => {
    const router = await loadRouter();

    formatScoreAbbr.mockReturnValue('ATL 5 - NYM 3');

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('COUNT(*) as total') && query.includes('SUM(CASE WHEN final_grade = \'W\'')) {
        return { rows: [{ total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1')) {
        return { rows: [{ tier: 'A', total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('GROUP BY league')) {
        return { rows: [{ league: 'mlb', total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1, 2')) {
        return { rows: [{ league: 'mlb', tier: 'A', total: '1', wins: '1', pushes: '0' }] };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return {
          rows: [{
            event_date: '2026-04-08',
            league: 'mlb',
            source_kind: 'games',
            entity_type: 'TEAM',
            entity_label: 'Braves vs Mets',
            action_type: 'SPRD',
            action_value: -1.5,
            direction: null,
            confidence_tier: null,
            result_value: -2,
            grade: 'W',
            clv_value: null,
            sort_date: '2026-04-08',
            home_team: 'New York Mets',
            away_team: 'Atlanta Braves',
            home_score: 3,
            away_score: 5,
            actual_winner: 'Atlanta Braves',
            benchmark_source: 'closing',
            odds_data_raw: JSON.stringify({
              spread: { away: { line: -1.5, odds: -110 }, home: { line: 1.5, odds: -110 } },
              moneyline: { away: -19463, home: 2548 },
            }),
            forecast_data_raw: JSON.stringify({
              forecast_side: 'Atlanta Braves',
              projected_lines: {
                spread: { away: -1.5, home: 1.5 },
                moneyline: { away: -10000, home: 1800 },
              },
            }),
            event_moneyline_raw: JSON.stringify({ away: null, home: null }),
            event_spread_raw: JSON.stringify({ away: { line: -1.5, odds: -110 }, home: { line: 1.5, odds: -110 } }),
            event_total_raw: JSON.stringify({ over: null, under: null }),
            predicted_winner: 'Atlanta Braves',
            closing_market: -1.5,
            benchmark_forecast: -1.5,
            trace_event_id: 'mlb-atl-nym-20260408',
            total_rows: 1,
          }],
        };
      }
      if (query.includes('SELECT slug FROM rm_blog_posts')) {
        return { rows: [] };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=games&window=7');

    expect(res.status).toBe(200);
    expect(res.body.ledger).toEqual([
      expect.objectContaining({
        eventId: 'mlb-atl-nym-20260408',
        entityLabel: 'NEW vs ATL',
        forecastLine: 'ATL -1.5',
        closingLine: 'ATL -1.5 (-110)',
        finalScore: 'ATL 5 - NYM 3',
      }),
    ]);
    expect(String(poolQuery.mock.calls.find(([sql]) => String(sql).includes('WITH combined AS ('))?.[0] || '')).toContain('LEFT JOIN rm_archived_forecasts af ON fa.event_id = af.event_id');
    expect(String(poolQuery.mock.calls.find(([sql]) => String(sql).includes('WITH combined AS ('))?.[0] || '')).toContain('LEFT JOIN rm_events ev ON fa.event_id = ev.event_id');
    expect(String(poolQuery.mock.calls.find(([sql]) => String(sql).includes('WITH combined AS ('))?.[0] || '')).toContain('COALESCE(af.event_id, fc.event_id, ev.event_id) IS NOT NULL');
  });

  it('reports untiered benchmark coverage gaps in summary and league aggregates', async () => {
    const router = await loadRouter();

    poolQuery.mockImplementation(async (sql: string) => {
      const query = String(sql);
      if (query.includes('FROM rm_forecast_accuracy_v2') && query.includes('COUNT(*) as total') && query.includes('SUM(CASE WHEN final_grade = \'W\'') && !query.includes('GROUP BY')) {
        return { rows: [{ total: '4', wins: '2', pushes: '0' }] };
      }
      if (query.includes('SELECT\n        ') && query.includes('as tier,') && query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1') && !query.includes('fa.league')) {
        return {
          rows: [
            { tier: 'A+', total: '1', wins: '1', pushes: '0' },
            { tier: 'B+', total: '2', wins: '1', pushes: '0' },
          ],
        };
      }
      if (query.includes('SELECT league,') && query.includes('FROM rm_forecast_accuracy_v2') && query.includes('GROUP BY league')) {
        return { rows: [{ league: 'mlb', total: '4', wins: '2', pushes: '0' }] };
      }
      if (query.includes('SELECT fa.league,') && query.includes('FROM rm_forecast_accuracy_v2 fa') && query.includes('GROUP BY 1, 2')) {
        return {
          rows: [
            { league: 'mlb', tier: 'A+', total: '1', wins: '1', pushes: '0' },
            { league: 'mlb', tier: 'B+', total: '2', wins: '1', pushes: '0' },
          ],
        };
      }
      if (query.includes('WITH combined AS (') && query.includes('COUNT(*) OVER()::int as total_rows')) {
        return { rows: [] };
      }
      if (query.includes('SELECT slug FROM rm_blog_posts')) {
        return { rows: [] };
      }
      return { rows: [] };
    });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/performance?kind=games&window=7');

    expect(res.status).toBe(200);
    expect(res.body.summary).toMatchObject({
      totalForecasts: 4,
      tieredForecasts: 3,
      untieredForecasts: 1,
      tierCoveragePct: 75,
    });
    expect(res.body.summary.sourceDetail).toContain('75% of graded games');
    expect(res.body.summary.sourceDetail).toContain('1 graded forecast remains untiered');
    expect(res.body.summary.sourceDetail).toContain('archived public confidence snapshot is missing');
    expect(res.body.byLeague).toEqual([
      expect.objectContaining({
        league: 'mlb',
        total: 4,
        aPlusSampleN: 1,
        tieredForecasts: 3,
        untieredForecasts: 1,
        tierCoveragePct: 75,
      }),
    ]);
  });

  it('uses composite confidence for upcoming tiers and suppresses highlight-only prop counts', async () => {
    const router = await loadRouter();

    poolQuery
      .mockResolvedValueOnce({
        rows: [{
          event_id: 'evt-1',
          league: 'mlb',
          home_team: 'Boston Red Sox',
          away_team: 'New York Yankees',
          public_confidence: '0.88',
          starts_at: '2026-03-30T23:00:00.000Z',
          forecast_data: {
            prop_highlights: [
              { player: 'Aaron Judge', prop: 'HR', recommendation: 'over', reasoning: 'edge' },
            ],
            winner_pick: 'New York Yankees',
          },
          ready_prop_count: 0,
          event_status: 'scheduled',
          home_short: 'BOS',
          away_short: 'NYY',
          spread: null,
          moneyline: null,
          total: null,
        }],
      })
      .mockResolvedValueOnce({ rows: [] });

    const app = express();
    app.use('/', router);
    const res = await request(app).get('/upcoming');

    expect(res.status).toBe(200);
    expect(res.body.total).toBe(1);
    expect(res.body.forecasts[0]).toMatchObject({
      confidenceScore: 0.88,
      confidenceTier: 'A+',
      forecastSummary: 'NEW',
      propHighlights: [],
    });
    expect(String(poolQuery.mock.calls[0]?.[0])).toContain('COALESCE(fc.composite_confidence, fc.confidence_score) as public_confidence');
    expect(String(poolQuery.mock.calls[0]?.[0])).toContain('ready_prop_count');
    expect(String(poolQuery.mock.calls[0]?.[0])).toContain('fc.starts_at > NOW()');
    expect(String(poolQuery.mock.calls[0]?.[0])).toContain("LOWER(COALESCE(e.status, 'scheduled')) NOT IN ('ended', 'final', 'complete', 'completed', 'closed', 'cancelled', 'canceled')");
  });
});
