const mockSnaps = { current: [] };
jest.mock('../../src/utils/supabase', () => ({
  getSupabaseServiceClient: () => ({
    from() {
      const proxy = {
        select() { return proxy; },
        eq() { return proxy; },
        order() { return Promise.resolve({ data: mockSnaps.current, error: null }); },
      };
      return proxy;
    },
  }),
}));

const lm = require('../../src/services/intelligence/lineMovement');

beforeEach(() => {
  mockSnaps.current = [];
});

describe('getLineMovement', () => {
  test('returns null when fewer than two snapshots', async () => {
    mockSnaps.current = [{ line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' }];
    expect(await lm.getLineMovement('g', 'P', 'points')).toBeNull();
  });

  test('reports opening and current line correctly', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 25.5, over_odds: -120, under_odds: +100, snapshot_at: 't2' },
      { line: 26.0, over_odds: -130, under_odds: +110, snapshot_at: 't3' },
    ];
    const result = await lm.getLineMovement('g', 'P', 'points');
    expect(result).toMatchObject({
      opening_line: 25.5,
      current_line: 26.0,
      movement: 0.5,
      direction: 'up',
      snapshots_count: 3,
    });
  });
});

describe('reverseLineMovement', () => {
  test('detects RLM when public is on over but line moves to under', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 24.5, over_odds: -130, under_odds: +110, snapshot_at: 't2' },
    ];
    const r = await lm.reverseLineMovement('g', 'P', 'points');
    expect(r.isReverse).toBe(true);
    expect(r.score).toBeGreaterThan(0);
    expect(r.publicSide).toBe('over');
    expect(r.lineDirection).toBe('under');
  });

  test('returns isReverse=false when line moved with public', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 26.5, over_odds: -130, under_odds: +110, snapshot_at: 't2' },
    ];
    const r = await lm.reverseLineMovement('g', 'P', 'points');
    expect(r.isReverse).toBe(false);
    expect(r.score).toBe(0);
  });

  test('returns null on flat movement', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 25.5, over_odds: -115, under_odds: -105, snapshot_at: 't2' },
    ];
    expect(await lm.reverseLineMovement('g', 'P', 'points')).toBeNull();
  });

  test('uses provided publicBetPct over odds-direction heuristic', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 26.5, over_odds: -110, under_odds: -110, snapshot_at: 't2' },
    ];
    const r = await lm.reverseLineMovement('g', 'P', 'points', 30);
    expect(r.isReverse).toBe(true);
    expect(r.publicSide).toBe('under');
    expect(r.lineDirection).toBe('over');
  });
});

describe('juiceDegradation', () => {
  test('flags juice change when the line itself stayed put', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 25.5, over_odds: -130, under_odds: +110, snapshot_at: 't2' },
    ];
    const r = await lm.juiceDegradation('g', 'P', 'points');
    expect(r.applicable).toBe(true);
    expect(r.score).toBeGreaterThan(0);
  });

  test('not applicable when line moved significantly', async () => {
    mockSnaps.current = [
      { line: 25.5, over_odds: -110, under_odds: -110, snapshot_at: 't1' },
      { line: 28.5, over_odds: -120, under_odds: +100, snapshot_at: 't2' },
    ];
    const r = await lm.juiceDegradation('g', 'P', 'points');
    expect(r.applicable).toBe(false);
  });
});
