const pe = require('../../src/services/intelligence/probabilityEstimator');

function logsAt(values) {
  return values.map((v) => ({ points: v }));
}

describe('probabilityEstimator.estimateProbability', () => {
  test('30-pt scorer with 25.5 line → p_over > 0.70', () => {
    const r = pe.estimateProbability({
      gameLogs: logsAt([32, 28, 31, 27, 33, 29, 30, 26, 35, 28]),
      line: 25.5,
      statType: 'points',
    });
    expect(r.p_over).toBeGreaterThan(0.70);
    expect(r.p_under).toBeCloseTo(1 - r.p_over, 5);
  });

  test('20-pt scorer with 25.5 line → p_over < 0.40', () => {
    const r = pe.estimateProbability({
      gameLogs: logsAt([18, 22, 19, 21, 20, 17, 23, 19, 18, 22]),
      line: 25.5,
      statType: 'points',
    });
    expect(r.p_over).toBeLessThan(0.40);
  });

  test('home game bumps p_over by ~0.015', () => {
    const base = pe.estimateProbability({
      gameLogs: logsAt([28, 26, 27, 25, 24]),
      line: 25.5,
      statType: 'points',
      features: {},
    });
    const home = pe.estimateProbability({
      gameLogs: logsAt([28, 26, 27, 25, 24]),
      line: 25.5,
      statType: 'points',
      features: { home_away: 1.0 },
    });
    expect(home.p_over - base.p_over).toBeCloseTo(0.015, 5);
  });

  test('weak opponent (rank >= 0.70) adds ~0.03', () => {
    const base = pe.estimateProbability({
      gameLogs: logsAt([26, 25, 27, 24, 26]),
      line: 25.5,
      statType: 'points',
    });
    const weakOpp = pe.estimateProbability({
      gameLogs: logsAt([26, 25, 27, 24, 26]),
      line: 25.5,
      statType: 'points',
      features: { opp_rank_stat: 0.85 },
    });
    expect(weakOpp.p_over - base.p_over).toBeCloseTo(0.03, 5);
  });

  test('top-defense opponent (rank <= 0.30) subtracts ~0.03', () => {
    const base = pe.estimateProbability({
      gameLogs: logsAt([26, 25, 27, 24, 26]),
      line: 25.5,
      statType: 'points',
    });
    const stiff = pe.estimateProbability({
      gameLogs: logsAt([26, 25, 27, 24, 26]),
      line: 25.5,
      statType: 'points',
      features: { opp_rank_stat: 0.1 },
    });
    expect(base.p_over - stiff.p_over).toBeCloseTo(0.03, 5);
  });

  test('volatile player (high cv) gets pulled toward 0.50', () => {
    // Use a 4/5 sample so base p ≈ 0.8 (well below the 0.95 ceiling)
    // — that way the pull-toward-0.5 has visible room to move.
    const stable = pe.estimateProbability({
      gameLogs: logsAt([28, 24, 29, 27, 30]),
      line: 25.5,
      statType: 'points',
      features: { l10_stddev: 1.0, l20_avg: 27.0 },  // cv ~0.04
    });
    const wild = pe.estimateProbability({
      gameLogs: logsAt([28, 24, 29, 27, 30]),
      line: 25.5,
      statType: 'points',
      features: { l10_stddev: 14.0, l20_avg: 27.0 },  // cv = 0.5, volatile
    });
    expect(wild.p_over).toBeLessThan(stable.p_over);
  });

  test('clamps to [0.10, 0.95]', () => {
    const ceil = pe.estimateProbability({
      gameLogs: logsAt([50, 48, 52, 49, 51]),
      line: 10,
      statType: 'points',
      features: { opp_rank_stat: 0.95, home_away: 1.0 },
    });
    const floor = pe.estimateProbability({
      gameLogs: logsAt([5, 6, 4, 7, 5]),
      line: 30,
      statType: 'points',
      features: { opp_rank_stat: 0.05, home_away: 0.0 },
    });
    expect(ceil.p_over).toBeLessThanOrEqual(0.95);
    expect(floor.p_over).toBeGreaterThanOrEqual(0.10);
  });

  test('fewer than 5 games uses all available for recency', () => {
    const r = pe.estimateProbability({
      gameLogs: logsAt([30, 28]),
      line: 25.5,
      statType: 'points',
    });
    expect(r.p_over).toBeGreaterThan(0.5);
  });

  test('returns nulls on empty input', () => {
    const r = pe.estimateProbability({ gameLogs: [], line: 25.5, statType: 'points' });
    expect(r.p_over).toBeNull();
    expect(r.reason).toBe('insufficient_data');
  });

  test('all-push sample returns reason all_pushes', () => {
    const r = pe.estimateProbability({
      gameLogs: logsAt([25.5, 25.5, 25.5]),
      line: 25.5,
      statType: 'points',
    });
    expect(r.p_over).toBeNull();
    expect(r.reason).toBe('all_pushes');
  });
});
