/**
 * RIE Signal: MLB Matchup Model
 *
 * Dedicated MLB signal built from:
 * - Bill James matchup model (park factors, runs created, log5, starter ERC)
 * - 2026 Steamer team projections (offense + pitching)
 * - ESPN probable starters mapped to FanGraphs starter profiles
 *
 * This is the baseball-specific structural signal for the live RIE path.
 * Probable starters are not yet reliably available in the feed, so this
 * model uses team-level projections and park context until a starter feed
 * is linked.
 */

import { Signal, SignalResult, MatchupContext } from '../types';
import {
  getTeamBattingProjection,
  getTeamPitchingProjection,
  getStarterProfile,
} from '../../fangraphs';
import { getBillJamesMatchup } from '../../bill-james';
import { getMlbProbableStarters } from '../../espn-mlb';

function clamp(val: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, val));
}

function normalize(val: number, lo: number, hi: number): number {
  return clamp((val - lo) / (hi - lo), 0, 1);
}

function getProjectionSeason(): number {
  return new Date().getFullYear();
}

export const mlbMatchupSignal: Signal = {
  id: 'mlb_matchup',
  name: 'MLB Matchup Model',
  supportedLeagues: ['mlb'],

  async collect(ctx: MatchupContext): Promise<SignalResult> {
    const start = Date.now();
    try {
      const season = getProjectionSeason();
      const statsSeason = new Date(ctx.startsAt || Date.now()).getMonth() < 3 ? season - 1 : season;
      const homeShort = ctx.homeShort?.toUpperCase() || '';
      const awayShort = ctx.awayShort?.toUpperCase() || '';

      if (!homeShort || !awayShort) {
        return {
          signalId: 'mlb_matchup',
          score: 0.5,
          weight: 0,
          available: false,
          rawData: { error: 'No team abbreviations' },
          metadata: { latencyMs: Date.now() - start, source: 'db', freshness: '' },
        };
      }

      const probables = await getMlbProbableStarters({
        homeShort,
        awayShort,
        startsAt: ctx.startsAt,
      });

      const homeStarterName = probables?.homeStarter?.name || null;
      const awayStarterName = probables?.awayStarter?.name || null;

      const [homeStarter, awayStarter] = await Promise.all([
        homeStarterName ? getStarterProfile(homeStarterName, homeShort, statsSeason) : Promise.resolve(null),
        awayStarterName ? getStarterProfile(awayStarterName, awayShort, statsSeason) : Promise.resolve(null),
      ]);

      const [billJames, homeBatProj, awayBatProj, homePitProj, awayPitProj] = await Promise.all([
        getBillJamesMatchup(homeShort, awayShort, season, homeStarterName || undefined, awayStarterName || undefined),
        getTeamBattingProjection(homeShort, season),
        getTeamBattingProjection(awayShort, season),
        getTeamPitchingProjection(homeShort, season),
        getTeamPitchingProjection(awayShort, season),
      ]);

      let log5Score = 0.5;
      if (billJames.log5?.homeWinProbHFA != null) {
        log5Score = clamp(billJames.log5.homeWinProbHFA, 0, 1);
      }

      let rcScore = 0.5;
      if (billJames.homeRC && billJames.awayRC) {
        rcScore = normalize(billJames.homeRC.rcParkAdj - billJames.awayRC.rcParkAdj, -2.5, 2.5);
      }

      let projOffenseScore = 0.5;
      if (homeBatProj && awayBatProj) {
        projOffenseScore = normalize(homeBatProj.projWrcPlus - awayBatProj.projWrcPlus, -30, 30);
      }

      let projPitchingScore = 0.5;
      if (homePitProj && awayPitProj) {
        projPitchingScore = normalize(awayPitProj.projFip - homePitProj.projFip, -1.5, 1.5);
      }

      let starterScore = 0.5;
      if (homeStarter && awayStarter) {
        const starterDiff =
          ((awayStarter.fip - homeStarter.fip) * 0.6) +
          ((awayStarter.xfip - homeStarter.xfip) * 0.3) +
          ((homeStarter.kBbPct - awayStarter.kBbPct) * 2.0 * 0.1);
        starterScore = normalize(starterDiff, -1.8, 1.8);
      } else if (billJames.homeStarterERC && billJames.awayStarterERC) {
        starterScore = normalize(
          billJames.awayStarterERC.componentERA - billJames.homeStarterERC.componentERA,
          -1.8,
          1.8
        );
      }

      const parkRunFactor = billJames.park?.runs ?? 1.0;
      const parkAdjustment = clamp((parkRunFactor - 1.0) * 0.08, -0.04, 0.04);

      const score = clamp(
        (log5Score * 0.30) +
        (rcScore * 0.15) +
        (projOffenseScore * 0.20) +
        (projPitchingScore * 0.15) +
        (starterScore * 0.20) +
        parkAdjustment,
        0,
        1
      );

      const hasData = !!(
        billJames.log5 ||
        (billJames.homeRC && billJames.awayRC) ||
        (homeBatProj && awayBatProj) ||
        (homePitProj && awayPitProj)
      );

      return {
        signalId: 'mlb_matchup',
        score,
        weight: 0,
        available: hasData,
        rawData: {
          season,
          park: billJames.park ? {
            team: billJames.park.team,
            runs: billJames.park.runs,
            hr: billJames.park.hr,
          } : null,
          pythagorean: billJames.homePyth && billJames.awayPyth ? {
            home: {
              winPct: billJames.homePyth.pythWinPct,
              runDifferential: billJames.homePyth.runDifferential,
              strengthRating: billJames.homePyth.strengthRating,
            },
            away: {
              winPct: billJames.awayPyth.pythWinPct,
              runDifferential: billJames.awayPyth.runDifferential,
              strengthRating: billJames.awayPyth.strengthRating,
            },
          } : null,
          log5: billJames.log5 ? {
            homeWinProbHFA: billJames.log5.homeWinProbHFA,
            impliedSpread: billJames.log5.impliedSpread,
            strengthDiff: billJames.log5.strengthDiff,
          } : null,
          runsCreated: billJames.homeRC && billJames.awayRC ? {
            home: billJames.homeRC.rcParkAdj,
            away: billJames.awayRC.rcParkAdj,
          } : null,
          projections: {
            homeBat: homeBatProj ? {
              projWrcPlus: homeBatProj.projWrcPlus,
              projOps: homeBatProj.projOps,
              projKPct: homeBatProj.projKPct,
            } : null,
            awayBat: awayBatProj ? {
              projWrcPlus: awayBatProj.projWrcPlus,
              projOps: awayBatProj.projOps,
              projKPct: awayBatProj.projKPct,
            } : null,
            homePit: homePitProj ? {
              projEra: homePitProj.projEra,
              projFip: homePitProj.projFip,
              projWhip: homePitProj.projWhip,
            } : null,
            awayPit: awayPitProj ? {
              projEra: awayPitProj.projEra,
              projFip: awayPitProj.projFip,
              projWhip: awayPitProj.projWhip,
            } : null,
          },
          starters: {
            source: probables?.eventId ? 'espn' : 'none',
            eventId: probables?.eventId || null,
            home: homeStarterName ? {
              name: homeStarterName,
              espnEra: probables?.homeStarter?.era ?? null,
              record: probables?.homeStarter?.record ?? null,
              fangraphs: homeStarter ? {
                fip: homeStarter.fip,
                xfip: homeStarter.xfip,
                era: homeStarter.era,
                kBbPct: homeStarter.kBbPct,
              } : null,
              componentEra: billJames.homeStarterERC ? {
                componentERA: billJames.homeStarterERC.componentERA,
                regression: billJames.homeStarterERC.regression,
                flag: billJames.homeStarterERC.flag,
              } : null,
            } : null,
            away: awayStarterName ? {
              name: awayStarterName,
              espnEra: probables?.awayStarter?.era ?? null,
              record: probables?.awayStarter?.record ?? null,
              fangraphs: awayStarter ? {
                fip: awayStarter.fip,
                xfip: awayStarter.xfip,
                era: awayStarter.era,
                kBbPct: awayStarter.kBbPct,
              } : null,
              componentEra: billJames.awayStarterERC ? {
                componentERA: billJames.awayStarterERC.componentERA,
                regression: billJames.awayStarterERC.regression,
                flag: billJames.awayStarterERC.flag,
              } : null,
            } : null,
          },
          starterStatus: homeStarterName && awayStarterName ? 'espn-linked' : 'unavailable',
        },
        metadata: {
          latencyMs: Date.now() - start,
          source: 'db',
          freshness: new Date().toISOString().slice(0, 10),
        },
      };
    } catch (err) {
      return {
        signalId: 'mlb_matchup',
        score: 0.5,
        weight: 0,
        available: false,
        rawData: { error: String(err) },
        metadata: { latencyMs: Date.now() - start, source: 'db', freshness: '' },
      };
    }
  },
};
