import { describe, expect, it } from 'vitest';

import { normalizeGroupedMlbMarkets } from '../mlb-market-gapfill';
import { groupRawMlbMarketSources, RawMlbMarketSource } from '../mlb-market-normalizer';
import { summarizeNormalizedMlbMarkets } from '../mlb-market-reporter';

function makeSource(overrides: Partial<RawMlbMarketSource>): RawMlbMarketSource {
  return {
    eventId: 'evt-1',
    startsAt: '2026-03-28T19:00:00.000Z',
    homeTeam: 'LAD',
    awayTeam: 'SEA',
    playerId: 'SHOHEI_OHTANI_1_MLB',
    playerName: 'Shohei Ohtani',
    statType: 'batting_hits',
    normalizedStatType: 'batting_hits',
    line: 1.5,
    side: 'over',
    odds: -110,
    impliedProb: 0.5238,
    provider: 'sportsgameodds',
    source: 'fanduel',
    sportsbookName: 'fanduel',
    sportsbookId: 'fanduel',
    timestamp: '2026-03-28T18:58:00.000Z',
    rawMarketId: 'market-1',
    marketName: 'Shohei Ohtani Hits Over/Under',
    teamShort: null,
    ...overrides,
  };
}

describe('MLB market completion', () => {
  it('flags over-only markets as incomplete', () => {
    const [market] = normalizeGroupedMlbMarkets(groupRawMlbMarketSources([
      makeSource({ side: 'over', source: 'consensus', sportsbookName: 'consensus', sportsbookId: 'consensus' }),
    ]));

    expect(market.completenessStatus).toBe('incomplete');
    expect(market.over?.source).toBe('consensus');
    expect(market.under).toBeNull();
    expect(market.availableSides).toEqual(['over']);
  });

  it('marks same-source paired markets as source_complete', () => {
    const [market] = normalizeGroupedMlbMarkets(groupRawMlbMarketSources([
      makeSource({ side: 'over', source: 'fanduel', rawMarketId: 'fd-over' }),
      makeSource({ side: 'under', source: 'fanduel', odds: -105, impliedProb: 0.5122, rawMarketId: 'fd-under' }),
    ]));

    expect(market.completenessStatus).toBe('source_complete');
    expect(market.completionMethod).toBe('source');
    expect(market.isGapFilled).toBe(false);
    expect(market.primarySource).toBe('fanduel');
  });

  it('gap-fills missing sides from secondary sources and preserves attribution', () => {
    const [market] = normalizeGroupedMlbMarkets(groupRawMlbMarketSources([
      makeSource({ side: 'over', source: 'consensus', sportsbookName: 'consensus', sportsbookId: 'consensus', rawMarketId: 'consensus-over' }),
      makeSource({ side: 'under', source: 'draftkings', sportsbookName: 'draftkings', sportsbookId: 'draftkings', odds: -120, impliedProb: 0.5454, rawMarketId: 'dk-under' }),
    ]));

    expect(market.completenessStatus).toBe('multi_source_complete');
    expect(market.completionMethod).toBe('multi_source');
    expect(market.isGapFilled).toBe(true);
    expect(market.over?.source).toBe('consensus');
    expect(market.under?.source).toBe('draftkings');
    expect(market.sourceMap).toEqual([
      expect.objectContaining({ side: 'over', source: 'consensus', rawMarketId: 'consensus-over' }),
      expect.objectContaining({ side: 'under', source: 'draftkings', rawMarketId: 'dk-under' }),
    ]);
  });

  it('does not merge conflicting lines', () => {
    const groups = groupRawMlbMarketSources([
      makeSource({ side: 'over', line: 1.5, rawMarketId: 'line-1.5-over' }),
      makeSource({ side: 'under', line: 2.5, rawMarketId: 'line-2.5-under', odds: +110, impliedProb: 0.4761 }),
    ]);

    expect(groups).toHaveLength(2);
    const normalized = normalizeGroupedMlbMarkets(groups);
    expect(normalized.map((market) => market.line)).toEqual([1.5, 2.5]);
    expect(normalized.every((market) => market.completenessStatus === 'incomplete')).toBe(true);
  });

  it('builds event-level telemetry for under-only, two-way, and zero-gap-fill shape', () => {
    const summary = summarizeNormalizedMlbMarkets(normalizeGroupedMlbMarkets(groupRawMlbMarketSources([
      makeSource({ eventId: 'evt-1', side: 'under', source: 'consensus', sportsbookName: 'consensus', sportsbookId: 'consensus', rawMarketId: 'evt-1-under' }),
      makeSource({ eventId: 'evt-1', playerId: 'MOOKIE_BETTS_1_MLB', playerName: 'Mookie Betts', side: 'over', rawMarketId: 'evt-1-over-fd' }),
      makeSource({ eventId: 'evt-1', playerId: 'MOOKIE_BETTS_1_MLB', playerName: 'Mookie Betts', side: 'under', source: 'draftkings', sportsbookName: 'draftkings', sportsbookId: 'draftkings', odds: -105, impliedProb: 0.5122, rawMarketId: 'evt-1-under-dk' }),
      makeSource({ eventId: 'evt-2', homeTeam: 'CHC', awayTeam: 'STL', playerId: 'SEIYA_SUZUKI_1_MLB', playerName: 'Seiya Suzuki', side: 'under', source: 'consensus', sportsbookName: 'consensus', sportsbookId: 'consensus', rawMarketId: 'evt-2-under' }),
    ])));

    expect(summary.totalMarkets).toBe(3);
    expect(summary.underOnlyMarkets).toBe(2);
    expect(summary.twoWayMarkets).toBe(1);
    expect(summary.gapFilled).toBe(1);
    expect(summary.zeroGapFillEvents).toBe(1);
    expect(summary.eventBreakdown).toEqual([
      expect.objectContaining({
        eventId: 'evt-2',
        underOnlyMarkets: 1,
        twoWayMarkets: 0,
        gapFilledMarkets: 0,
        zeroGapFillEvent: true,
      }),
      expect.objectContaining({
        eventId: 'evt-1',
        underOnlyMarkets: 1,
        twoWayMarkets: 1,
        gapFilledMarkets: 1,
        zeroGapFillEvent: false,
      }),
    ]);
  });
});
