/**
 * RIE Signal: FanGraphs Advanced MLB Analytics
 *
 * Computes a 0-1 confidence score from FanGraphs sabermetric data:
 *   - Team wRC+ differential (offensive quality)
 *   - Probable starter FIP/xFIP comparison when ESPN starters are available
 *   - Statcast edge (EV, barrel% differential)
 *   - Recent form (last-14-day splits)
 *
 * Only supports MLB.
 */

import { Signal, SignalResult, MatchupContext } from '../types';
import {
  getTeamBatting,
  getTeamPitching,
  getStarterProfile,
  type TeamBattingProfile,
} from '../../fangraphs';
import { getMlbProbableStarters } from '../../espn-mlb';

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

/** Normalize a value from [lo, hi] → [0, 1] */
function normalize(val: number, lo: number, hi: number): number {
  return clamp((val - lo) / (hi - lo), 0, 1);
}

function getCurrentSeason(): number {
  const now = new Date();
  if (now.getMonth() < 3 || (now.getMonth() === 2 && now.getDate() < 25)) {
    return now.getFullYear() - 1;
  }
  return now.getFullYear();
}

function getStatsSeason(startsAt: string): number {
  const dt = new Date(startsAt || Date.now());
  if (dt.getMonth() < 3 || (dt.getMonth() === 2 && dt.getDate() < 25)) {
    return dt.getFullYear() - 1;
  }
  return dt.getFullYear();
}

/**
 * Compute FanGraphs composite score.
 *
 * Score > 0.5 = home advantage, < 0.5 = away advantage.
 * Weights: offense 35%, pitching 35%, statcast 15%, form 15%
 */
function computeScore(
  homeBat: TeamBattingProfile | null,
  awayBat: TeamBattingProfile | null,
  homePit: { era: number; fip: number; xfip: number; kPct: number; bbPct: number; whip: number; ev: number; barrelPct: number } | null,
  awayPit: { era: number; fip: number; xfip: number; kPct: number; bbPct: number; whip: number; ev: number; barrelPct: number } | null,
  homeBatRecent: TeamBattingProfile | null,
  awayBatRecent: TeamBattingProfile | null,
): number {
  let offScore = 0.5;
  let pitScore = 0.5;
  let statcastScore = 0.5;
  let formScore = 0.5;
  let totalWeight = 0;

  // Offense: wRC+ differential
  if (homeBat && awayBat && homeBat.wrcPlus > 0 && awayBat.wrcPlus > 0) {
    const wrcDiff = homeBat.wrcPlus - awayBat.wrcPlus;
    // +30 wRC+ = dominant edge, scale: -30 to +30 → 0 to 1
    offScore = normalize(wrcDiff, -30, 30);
    totalWeight += 0.35;
  }

  // Pitching: FIP differential (lower = better, so invert)
  if (homePit && awayPit && homePit.fip > 0 && awayPit.fip > 0) {
    const fipDiff = awayPit.fip - homePit.fip; // positive = home pitcher is better
    // +1.5 FIP gap = dominant, scale: -1.5 to +1.5 → 0 to 1
    pitScore = normalize(fipDiff, -1.5, 1.5);
    totalWeight += 0.35;
  }

  // Statcast: EV + Barrel% differential
  if (homeBat && awayBat && homeBat.ev > 0 && awayBat.ev > 0) {
    const evDiff = homeBat.ev - awayBat.ev;
    const barrelDiff = homeBat.barrelPct - awayBat.barrelPct;
    const evScore = normalize(evDiff, -3, 3);
    const barrelScore = normalize(barrelDiff, -0.05, 0.05);
    statcastScore = evScore * 0.6 + barrelScore * 0.4;
    totalWeight += 0.15;
  }

  // Recent form: last-14-day wRC+ edge
  if (homeBatRecent && awayBatRecent && homeBatRecent.wrcPlus > 0 && awayBatRecent.wrcPlus > 0) {
    const recentDiff = homeBatRecent.wrcPlus - awayBatRecent.wrcPlus;
    formScore = normalize(recentDiff, -40, 40);
    totalWeight += 0.15;
  }

  if (totalWeight === 0) return 0.5;

  // Weighted average
  const raw = (
    (offScore * 0.35) +
    (pitScore * 0.35) +
    (statcastScore * 0.15) +
    (formScore * 0.15)
  ) / (0.35 + 0.35 + 0.15 + 0.15);

  // Regress toward 0.5 based on data completeness
  const confidence = clamp(totalWeight / 1.0, 0.5, 1.0);
  return 0.5 + (raw - 0.5) * confidence;
}

