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

import { __mlbPropMarketInternals } from '../mlb-prop-markets';
import { NormalizedPlayerPropMarket } from '../mlb-market-normalizer';

function makeMarket(overrides: Partial<NormalizedPlayerPropMarket>): NormalizedPlayerPropMarket {
  return {
    eventId: 'evt-1',
    startsAt: '2026-03-28T19:00:00.000Z',
    homeTeam: 'LAD',
    awayTeam: 'SEA',
    playerId: 'SHOHEI_OHTANI_1_MLB',
    playerName: 'Shohei Ohtani',
    teamShort: null,
    statType: 'batting_hits',
    normalizedStatType: 'batting_hits',
    line: 1.5,
    marketName: 'Shohei Ohtani Hits Over/Under',
    over: {
      odds: -110,
      impliedProb: 0.5238,
      source: 'fanduel',
      provider: 'sportsgameodds',
      sportsbookName: 'fanduel',
      sportsbookId: 'fanduel',
      timestamp: '2026-03-28T18:58:00.000Z',
      rawMarketId: 'over-1',
    },
    under: {
      odds: -105,
      impliedProb: 0.5122,
      source: 'fanduel',
      provider: 'sportsgameodds',
      sportsbookName: 'fanduel',
      sportsbookId: 'fanduel',
      timestamp: '2026-03-28T18:58:00.000Z',
      rawMarketId: 'under-1',
    },
    primarySource: 'fanduel',
    completionMethod: 'source',
    completenessStatus: 'source_complete',
    sourceMap: [],
    isGapFilled: false,
    availableSides: ['over', 'under'],
    rawMarketCount: 2,
    updatedAt: '2026-03-28T18:58:00.000Z',
    ...overrides,
  };
}

