// Wave 1 trust bug — MLB namesake collision. Two different players can share an
// EXACT nameKey ("James Wood": the Nationals star + a Cubs-affiliate namesake).
// The old `.find` took the first → wrong currentTeam → wrong opponents → "built
// vs AL East" fabrication in streaks/rosterlogs. Doctrine: never guess among
// namesakes — resolve only with a confident team hint, else refuse (null).
//
// Hermetic: `searchPlayer` accepts injected `people`/`teams` fixtures — no
// network, no redis.

const mlb = require('../../src/services/adapters/mlbStatsAdapter');
const { teamRecordMatchesHint, disambiguateByHint, canonAbbr } = mlb.__internals;

// statsapi-shaped fixtures.
const JUDGE = {
  id: 592450, fullName: 'Aaron Judge',
  currentTeam: { id: 147, name: 'New York Yankees' },
  primaryPosition: { abbreviation: 'RF' },
};
const WOOD_NATIONALS = {
  id: 691026, fullName: 'James Wood',
  currentTeam: { id: 120, name: 'Washington Nationals' },
  primaryPosition: { abbreviation: 'LF' },
};
const WOOD_CUBS = {
  id: 999999, fullName: 'James Wood',
  currentTeam: { id: 112, name: 'Chicago Cubs' },
  primaryPosition: { abbreviation: 'P' },
};
const SOTO_METS = {
  id: 665742, fullName: 'Juan Soto',
  currentTeam: { id: 121, name: 'New York Mets' },
  primaryPosition: { abbreviation: 'RF' },
};

const TEAMS = [
  { id: 147, abbr: 'NYY', name: 'New York Yankees' },
  { id: 120, abbr: 'WSH', name: 'Washington Nationals' },
  { id: 112, abbr: 'CHC', name: 'Chicago Cubs' },
  { id: 146, abbr: 'MIA', name: 'Miami Marlins' },
  { id: 121, abbr: 'NYM', name: 'New York Mets' },
  { id: 119, abbr: 'LAD', name: 'Los Angeles Dodgers' },
  { id: 135, abbr: 'SD', name: 'San Diego Padres' },
];

describe('searchPlayer — namesake disambiguation (never guess)', () => {
  test('(a) a single unambiguous name resolves (Aaron Judge → Yankees)', async () => {
    const r = await mlb.searchPlayer('Aaron Judge', 2026, { people: [JUDGE], teams: TEAMS });
    expect(r).not.toBeNull();
    expect(r.id).toBe(592450);
    expect(r.team).toBe('New York Yankees');
  });

  test('(b) two "James Wood" with NO hint → null (NEVER the Cubs one)', async () => {
    const r = await mlb.searchPlayer('James Wood', 2026, { people: [WOOD_NATIONALS, WOOD_CUBS], teams: TEAMS });
    expect(r).toBeNull(); // refuse — honest absent beats wrong
  });

  test('(c) two "James Wood" + a Nationals-game hint → the Nationals Wood', async () => {
    const r = await mlb.searchPlayer('James Wood', 2026, {
      people: [WOOD_CUBS, WOOD_NATIONALS], // Cubs first — the old bug picked this
      teams: TEAMS,
      teamHint: ['WSH', 'MIA'], // Nationals @ Marlins
    });
    expect(r).not.toBeNull();
    expect(r.id).toBe(691026);
    expect(r.team).toBe('Washington Nationals');
    expect(r.team).not.toBe('Chicago Cubs');
  });

  test('(c2) a full-team-name hint disambiguates too', async () => {
    const r = await mlb.searchPlayer('James Wood', 2026, {
      people: [WOOD_CUBS, WOOD_NATIONALS],
      teams: TEAMS,
      teamHint: ['Washington Nationals', 'Miami Marlins'],
    });
    expect(r.id).toBe(691026);
  });

  test('(c3) two namesakes + a hint matching NEITHER → null (still refuse)', async () => {
    const r = await mlb.searchPlayer('James Wood', 2026, {
      people: [WOOD_NATIONALS, WOOD_CUBS],
      teams: TEAMS,
      teamHint: ['LAD', 'SD'],
    });
    expect(r).toBeNull();
  });
});

// JOIN INVARIANT (streaks/rosterlogs) — a resolved player's team must be a
// participant of the prop's game; on mismatch the team is DROPPED, never a
// foreign tag. This is the mechanism snapshotService relies on before it writes
// teamByPlayer + rosterlogs opponents.
describe('searchPlayer — team join invariant (drop, never tag foreign)', () => {
  test('a single player whose team is NOT in the hinted game → team dropped to null', async () => {
    const r = await mlb.searchPlayer('Juan Soto', 2026, {
      people: [SOTO_METS],
      teams: TEAMS,
      teamHint: ['LAD', 'SD'], // Soto (Mets) is in neither → cannot confirm join
    });
    expect(r).not.toBeNull();     // still the right player by name
    expect(r.id).toBe(665742);
    expect(r.team).toBeNull();    // but his team is not tagged onto a foreign game
    expect(r.teamId).toBeNull();
  });

  test('a single player whose team IS in the hinted game keeps its team', async () => {
    const r = await mlb.searchPlayer('Juan Soto', 2026, {
      people: [SOTO_METS],
      teams: TEAMS,
      teamHint: ['NYM', 'LAD'], // Mets @ Dodgers → confirmed
    });
    expect(r.team).toBe('New York Mets');
  });

  test('with NO hint, a single player keeps its authoritative team', async () => {
    const r = await mlb.searchPlayer('Juan Soto', 2026, { people: [SOTO_METS], teams: TEAMS });
    expect(r.team).toBe('New York Mets');
  });
});

describe('pure helpers — abbr reconciliation + hint matching', () => {
  test('ESPN↔statsapi abbr aliases canonicalize (AZ↔ARI, CHW↔CWS)', () => {
    expect(canonAbbr('AZ')).toBe(canonAbbr('ARI'));
    expect(canonAbbr('CHW')).toBe(canonAbbr('CWS'));
    expect(canonAbbr('nyy')).toBe('NYY');
  });

  test('teamRecordMatchesHint matches by abbr via the teams list', () => {
    expect(teamRecordMatchesHint({ id: 120, name: 'Washington Nationals' }, ['WSH'], TEAMS)).toBe(true);
    expect(teamRecordMatchesHint({ id: 120, name: 'Washington Nationals' }, ['CHC'], TEAMS)).toBe(false);
  });

  test('teamRecordMatchesHint matches by partial name', () => {
    expect(teamRecordMatchesHint({ id: 120, name: 'Washington Nationals' }, ['Nationals'], TEAMS)).toBe(true);
  });

  test('disambiguateByHint refuses when 2 candidates share the hinted team', () => {
    const dupe = [WOOD_NATIONALS, { ...WOOD_CUBS, currentTeam: { id: 120, name: 'Washington Nationals' } }];
    expect(disambiguateByHint(dupe, ['WSH'], TEAMS)).toBeNull();
  });
});