export const fangraphsSignal: Signal = {
  id: 'fangraphs',
  name: 'FanGraphs Advanced Analytics',
  supportedLeagues: ['mlb'],

  async collect(ctx: MatchupContext): Promise<SignalResult> {
    const start = Date.now();
    try {
      const season = getCurrentSeason();
      const statsSeason = getStatsSeason(ctx.startsAt);
      const homeShort = ctx.homeShort?.toUpperCase() || '';
      const awayShort = ctx.awayShort?.toUpperCase() || '';

      if (!homeShort || !awayShort) {
        return { signalId: 'fangraphs', 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;

      // Fetch team batting profiles (full season + recent)
      const [homeBat, awayBat, homeBatRecent, awayBatRecent, homeTeamPit, awayTeamPit, homeStarter, awayStarter] = await Promise.all([
        getTeamBatting(homeShort, season, 'full'),
        getTeamBatting(awayShort, season, 'full'),
        getTeamBatting(homeShort, season, 'last14'),
        getTeamBatting(awayShort, season, 'last14'),
        getTeamPitching(homeShort, season, 'full'),
        getTeamPitching(awayShort, season, 'full'),
        homeStarterName ? getStarterProfile(homeStarterName, homeShort, statsSeason) : Promise.resolve(null),
        awayStarterName ? getStarterProfile(awayStarterName, awayShort, statsSeason) : Promise.resolve(null),
      ]);

      const homePit = homeStarter ? {
        era: homeStarter.era,
        fip: homeStarter.fip,
        xfip: homeStarter.xfip,
        kPct: homeStarter.kPct,
        bbPct: homeStarter.bbPct,
        whip: homeStarter.whip,
        ev: homeStarter.ev,
        barrelPct: homeStarter.barrelPct,
        hardHitPct: homeStarter.hardHitPct,
        gbPct: homeStarter.gbPct,
        hrFb: homeStarter.hrFb,
        throws: homeStarter.throws,
      } : (homeTeamPit ? {
        ...homeTeamPit,
        hardHitPct: null,
        gbPct: null,
        hrFb: null,
        throws: null,
      } : null);
      const awayPit = awayStarter ? {
        era: awayStarter.era,
        fip: awayStarter.fip,
        xfip: awayStarter.xfip,
        kPct: awayStarter.kPct,
        bbPct: awayStarter.bbPct,
        whip: awayStarter.whip,
        ev: awayStarter.ev,
        barrelPct: awayStarter.barrelPct,
        hardHitPct: awayStarter.hardHitPct,
        gbPct: awayStarter.gbPct,
        hrFb: awayStarter.hrFb,
        throws: awayStarter.throws,
      } : (awayTeamPit ? {
        ...awayTeamPit,
        hardHitPct: null,
        gbPct: null,
        hrFb: null,
        throws: null,
      } : null);

      const hasData = !!(homeBat || awayBat || homePit || awayPit);
      const score = computeScore(homeBat, awayBat, homePit, awayPit, homeBatRecent, awayBatRecent);

      return {
        signalId: 'fangraphs',
        score,
        weight: 0, // set by strategy
        available: hasData,
        rawData: {
          homeBat: homeBat ? { wrcPlus: homeBat.wrcPlus, woba: homeBat.woba, ops: homeBat.ops, ev: homeBat.ev, barrelPct: homeBat.barrelPct, hardHitPct: homeBat.hardHitPct, gbPct: homeBat.gbPct, fbPct: homeBat.fbPct, kPct: homeBat.kPct } : null,
          awayBat: awayBat ? { wrcPlus: awayBat.wrcPlus, woba: awayBat.woba, ops: awayBat.ops, ev: awayBat.ev, barrelPct: awayBat.barrelPct, hardHitPct: awayBat.hardHitPct, gbPct: awayBat.gbPct, fbPct: awayBat.fbPct, kPct: awayBat.kPct } : null,
          homePit: homePit ? { era: homePit.era, fip: homePit.fip, xfip: homePit.xfip, kPct: homePit.kPct, whip: homePit.whip, ev: homePit.ev, barrelPct: homePit.barrelPct, hardHitPct: homePit.hardHitPct ?? null, gbPct: homePit.gbPct ?? null, hrFb: homePit.hrFb ?? null, throws: homePit.throws ?? null } : null,
          awayPit: awayPit ? { era: awayPit.era, fip: awayPit.fip, xfip: awayPit.xfip, kPct: awayPit.kPct, whip: awayPit.whip, ev: awayPit.ev, barrelPct: awayPit.barrelPct, hardHitPct: awayPit.hardHitPct ?? null, gbPct: awayPit.gbPct ?? null, hrFb: awayPit.hrFb ?? null, throws: awayPit.throws ?? null } : null,
          homeBatRecent: homeBatRecent ? { wrcPlus: homeBatRecent.wrcPlus, woba: homeBatRecent.woba, ev: homeBatRecent.ev, barrelPct: homeBatRecent.barrelPct, hardHitPct: homeBatRecent.hardHitPct, gbPct: homeBatRecent.gbPct, fbPct: homeBatRecent.fbPct } : null,
          awayBatRecent: awayBatRecent ? { wrcPlus: awayBatRecent.wrcPlus, woba: awayBatRecent.woba, ev: awayBatRecent.ev, barrelPct: awayBatRecent.barrelPct, hardHitPct: awayBatRecent.hardHitPct, gbPct: awayBatRecent.gbPct, fbPct: awayBatRecent.fbPct } : null,
          starters: {
            source: probables?.eventId ? 'espn' : 'none',
            home: homeStarterName ? {
              name: homeStarterName,
              profileFound: !!homeStarter,
              espnEra: probables?.homeStarter?.era ?? null,
            } : null,
            away: awayStarterName ? {
              name: awayStarterName,
              profileFound: !!awayStarter,
              espnEra: probables?.awayStarter?.era ?? null,
            } : null,
          },
        },
        metadata: {
          latencyMs: Date.now() - start,
          source: 'db',
          freshness: new Date().toISOString().slice(0, 10),
        },
      };
    } catch (err) {
      return {
        signalId: 'fangraphs', score: 0.5, weight: 0, available: false,
        rawData: { error: String(err) },
        metadata: { latencyMs: Date.now() - start, source: 'db', freshness: '' },
      };
    }
  },
};
