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

const { registerSignal, collectSignals, computeIntelligence } = vi.hoisted(() => ({
  registerSignal: vi.fn(),
  collectSignals: vi.fn(),
  computeIntelligence: vi.fn(),
}));

vi.mock('../rie/signal-registry', () => ({
  registerSignal,
  getSignalRegistry: vi.fn(),
}));

vi.mock('../rie/signal-collector', () => ({ collectSignals }));
vi.mock('../rie/composite-engine', () => ({ computeIntelligence }));
vi.mock('../rie/rag-client', () => ({ queryRag: vi.fn() }));
vi.mock('../rie/signals/piff-signal', () => ({ piffSignal: { signalId: 'piff' } }));
vi.mock('../rie/signals/digimon-signal', () => ({ digimonSignal: { signalId: 'digimon' } }));
vi.mock('../rie/signals/dvp-signal', () => ({ dvpSignal: { signalId: 'dvp' } }));
vi.mock('../rie/signals/kenpom-signal', () => ({ kenpomSignal: { signalId: 'kenpom' } }));
vi.mock('../rie/signals/corner-scout-signal', () => ({ cornerScoutSignal: { signalId: 'corner_scout' } }));
vi.mock('../rie/signals/rag-signal', () => ({ ragSignal: { signalId: 'rag' } }));
vi.mock('../rie/signals/fangraphs-signal', () => ({ fangraphsSignal: { signalId: 'fangraphs' } }));
vi.mock('../rie/signals/mlb-matchup-signal', () => ({ mlbMatchupSignal: { signalId: 'mlb_matchup' } }));
vi.mock('../rie/signals/mlb-phase-signal', () => ({ mlbPhaseSignal: { signalId: 'mlb_phase' } }));