describe('MLB prop candidate integration', () => {
  it('passes single-sided priced markets into forecast candidates', () => {
    const candidates = __mlbPropMarketInternals.mapNormalizedMarketsToCandidates(
      [
        makeMarket({ completenessStatus: 'source_complete' }),
        makeMarket({
          playerId: 'MIKE_TROUT_1_MLB',
          playerName: 'Mike Trout',
          completenessStatus: 'incomplete',
          completionMethod: 'none',
          under: null,
          availableSides: ['over'],
        }),
      ],
      new Set(['shohei ohtani', 'mike trout']),
      null,
      'LAD',
    );

    expect(candidates).toHaveLength(2);
    expect(candidates[0]).toMatchObject({
      player: 'Mike Trout',
      teamShort: 'LAD',
      marketLineValue: 1.5,
      overOdds: -110,
      underOdds: null,
      completenessStatus: 'incomplete',
      availableSides: ['over'],
    });
    expect(candidates[1]).toMatchObject({
      player: 'Shohei Ohtani',
      teamShort: 'LAD',
      marketLineValue: 1.5,
      overOdds: -110,
      underOdds: -105,
      completenessStatus: 'source_complete',
    });
  });

  it('suppresses low-value under-only hitter 0.5 markets before candidate scoring', () => {
    const candidates = __mlbPropMarketInternals.mapNormalizedMarketsToCandidates(
      [
        makeMarket({
          playerId: 'CARSON_WILLIAMS_1_MLB',
          playerName: 'Carson Williams',
          statType: 'batting_hits',
          normalizedStatType: 'batting_hits',
          line: 0.5,
          marketName: 'Carson Williams Hits Over/Under',
          over: null,
          under: {
            odds: -290,
            impliedProb: 0.7436,
            source: 'fanduel',
            provider: 'sportsgameodds',
            sportsbookName: 'fanduel',
            sportsbookId: 'fanduel',
            timestamp: '2026-03-28T18:58:00.000Z',
            rawMarketId: 'under-only-1',
          },
          completenessStatus: 'incomplete',
          completionMethod: 'none',
          availableSides: ['under'],
        }),
      ],
      new Set(['carson williams']),
      null,
      'TB',
    );

    expect(candidates).toHaveLength(0);
  });

  it('suppresses one-sided hitter under combo and fantasy markets that the publish scorer already treats as junk', () => {
    const candidates = __mlbPropMarketInternals.mapNormalizedMarketsToCandidates(
      [
        makeMarket({
          playerId: 'CHANDLER_SIMPSON_1_MLB',
          playerName: 'Chandler Simpson',
          statType: 'batting_totalBases',
          normalizedStatType: 'batting_totalBases',
          line: 1.5,
          marketName: 'Chandler Simpson Total Bases Over/Under',
          over: null,
          under: {
            odds: -425,
            impliedProb: 0.8095,
            source: 'fanduel',
            provider: 'sportsgameodds',
            sportsbookName: 'fanduel',
            sportsbookId: 'fanduel',
            timestamp: '2026-03-28T18:58:00.000Z',
            rawMarketId: 'under-only-total-bases',
          },
          completenessStatus: 'incomplete',
          completionMethod: 'none',
          availableSides: ['under'],
        }),
        makeMarket({
          playerId: 'ISAAC_PAREDES_1_MLB',
          playerName: 'Isaac Paredes',
          statType: 'fantasyScore',
          normalizedStatType: 'fantasyScore',
          line: 7.5,
          marketName: 'Isaac Paredes Fantasy Score Over/Under',
          over: null,
          under: {
            odds: -160,
            impliedProb: 0.6154,
            source: 'fanduel',
            provider: 'sportsgameodds',
            sportsbookName: 'fanduel',
            sportsbookId: 'fanduel',
            timestamp: '2026-03-28T18:58:00.000Z',
            rawMarketId: 'under-only-fantasy',
          },
          completenessStatus: 'incomplete',
          completionMethod: 'none',
          availableSides: ['under'],
        }),
      ],
      new Set(['chandler simpson', 'isaac paredes']),
      null,
      'TB',
    );

    expect(candidates).toHaveLength(0);
  });

  it('respects confirmed lineup filtering for batters', () => {
    const candidates = __mlbPropMarketInternals.mapNormalizedMarketsToCandidates(
      [makeMarket({ completenessStatus: 'multi_source_complete', completionMethod: 'multi_source', isGapFilled: true })],
      new Set(['shohei ohtani']),
      new Set(['mookie betts']),
      'LAD',
    );

    expect(candidates).toHaveLength(0);
  });

  it('sanitizes dirty pitcher market names before roster validation', () => {
    const candidates = __mlbPropMarketInternals.mapNormalizedMarketsToCandidates(
      [
        makeMarket({
          playerId: 'JOE_BOYLE_1_MLB',
          playerName: 'Joe Boyle Outs Over/Under',
          statType: 'pitching_outs',
          normalizedStatType: 'pitching_outs',
          line: 15.5,
          marketName: 'Joe Boyle Outs Over/Under',
          over: null,
          under: {
            odds: -135,
            impliedProb: 0.5745,
            source: 'fanduel',
            provider: 'sportsgameodds',
            sportsbookName: 'fanduel',
            sportsbookId: 'fanduel',
            timestamp: '2026-03-28T18:58:00.000Z',
            rawMarketId: 'pitcher-under-1',
          },
          completenessStatus: 'incomplete',
          completionMethod: 'none',
          availableSides: ['under'],
        }),
      ],
      new Set(['joe boyle']),
      null,
      'TB',
    );

    expect(candidates).toHaveLength(1);
    expect(candidates[0]).toMatchObject({
      player: 'Joe Boyle',
      statType: 'pitching_outs',
      marketLineValue: 15.5,
      underOdds: -135,
    });
  });

  it('accepts common MLB abbreviation aliases for roster/team resolution', async () => {
    const { __mlbPropMarketInternals } = await import('../mlb-prop-markets');
    expect(__mlbPropMarketInternals.getMlbTeamShortVariants('ARI')).toEqual(['ARI', 'AZ']);
    expect(__mlbPropMarketInternals.getMlbTeamShortVariants('AZ')).toEqual(['AZ', 'ARI']);
    expect(__mlbPropMarketInternals.getMlbTeamShortVariants('LAD')).toEqual(['LAD']);
  });

  it('fills a missing MLB market side from a matching The Odds event snapshot', () => {
    const merged = __mlbPropMarketInternals.mergeMlbMarketWithTheOddsSnapshot(
      makeMarket({
        playerId: 'SHOHEI_OHTANI_1_MLB',
        playerName: 'Shohei Ohtani',
        statType: 'batting_hits',
        normalizedStatType: 'batting_hits',
        line: 1.5,
        over: {
          odds: -110,
          impliedProb: 0.5238,
          source: 'draftkings',
          provider: 'sportsgameodds',
          sportsbookName: 'draftkings',
          sportsbookId: 'draftkings',
          timestamp: '2026-03-28T18:58:00.000Z',
          rawMarketId: 'over-only-1',
        },
        under: null,
        primarySource: 'draftkings',
        completionMethod: 'none',
        completenessStatus: 'incomplete',
        availableSides: ['over'],
        sourceMap: [{
          side: 'over',
          source: 'draftkings',
          odds: -110,
          impliedProb: 0.5238,
          provider: 'sportsgameodds',
          sportsbookName: 'draftkings',
          sportsbookId: 'draftkings',
          timestamp: '2026-03-28T18:58:00.000Z',
          rawMarketId: 'over-only-1',
        }],
        rawMarketCount: 1,
      }),
      {
        timestamp: '2026-03-28T19:00:00.000Z',
        eventId: 'evt-1',
        sportKey: 'baseball_mlb',
        commenceTime: '2026-03-28T19:00:00.000Z',
        homeTeam: 'Los Angeles Dodgers',
        awayTeam: 'Seattle Mariners',
        bookmakers: [{
          key: 'fanduel',
          title: 'FanDuel',
          markets: [{
            key: 'batter_hits',
            outcomes: [
              { name: 'Under', description: 'Shohei Ohtani', price: -112, point: 1.5 },
              { name: 'Over', description: 'Shohei Ohtani', price: -108, point: 1.5 },
            ],
          }],
        }],
      },
    );

    expect(merged).toMatchObject({
      completionMethod: 'multi_source',
      completenessStatus: 'multi_source_complete',
      isGapFilled: true,
      availableSides: ['over', 'under'],
      over: expect.objectContaining({ odds: -110, source: 'draftkings' }),
      under: expect.objectContaining({ odds: -112, source: 'fanduel', provider: 'theodds' }),
    });
    expect(merged.sourceMap).toEqual(expect.arrayContaining([
      expect.objectContaining({ side: 'over', source: 'draftkings' }),
      expect.objectContaining({ side: 'under', source: 'fanduel', provider: 'theodds' }),
    ]));
  });
});
