const mockAxiosGet = jest.fn();
jest.mock('axios', () => ({ get: (...args) => mockAxiosGet(...args) }));

const mockCache = { current: new Map() };
jest.mock('../../src/utils/redis', () => ({
  cacheGet: async (k) => mockCache.current.get(k) ?? null,
  cacheSet: async (k, v) => { mockCache.current.set(k, v); return true; },
  cacheDel: async (k) => { mockCache.current.delete(k); return true; },
}));

jest.mock('../../src/utils/rateLimiter', () => ({
  createLimiter: () => ({ waitForToken: async () => true, snapshot: () => ({}) }),
  createCircuitBreaker: () => ({ call: async (fn) => fn(), snapshot: () => ({}) }),
}));

const cache = require('../../src/services/intelligence/teamStatsCache');

beforeEach(() => {
  mockAxiosGet.mockReset();
  mockCache.current.clear();
});

describe('teamStatsCache', () => {
  test('refreshTeamStats walks team list and writes cache per team', async () => {
    mockAxiosGet
      // teams list
      .mockResolvedValueOnce({
        status: 200,
        data: {
          sports: [{ leagues: [{ teams: [
            { team: { id: 1, abbreviation: 'NYK', displayName: 'Knicks' } },
            { team: { id: 2, abbreviation: 'BOS', displayName: 'Celtics' } },
          ] }] }],
        },
      })
      // team 1 stats
      .mockResolvedValueOnce({
        status: 200,
        data: { results: { stats: [{ stats: [
          { name: 'offensiveRating', value: 118.5 },
          { name: 'defensiveRating', value: 110.2 },
          { name: 'pace', value: 100.4 },
        ] }] } },
      })
      // team 2 stats
      .mockResolvedValueOnce({
        status: 200,
        data: { results: { stats: [{ stats: [
          { name: 'offensiveRating', value: 120.0 },
          { name: 'defensiveRating', value: 108.0 },
        ] }] } },
      });

    const summary = await cache.refreshTeamStats('nba');
    expect(summary.captured).toBe(2);
    expect(summary.total).toBe(2);

    const nyk = await cache.getTeamStats('nba', 'NYK');
    expect(nyk).toMatchObject({ offensive_rating: 118.5, defensive_rating: 110.2, pace: 100.4 });

    const bos = await cache.getTeamStats('nba', 'BOS');
    expect(bos).toMatchObject({ offensive_rating: 120.0, defensive_rating: 108.0 });
  });

  test('getOpponentRank returns the normalized 0..1 rank baked at refresh time', async () => {
    // The normalized value is set during refreshTeamStats; reads use it
    // directly. A solo-team cache entry without the field returns null.
    mockCache.current.set('team_stats:nba:NYK', {
      defensive_rating: 110.2,
      defensive_rank_normalized: 0.45,
    });
    expect(await cache.getOpponentRank('nba', 'NYK', 'points')).toBe(0.45);
  });

  test('getOpponentRank returns null when cache predates the normalization upgrade', async () => {
    mockCache.current.set('team_stats:nba:OLD', { defensive_rating: 110.2 });
    expect(await cache.getOpponentRank('nba', 'OLD', 'points')).toBeNull();
  });

  test('refreshTeamStats normalizes defensive_rank_normalized across the league', async () => {
    mockAxiosGet
      .mockResolvedValueOnce({
        status: 200,
        data: {
          sports: [{ leagues: [{ teams: [
            { team: { id: 1, abbreviation: 'BEST', displayName: 'Best D' } },
            { team: { id: 2, abbreviation: 'MID', displayName: 'Middle' } },
            { team: { id: 3, abbreviation: 'WORST', displayName: 'Worst D' } },
          ] }] }],
        },
      })
      .mockResolvedValueOnce({ status: 200, data: { results: { stats: [{ stats: [{ name: 'defensiveRating', value: 105 }] }] } } })
      .mockResolvedValueOnce({ status: 200, data: { results: { stats: [{ stats: [{ name: 'defensiveRating', value: 112 }] }] } } })
      .mockResolvedValueOnce({ status: 200, data: { results: { stats: [{ stats: [{ name: 'defensiveRating', value: 118 }] }] } } });
    await cache.refreshTeamStats('nba');
    expect(await cache.getOpponentRank('nba', 'BEST', 'points')).toBe(0);
    expect(await cache.getOpponentRank('nba', 'MID', 'points')).toBeCloseTo(0.5, 5);
    expect(await cache.getOpponentRank('nba', 'WORST', 'points')).toBe(1);
  });

  test('getTeamStats returns null when nothing cached', async () => {
    expect(await cache.getTeamStats('nba', 'GHOST')).toBeNull();
  });

  test('refreshTeamStats skips unsupported sport gracefully', async () => {
    const summary = await cache.refreshTeamStats('curling');
    // listTeams returns [] for unsupported sport, so total = 0.
    expect(summary).toMatchObject({ captured: 0, errored: 0, total: 0 });
  });
});
