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

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

const adapter = require('../../src/services/adapters/tank01NbaAdapter');

beforeEach(() => {
  mockAxiosGet.mockReset();
  mockCacheStore.clear();
  mockCacheTtls.clear();
});

describe('tank01NbaAdapter', () => {
  describe('graceful degradation (no RAPID_API_KEY)', () => {
    const original = process.env.RAPID_API_KEY;
    beforeAll(() => { delete process.env.RAPID_API_KEY; });
    afterAll(() => { if (original !== undefined) process.env.RAPID_API_KEY = original; });

    test('hasApiKey false', () => {
      expect(adapter.hasApiKey()).toBe(false);
    });

    test('all endpoints return null without touching axios', async () => {
      expect(await adapter.getNBABoxScore('20260611_LAL_BOS')).toBeNull();
      expect(await adapter.getNBAGamesForDate('20260611')).toBeNull();
      expect(await adapter.getNBABettingOdds('20260611')).toBeNull();
      expect(mockAxiosGet).not.toHaveBeenCalled();
    });
  });

  describe('with key configured', () => {
    beforeAll(() => { process.env.RAPID_API_KEY = 'test-rapid-key'; });

    test('RapidAPI auth headers wired correctly', async () => {
      mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
      await adapter.getNBAGamesForDate('20260611');
      const [url, opts] = mockAxiosGet.mock.calls[0];
      expect(url).toMatch(/^https:\/\/tank01-fantasy-stats\.p\.rapidapi\.com/);
      expect(opts.headers['x-rapidapi-key']).toBe('test-rapid-key');
      expect(opts.headers['x-rapidapi-host']).toBe('tank01-fantasy-stats.p.rapidapi.com');
    });

    test('TANK01_NBA_HOST override is honored', async () => {
      const original = process.env.TANK01_NBA_HOST;
      process.env.TANK01_NBA_HOST = 'alt-tank01.rapidapi.com';
      mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
      await adapter.getNBAGamesForDate('20260611');
      const [url, opts] = mockAxiosGet.mock.calls[0];
      expect(url).toMatch(/^https:\/\/alt-tank01\.rapidapi\.com/);
      expect(opts.headers['x-rapidapi-host']).toBe('alt-tank01.rapidapi.com');
      if (original !== undefined) process.env.TANK01_NBA_HOST = original;
      else delete process.env.TANK01_NBA_HOST;
    });

    test('getNBABoxScore projects playerStats map into a flat list', async () => {
      mockAxiosGet.mockResolvedValueOnce({
        data: {
          body: {
            gameStatus: 'InProgress',
            playerStats: {
              'NBA-123': { longName: 'Jaylen Brown', teamAbv: 'BOS', mins: '34', pts: 27, reb: 5, ast: 4 },
              'NBA-456': { longName: 'Jayson Tatum',  teamAbv: 'BOS', mins: '36', pts: 31, reb: 8, ast: 6, tptfgm: 5 },
            },
          },
        },
      });
      const stats = await adapter.getNBABoxScore('20260611_LAL_BOS');
      expect(stats).toHaveLength(2);
      const tatum = stats.find((p) => p.name === 'Jayson Tatum');
      expect(tatum.team).toBe('BOS');
      expect(tatum.pts).toBe(31);
      expect(tatum.threes).toBe(5);
      expect(tatum._final).toBe(false);
    });

    test('Final game upgrades the cache TTL to 24h', async () => {
      mockAxiosGet.mockResolvedValueOnce({
        data: { body: { gameStatus: 'Final', playerStats: {} } },
      });
      await adapter.getNBABoxScore('GAME-1');
      const ttl = mockCacheTtls.get('tank01:nba:boxscore:GAME-1');
      expect(ttl).toBe(adapter.__internals.TTL.boxScoreFinal);
    });

    test('In-progress game stays on 5-min TTL', async () => {
      mockAxiosGet.mockResolvedValueOnce({
        data: { body: { gameStatus: 'InProgress', playerStats: {} } },
      });
      await adapter.getNBABoxScore('GAME-2');
      const ttl = mockCacheTtls.get('tank01:nba:boxscore:GAME-2');
      expect(ttl).toBe(adapter.__internals.TTL.boxScoreLive);
    });

    test('getNBAGamesForDate strips dashes from ISO dates', async () => {
      mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
      await adapter.getNBAGamesForDate('2026-06-11');
      const [url] = mockAxiosGet.mock.calls[0];
      expect(url).toMatch(/gameDate=20260611/);
    });

    test('getNBAGamesForDate projects to stable shape', async () => {
      mockAxiosGet.mockResolvedValueOnce({
        data: { body: [
          { gameID: '20260611_BOS_LAL', home: 'LAL', away: 'BOS', gameTime: '7:30p ET', gameStatus: 'Final', homePts: 110, awayPts: 105 },
        ] },
      });
      const games = await adapter.getNBAGamesForDate('20260611');
      expect(games[0]).toMatchObject({
        gameId: '20260611_BOS_LAL', homeTeam: 'LAL', awayTeam: 'BOS', homeScore: 110, awayScore: 105, gameStatus: 'Final',
      });
    });

    test('null IDs return null without touching axios', async () => {
      expect(await adapter.getNBABoxScore(null)).toBeNull();
      expect(await adapter.getNBAGamesForDate(null)).toBeNull();
      expect(await adapter.getNBABettingOdds(null)).toBeNull();
      expect(mockAxiosGet).not.toHaveBeenCalled();
    });

    test('axios error → null + stale fallback', async () => {
      mockCacheStore.set('tank01:nba:games:20260611:stale', { body: [{ gameID: 'STALE' }] });
      mockAxiosGet.mockRejectedValueOnce(new Error('upstream 503'));
      const games = await adapter.getNBAGamesForDate('20260611');
      expect(games).toHaveLength(1);
      expect(games[0].gameId).toBe('STALE');
    });

    test('cache hit on repeat call (axios not re-invoked)', async () => {
      mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
      await adapter.getNBAGamesForDate('20260611');
      await adapter.getNBAGamesForDate('20260611');
      expect(mockAxiosGet).toHaveBeenCalledTimes(1);
    });
  });
});
