// Unit: provider preference + source tracking in getOdds (Session 30).

const mockRedis = { get: jest.fn(), set: jest.fn(), hset: jest.fn(), hgetall: jest.fn(), expire: jest.fn() };
jest.mock('../../src/utils/redis', () => ({
  getRedisClient: () => mockRedis,
  cacheGet: jest.fn(async () => null),
  cacheSet: jest.fn(async () => true),
  cacheDel: jest.fn(async () => true),
  isDegraded: jest.fn(() => true), // gateway/quota fail open in tests
}));

jest.mock('axios');
const axios = require('axios');

// PropLine adapter mock — we drive hasKeys + getProps per test.
jest.mock('../../src/services/adapters/proplineAdapter', () => ({
  hasKeys: jest.fn(),
  getProps: jest.fn(),
}));
const propline = require('../../src/services/adapters/proplineAdapter');

process.env.ODDS_API_KEY = 'test-api-key';
const { getOdds } = require('../../src/services/oddsService');

beforeEach(() => {
  jest.clearAllMocks();
  mockRedis.get.mockResolvedValue(null);   // cache miss
  mockRedis.set.mockResolvedValue('OK');
  mockRedis.hgetall.mockResolvedValue({});
});

describe('getOdds — PropLine preferred', () => {
  test('serves from PropLine when it returns props (provider=propline, no odds-api call)', async () => {
    propline.hasKeys.mockReturnValue(true);
    propline.getProps.mockResolvedValue({
      props: [{ player: 'Acuna', stat_type: 'hits', line: 0.5, over_odds: -200, under_odds: 160, book: 'betmgm' }],
      spreads: [],
      source: 'propline',
    });

    const res = await getOdds('mlb');
    expect(res.source).toBe('live');
    expect(res.provider).toBe('propline');
    expect(res.props).toHaveLength(1);
    expect(axios.get).not.toHaveBeenCalled(); // odds-api never touched
    // Cached payload carries the provider tag.
    const cachedArg = JSON.parse(mockRedis.set.mock.calls[0][1]);
    expect(cachedArg.provider).toBe('propline');
  });

  test('falls back to odds-api when PropLine returns empty (provider=odds-api)', async () => {
    propline.hasKeys.mockReturnValue(true);
    propline.getProps.mockResolvedValue({ props: [], spreads: [], source: 'propline' });

    // odds-api fallback: events list, then per-event odds.
    axios.get
      .mockResolvedValueOnce({ data: [{ id: 'e1', home_team: 'Denver Nuggets', away_team: 'Los Angeles Lakers', commence_time: '2026-03-21T19:00:00Z' }], headers: {} })
      .mockResolvedValueOnce({
        data: {
          id: 'e1', home_team: 'Denver Nuggets', away_team: 'Los Angeles Lakers', commence_time: '2026-03-21T19:00:00Z',
          bookmakers: [{ key: 'draftkings', title: 'DK', markets: [{ key: 'player_points', last_update: 't', outcomes: [
            { name: 'Over', description: 'Jokic', price: -110, point: 26.5 },
            { name: 'Under', description: 'Jokic', price: -110, point: 26.5 },
          ] }] }],
        },
        headers: { 'x-requests-remaining': '400' },
      });

    const res = await getOdds('nba');
    expect(res.provider).toBe('odds-api');
    expect(axios.get).toHaveBeenCalled();
    expect(res.props.length).toBeGreaterThan(0);
  });

  test('PropLine skipped entirely when no keys (provider=odds-api)', async () => {
    propline.hasKeys.mockReturnValue(false);
    axios.get
      .mockResolvedValueOnce({ data: [{ id: 'e1', home_team: 'Denver Nuggets', away_team: 'Los Angeles Lakers', commence_time: '2026-03-21T19:00:00Z' }], headers: {} })
      .mockResolvedValueOnce({
        data: { id: 'e1', home_team: 'Denver Nuggets', away_team: 'Los Angeles Lakers', commence_time: '2026-03-21T19:00:00Z',
          bookmakers: [{ key: 'draftkings', title: 'DK', markets: [{ key: 'player_points', last_update: 't', outcomes: [
            { name: 'Over', description: 'Jokic', price: -110, point: 26.5 },
            { name: 'Under', description: 'Jokic', price: -110, point: 26.5 },
          ] }] }] },
        headers: {},
      });
    const res = await getOdds('nba');
    expect(propline.getProps).not.toHaveBeenCalled();
    expect(res.provider).toBe('odds-api');
  });

  test('PropLine error → graceful fallback to odds-api', async () => {
    propline.hasKeys.mockReturnValue(true);
    propline.getProps.mockRejectedValue(new Error('propline down'));
    axios.get
      .mockResolvedValueOnce({ data: [], headers: {} }); // empty events → empty props, but provider tagged
    const res = await getOdds('nba');
    expect(res.provider).toBe('odds-api');
  });

  test('cached response surfaces the stored provider', async () => {
    mockRedis.get.mockResolvedValue(JSON.stringify({ updated_at: 't', props: [{ player: 'X' }], spreads: [], provider: 'propline' }));
    const res = await getOdds('mlb');
    expect(res.source).toBe('cache');
    expect(res.provider).toBe('propline');
  });
});