describe('rie/index', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.resetModules();
    delete process.env.FORECAST_PAYLOAD_V2;
  });

  it('registers signals once and builds intelligence', async () => {
    const { buildIntelligence } = await import('../rie');
    collectSignals.mockResolvedValue([{ signalId: 'rag', available: true, rawData: { insights: [{ title: 'Angle' }] } }]);
    computeIntelligence.mockReturnValue({
      compositeConfidence: 0.81,
      stormCategory: 5,
      signals: [{ signalId: 'rag', available: true, rawData: { insights: [{ title: 'Angle' }] } }],
      edgeBreakdown: { grok: { score: 0.8, weight: 1 } },
      inputQuality: { piff: 'A', dvp: 'B', digimon: 'N/A', odds: 'A', overall: 'A' },
      strategyProfile: { league: 'nba' },
      ragInsights: [{ title: 'Angle' }],
      compositeVersion: 'rm_2.0',
    });

    const result = await buildIntelligence({
      event: {}, league: 'nba', homeTeam: 'Lakers', awayTeam: 'Celtics', homeShort: 'LAL', awayShort: 'BOS', startsAt: '2026-03-27T19:00:00.000Z', eventId: 'evt1', grokConfidence: 0.8, grokValueRating: 8,
    });

    expect(registerSignal).toHaveBeenCalledTimes(9);
    expect(collectSignals).toHaveBeenCalledTimes(1);
    expect(result.compositeConfidence).toBe(0.81);
  });

  it('keeps additive payload fields hidden when FORECAST_PAYLOAD_V2 is off', async () => {
    process.env.FORECAST_PAYLOAD_V2 = 'false';
    const { mapToLegacyComposite } = await import('../rie');

    const result = mapToLegacyComposite({
      compositeConfidence: 0.79,
      stormCategory: 4,
      signals: [
        { signalId: 'piff', available: true, rawData: { avgEdge: 8.1, legCount: 4, topPicks: [{ name: 'A', stat: 'PTS', line: 20.5, edge: 8.1 }] } },
      ],
      edgeBreakdown: {
        grok: { score: 0.74, weight: 0.5 },
        piff: { score: 0.84, weight: 0.5 },
      },
      inputQuality: { piff: 'A', dvp: 'B', digimon: 'N/A', odds: 'A', overall: 'A' },
      strategyProfile: { league: 'nba' },
      ragInsights: [{ query: 'pace', topChunks: [], applicableTheory: 'tempo', cached: true }],
      compositeVersion: 'rm_2.0',
    } as any, 0.74, 8);

    expect(result.rieSignals).toBeUndefined();
    expect(result.ragInsights).toBeUndefined();
    expect(result.strategyId).toBeUndefined();
  });

  it('maps intelligence output back to the legacy composite shape with additive fields when enabled', async () => {
    process.env.FORECAST_PAYLOAD_V2 = 'true';
    const { mapToLegacyComposite } = await import('../rie');

    const result = mapToLegacyComposite({
      compositeConfidence: 0.79,
      stormCategory: 4,
      signals: [
        { signalId: 'piff', available: true, rawData: { avgEdge: 8.1, legCount: 4, topPicks: [{ name: 'A', stat: 'PTS', line: 20.5, edge: 8.1 }, { name: 'B', stat: 'REB', line: 9.5, edge: 7.2 }, { name: 'C', stat: 'AST', line: 6.5, edge: 6.4 }, { name: 'D', stat: '3PM', line: 2.5, edge: 6.1 }] } },
        { signalId: 'digimon', available: false, rawData: null },
        { signalId: 'dvp', available: false, rawData: null },
      ],
      edgeBreakdown: {
        grok: { score: 0.74, weight: 0.5 },
        piff: { score: 0.84, weight: 0.5 },
      },
      inputQuality: { piff: 'A', dvp: 'B', digimon: 'N/A', odds: 'A', overall: 'A' },
      strategyProfile: { league: 'nba' },
      ragInsights: [],
      compositeVersion: 'rm_2.0',
    } as any, 0.74, 8);

    expect(result.modelSignals.grok?.confidence).toBe(0.74);
    expect(result.modelSignals.piff?.topPicks).toHaveLength(3);
    expect(result.modelSignals.digimon).toBeNull();
    expect(result.rieSignals).toHaveLength(3);
    expect(result.ragInsights).toEqual([]);
    expect(result.strategyId).toBe('nba');
  });

  it('reconstructs NBA legacy confidence from serialized edgeBreakdown including hidden weighted signals', async () => {
    const { mapToLegacyComposite, reconstructLegacyCompositeConfidence } = await import('../rie');

    const result = mapToLegacyComposite({
      compositeConfidence: 0.907,
      stormCategory: 5,
      signals: [
        { signalId: 'piff', available: false, rawData: null },
        { signalId: 'digimon', available: false, rawData: null },
        { signalId: 'rag', available: true, rawData: { chunkCount: 3 } },
      ],
      edgeBreakdown: {
        grok: { score: 1, weight: 0.811, contribution: 0.811 },
        rag: { score: 0.508, weight: 0.189, contribution: 0.096 },
        piff: { score: 0.5, weight: 0, contribution: 0 },
        digimon: { score: 0.5, weight: 0, contribution: 0 },
      },
      inputQuality: { piff: 'C', dvp: 'N/A', digimon: 'N/A', odds: 'A', rag: 'B', overall: 'B' },
      strategyProfile: { league: 'nba' },
      ragInsights: [],
      compositeVersion: 'rm_2.0',
    } as any, 1, 8);

    expect((result.edgeBreakdown as any).ragScore).toBe(0.508);
    expect((result.edgeBreakdown as any).ragContribution).toBe(0.096);
    expect(reconstructLegacyCompositeConfidence(result)).toBe(result.compositeConfidence);
    expect(result.compositeConfidence).toBe(0.907);
  });

  it('reconstructs NCAAB legacy confidence from serialized edgeBreakdown with explicit adjustment contribution', async () => {
    const { mapToLegacyComposite, reconstructLegacyCompositeConfidence } = await import('../rie');

    const result = mapToLegacyComposite({
      compositeConfidence: 0.694,
      stormCategory: 3,
      signals: [
        { signalId: 'kenpom', available: true, rawData: { predictedMOV: 7.5 } },
        { signalId: 'rag', available: true, rawData: { chunkCount: 2 } },
      ],
      edgeBreakdown: {
        grok: { score: 0.68, weight: 0.421, contribution: 0.286 },
        kenpom: { score: 0.726, weight: 0.474, contribution: 0.344 },
        rag: { score: 0.505, weight: 0.105, contribution: 0.053 },
      },
      inputQuality: { piff: 'N/A', dvp: 'N/A', digimon: 'N/A', odds: 'A', rag: 'B', overall: 'B' },
      strategyProfile: { league: 'ncaab' },
      ragInsights: [],
      compositeVersion: 'rm_2.0',
    } as any, 0.68, 7);

    expect(result.edgeBreakdown.kenpomScore).toBe(0.726);
    expect((result.edgeBreakdown as any).ragScore).toBe(0.505);
    expect((result.edgeBreakdown as any).adjustmentContribution).toBe(0.011);
    expect(reconstructLegacyCompositeConfidence(result)).toBe(result.compositeConfidence);
    expect(result.compositeConfidence).toBe(0.694);
  });
});
