import { getCurrentEtDateKey, getEtDateKey } from '../lib/league-windows';
import { getDigimonForGame, loadDigimonPicksForDate, type DigimonPick } from './digimon';
import { getPiffPropsForGame, loadPiffPropsForDate, type PiffLeg } from './piff';
import { dvpSignal } from './rie/signals/dvp-signal';
import { fangraphsSignal } from './rie/signals/fangraphs-signal';
import { mlbMatchupSignal } from './rie/signals/mlb-matchup-signal';
import { mlbPhaseSignal } from './rie/signals/mlb-phase-signal';
import type { MatchupContext, SignalResult } from './rie/types';

export type TeamPlusLeague = 'mlb' | 'nba' | 'nhl';

type TeamPlusSignalKey =
  | 'piff_team_plus'
  | 'digimon_team_plus'
  | 'dvp_team_plus'
  | 'mlb_matchup'
  | 'mlb_phase'
  | 'fangraphs';

type TeamPlusComponent = {
  signalId: TeamPlusSignalKey;
  available: boolean;
  homeScore: number | null;
  weight: number;
  rawData: Record<string, any>;
};

export type TeamPlusWeightProfile = Record<TeamPlusLeague, Record<TeamPlusSignalKey, number>>;

export type TeamPlusDecision = {
  league: TeamPlusLeague;
  available: boolean;
  homeScore: number;
  awayScore: number;
  winnerPick: string;
  confidence: number;
  lockedMargin: number;
  weights: Record<string, number>;
  components: TeamPlusComponent[];
  explanation: string[];
};

export type MlbTeamPlusGateSettings = {
  confidenceThreshold: number;
  requireStarterLinked: boolean;
  requireMatchupPhaseAgreement: boolean;
  requirePhaseInternalAgreement: boolean;
  requireFangraphsAgreement: boolean;
  minMatchupEdge: number;
  minPhaseEdge: number;
  minContextEdge: number;
  minBullpenEdge: number;
};

export type MlbTeamPlusMetrics = {
  starterLinked: boolean;
  matchupPhaseAgreement: boolean;
  phaseInternalAgreement: boolean;
  fangraphsAgreement: boolean | null;
  matchupEdge: number;
  phaseEdge: number;
  contextEdge: number;
  bullpenEdge: number;
  fullGameSideReady: boolean;
};

export type MlbAlgoOverride = {
  shouldOverride: boolean;
  winnerPick: string;
  flipProbability: number;
  features: Record<string, number>;
  reason: string[];
};

type HistoricalDecisionParams = {
  league: TeamPlusLeague;
  homeTeam: string;
  awayTeam: string;
  homeShort: string;
  awayShort: string;
  startsAt: string;
  rieSignals: SignalResult[];
  weightsOverride?: TeamPlusWeightProfile;
};

type LiveDecisionParams = {
  league: TeamPlusLeague;
  homeTeam: string;
  awayTeam: string;
  homeShort: string;
  awayShort: string;
  startsAt: string;
  eventId: string;
  event?: any;
};

export const TEAM_PLUS_WEIGHTS: TeamPlusWeightProfile = {
  mlb: {
    mlb_matchup: 0.32,
    mlb_phase: 0.36,
    fangraphs: 0.14,
    piff_team_plus: 0.10,
    dvp_team_plus: 0.08,
    digimon_team_plus: 0,
  },
  nba: {
    piff_team_plus: 0.45,
    digimon_team_plus: 0.30,
    dvp_team_plus: 0.25,
    mlb_matchup: 0,
    mlb_phase: 0,
    fangraphs: 0,
  },
  nhl: {
    piff_team_plus: 0.55,
    dvp_team_plus: 0.45,
    digimon_team_plus: 0,
    mlb_matchup: 0,
    mlb_phase: 0,
    fangraphs: 0,
  },
};

export const MLB_TEAM_PLUS_GATE_DEFAULTS: MlbTeamPlusGateSettings = {
  confidenceThreshold: 0.60,
  requireStarterLinked: true,
  requireMatchupPhaseAgreement: true,
  requirePhaseInternalAgreement: true,
  requireFangraphsAgreement: false,
  minMatchupEdge: 0.06,
  minPhaseEdge: 0.04,
  minContextEdge: 0.08,
  minBullpenEdge: 0.08,
};

const MLB_BASELINE_OVERRIDE_FEATURES = [
  'base_log5_align',
  'injury_edge',
  'matchup_phase_gap',
  'fullgame_ready',
  'base_fangraphs_align',
  'base_dvp_align',
  'base_pitch_align',
  'base_matchup_align',
  'starter_linked',
  'base_phase_align',
  'base_firstfive_align',
  'base_runs_align',
] as const;

const MLB_BASELINE_OVERRIDE_MEANS = [
  0.165183,
  -0.014061,
  0.087359,
  0.993939,
  0.07055,
  0.004528,
  0.084445,
  0.080272,
  0.860606,
  0.024816,
  0.015801,
  0.128703,
];

const MLB_BASELINE_OVERRIDE_SCALES = [
  0.154796,
  0.638617,
  0.074279,
  0.077614,
  0.232656,
  0.065352,
  0.224995,
  0.087039,
  0.346357,
  0.117503,
  0.175079,
  0.552771,
];

const MLB_BASELINE_OVERRIDE_COEFFICIENTS = [
  0.502511,
  0.380433,
  -0.369089,
  0.343125,
  0.184423,
  0.151116,
  0.125248,
  -0.14923,
  0.105601,
  -0.152979,
  -0.036443,
  0.095294,
];

const MLB_BASELINE_OVERRIDE_INTERCEPT = 0.051793;
const MLB_BASELINE_OVERRIDE_THRESHOLD = 0.57;

const TEAM_PLUS_HOME_BIAS: Record<TeamPlusLeague, number> = {
  mlb: 0.02,
  nba: 0.03,
  nhl: 0.025,
};

const TEAM_PLUS_PROFILE: Record<TeamPlusLeague, {
  piffScale: number;
  dvpScale: number;
  digimonScale: number;
  dvpEasyWeight: number;
  dvpAvgWeight: number;
  dvpToughWeight: number;
}> = {
  mlb: {
    piffScale: 0.9,
    dvpScale: 1.1,
    digimonScale: 0.9,
    dvpEasyWeight: 0.9,
    dvpAvgWeight: 0.10,
    dvpToughWeight: 0.65,
  },
  nba: {
    piffScale: 1.25,
    dvpScale: 1.75,
    digimonScale: 1.15,
    dvpEasyWeight: 1.0,
    dvpAvgWeight: 0.25,
    dvpToughWeight: 0.45,
  },
  nhl: {
    piffScale: 1.45,
    dvpScale: 1.2,
    digimonScale: 1.0,
    dvpEasyWeight: 0.95,
    dvpAvgWeight: 0.12,
    dvpToughWeight: 0.55,
  },
};

const INVERSE_GOOD_UNDER_STATS = new Set([
  'batterstrikeouts',
  'mlbstrikeouts',
  'earnedruns',
  'mlbearnedruns',
  'hitsallowed',
  'walksallowed',
  'runsscoredallowed',
  'shotsongoalallowed',
  'goalsallowed',
]);

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

function round3(value: number): number {
  return Math.round(value * 1000) / 1000;
}

function normalizeKey(value: string | null | undefined): string {
  return String(value || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9]/g, '');
}

function normalizeWeights(weights: Record<string, number>): Record<string, number> {
  const active = Object.entries(weights).filter(([, value]) => value > 0);
  const total = active.reduce((sum, [, value]) => sum + value, 0);
  if (total <= 0) return Object.fromEntries(active);
  return Object.fromEntries(active.map(([key, value]) => [key, round3(value / total)]));
}

function tierMultiplier(label: string | null | undefined): number {
  switch (String(label || '').toUpperCase()) {
    case 'T1_LOCK':
    case 'LOCK':
      return 1.25;
    case 'T2_STRONG':
    case 'STRONG':
      return 1.0;
    case 'T3_SOLID':
    case 'PLAY':
      return 0.8;
    case 'LEAN':
      return 0.65;
    default:
      return 0.55;
  }
}

function verdictMultiplier(verdict: string | null | undefined): number {
  switch (String(verdict || '').toUpperCase()) {
    case 'LOCK':
      return 1.2;
    case 'PLAY':
      return 1.0;
    case 'LEAN':
      return 0.75;
    default:
      return 0.45;
  }
}

function directionSign(direction: string | null | undefined, stat: string | null | undefined): number {
  const dir = normalizeKey(direction);
  const statKey = normalizeKey(stat);
  const underIsBullish = INVERSE_GOOD_UNDER_STATS.has(statKey)
    || (statKey === 'strikeouts' && !statKey.includes('pitcher'));

  if (dir === 'under') {
    return underIsBullish ? 1 : -1;
  }
  return underIsBullish ? -1 : 1;
}

function diffToHomeScore(diff: number, scale: number, bias = 0): number {
  return clamp(0.5 + Math.tanh(diff * scale) * 0.35 + bias, 0.08, 0.92);
}

function buildPiffTeamPlusComponent(
  league: TeamPlusLeague,
  props: PiffLeg[],
  homeShort: string,
  awayShort: string,
): TeamPlusComponent {
  const homeKey = homeShort.toUpperCase();
  const awayKey = awayShort.toUpperCase();
  let homeSupport = 0;
  let awaySupport = 0;
  let homeLegs = 0;
  let awayLegs = 0;

  for (const leg of props) {
    const team = String(leg.team || '').toUpperCase();
    const support = (leg.edge || 0) * tierMultiplier(leg.tier_label) * directionSign(leg.direction, leg.stat);
    if (team === homeKey) {
      homeSupport += support;
      homeLegs += 1;
    } else if (team === awayKey) {
      awaySupport += support;
      awayLegs += 1;
    }
  }

  const available = homeLegs + awayLegs > 0;
  const diff = homeSupport - awaySupport;
  return {
    signalId: 'piff_team_plus',
    available,
    homeScore: available ? round3(diffToHomeScore(diff, TEAM_PLUS_PROFILE[league].piffScale)) : null,
    weight: 0,
    rawData: {
      homeLegs,
      awayLegs,
      homeSupport: round3(homeSupport),
      awaySupport: round3(awaySupport),
      supportDiff: round3(diff),
    },
  };
}

function buildDigimonTeamPlusComponent(
  league: TeamPlusLeague,
  picks: DigimonPick[],
  homeShort: string,
  awayShort: string,
): TeamPlusComponent {
  const homeKey = homeShort.toUpperCase();
  const awayKey = awayShort.toUpperCase();
  let homeSupport = 0;
  let awaySupport = 0;
  let homePicks = 0;
  let awayPicks = 0;

  for (const pick of picks) {
    const team = String(pick.team || '').toUpperCase();
    const support = Math.max(0, 1 - Number(pick.missRate || 0.5)) * verdictMultiplier(pick.verdict);
    if (team === homeKey) {
      homeSupport += support;
      homePicks += 1;
    } else if (team === awayKey) {
      awaySupport += support;
      awayPicks += 1;
    }
  }

  const available = homePicks + awayPicks > 0;
  const diff = homeSupport - awaySupport;
  return {
    signalId: 'digimon_team_plus',
    available,
    homeScore: available ? round3(diffToHomeScore(diff, TEAM_PLUS_PROFILE[league].digimonScale)) : null,
    weight: 0,
    rawData: {
      homePicks,
      awayPicks,
      homeSupport: round3(homeSupport),
      awaySupport: round3(awaySupport),
      supportDiff: round3(diff),
    },
  };
}

function vulnerabilityScore(
  league: TeamPlusLeague,
  tiers: { easy?: number; avg?: number; tough?: number; total?: number } | null | undefined,
): number {
  const total = Number(tiers?.total || 0);
  if (total <= 0) return 0;
  const easy = Number(tiers?.easy || 0);
  const avg = Number(tiers?.avg || 0);
  const tough = Number(tiers?.tough || 0);
  const profile = TEAM_PLUS_PROFILE[league];
  return ((easy * profile.dvpEasyWeight) + (avg * profile.dvpAvgWeight) - (tough * profile.dvpToughWeight)) / total;
}

function buildDvpTeamPlusComponent(league: TeamPlusLeague, rawData: any): TeamPlusComponent {
  const homeDefenseVulnerability = vulnerabilityScore(league, rawData?.homeTiers);
  const awayDefenseVulnerability = vulnerabilityScore(league, rawData?.awayTiers);
  const available = Number(rawData?.homeTiers?.total || 0) > 0 || Number(rawData?.awayTiers?.total || 0) > 0;
  const diff = awayDefenseVulnerability - homeDefenseVulnerability;

  return {
    signalId: 'dvp_team_plus',
    available,
    homeScore: available ? round3(diffToHomeScore(diff, TEAM_PLUS_PROFILE[league].dvpScale)) : null,
    weight: 0,
    rawData: {
      homeDefenseVulnerability: round3(homeDefenseVulnerability),
      awayDefenseVulnerability: round3(awayDefenseVulnerability),
      vulnerabilityDiff: round3(diff),
      homeTiers: rawData?.homeTiers || null,
      awayTiers: rawData?.awayTiers || null,
    },
  };
}

function buildStoredSignalComponent(signalId: TeamPlusSignalKey, signal: SignalResult | undefined): TeamPlusComponent {
  return {
    signalId,
    available: Boolean(signal?.available),
    homeScore: signal?.available ? round3(signal.score) : null,
    weight: 0,
    rawData: signal?.rawData || {},
  };
}

function getComponent(components: TeamPlusComponent[], signalId: TeamPlusSignalKey): TeamPlusComponent | undefined {
  return components.find((component) => component.signalId === signalId);
}

function logistic(value: number): number {
  return 1 / (1 + Math.exp(-value));
}

export function getMlbTeamPlusMetrics(decision: TeamPlusDecision): MlbTeamPlusMetrics | null {
  if (decision.league !== 'mlb') return null;

  const matchup = getComponent(decision.components, 'mlb_matchup');
  const phase = getComponent(decision.components, 'mlb_phase');
  const fangraphs = getComponent(decision.components, 'fangraphs');
  if (!matchup?.available || matchup.homeScore == null || !phase?.available || phase.homeScore == null) {
    return null;
  }

  const matchupLeanHome = matchup.homeScore >= 0.5;
  const phaseLeanHome = phase.homeScore >= 0.5;
  const firstFiveScore = Number(phase.rawData?.firstFive?.sideScore ?? 0.5);
  const bullpenScore = Number(phase.rawData?.bullpen?.sideScore ?? 0.5);
  const firstFiveLeanHome = firstFiveScore >= 0.5;
  const bullpenLeanHome = bullpenScore >= 0.5;
  const fangraphsLeanHome = fangraphs?.available && fangraphs.homeScore != null ? fangraphs.homeScore >= 0.5 : null;
  const starterSource = String(matchup.rawData?.starters?.source || 'none');

  return {
    starterLinked: starterSource === 'espn',
    matchupPhaseAgreement: matchupLeanHome === phaseLeanHome,
    phaseInternalAgreement: firstFiveLeanHome === bullpenLeanHome,
    fangraphsAgreement: fangraphsLeanHome == null ? null : fangraphsLeanHome === matchupLeanHome,
    matchupEdge: round3(Math.abs(matchup.homeScore - 0.5)),
    phaseEdge: round3(Math.abs(phase.homeScore - 0.5)),
    contextEdge: round3(Math.abs(Number(phase.rawData?.context?.sideScore ?? 0.5) - 0.5)),
    bullpenEdge: round3(Math.abs(bullpenScore - 0.5)),
    fullGameSideReady: Boolean(phase.rawData?.applicationHints?.fullGameSideReady),
  };
}

export function qualifiesForMlbTeamPlusLock(
  decision: TeamPlusDecision,
  settings: MlbTeamPlusGateSettings = MLB_TEAM_PLUS_GATE_DEFAULTS,
): boolean {
  const metrics = getMlbTeamPlusMetrics(decision);
  if (!metrics) return false;
  if (!metrics.fullGameSideReady) return false;
  if (decision.confidence < settings.confidenceThreshold) return false;
  if (settings.requireStarterLinked && !metrics.starterLinked) return false;
  if (settings.requireMatchupPhaseAgreement && !metrics.matchupPhaseAgreement) return false;
  if (settings.requirePhaseInternalAgreement && !metrics.phaseInternalAgreement) return false;
  if (settings.requireFangraphsAgreement && metrics.fangraphsAgreement === false) return false;
  if (metrics.matchupEdge < settings.minMatchupEdge) return false;
  if (metrics.phaseEdge < settings.minPhaseEdge) return false;
  if (metrics.contextEdge < settings.minContextEdge) return false;
  if (metrics.bullpenEdge < settings.minBullpenEdge) return false;
  return true;
}

export function buildMlbBaselineOverride(params: {
  decision: TeamPlusDecision | null;
  baselineWinner: string;
  homeTeam: string;
  awayTeam: string;
}): MlbAlgoOverride | null {
  if (!params.decision || params.decision.league !== 'mlb') return null;

  const metrics = getMlbTeamPlusMetrics(params.decision);
  const matchup = getComponent(params.decision.components, 'mlb_matchup');
  const phase = getComponent(params.decision.components, 'mlb_phase');
  const fangraphs = getComponent(params.decision.components, 'fangraphs');
  const dvp = getComponent(params.decision.components, 'dvp_team_plus');
  if (!metrics || !matchup?.available || matchup.homeScore == null || !phase?.available || phase.homeScore == null) {
    return null;
  }

  const baselineIsHome = params.baselineWinner === params.homeTeam;
  const baselineSign = baselineIsHome ? 1 : -1;
  const matchupRaw = matchup.rawData || {};
  const phaseRaw = phase.rawData || {};
  const fangraphsHomeScore = fangraphs?.available && fangraphs.homeScore != null ? fangraphs.homeScore - 0.5 : 0;
  const dvpHomeScore = dvp?.available && dvp.homeScore != null ? dvp.homeScore - 0.5 : 0;
  const awayProjFip = Number(matchupRaw?.projections?.awayPit?.projFip ?? 4.2);
  const homeProjFip = Number(matchupRaw?.projections?.homePit?.projFip ?? 4.2);
  const runsHome = Number(matchupRaw?.runsCreated?.home ?? 0);
  const runsAway = Number(matchupRaw?.runsCreated?.away ?? 0);
  const injuries = phaseRaw?.context?.injuries || {};
  const features: Record<(typeof MLB_BASELINE_OVERRIDE_FEATURES)[number], number> = {
    base_log5_align: baselineSign * (Number(matchupRaw?.log5?.homeWinProbHFA ?? 0.5) - 0.5),
    injury_edge: clamp((Number(injuries.awaySeverity || 0) - Number(injuries.homeSeverity || 0)) / 5, -3, 3),
    matchup_phase_gap: Math.abs((matchup.homeScore - 0.5) - (phase.homeScore - 0.5)),
    fullgame_ready: metrics.fullGameSideReady ? 1 : 0,
    base_fangraphs_align: baselineSign * fangraphsHomeScore,
    base_dvp_align: baselineSign * dvpHomeScore,
    base_pitch_align: baselineSign * clamp((awayProjFip - homeProjFip) / 1.5, -3, 3),
    base_matchup_align: baselineSign * (matchup.homeScore - 0.5),
    starter_linked: metrics.starterLinked ? 1 : 0,
    base_phase_align: baselineSign * (phase.homeScore - 0.5),
    base_firstfive_align: baselineSign * (Number(phaseRaw?.firstFive?.sideScore ?? 0.5) - 0.5),
    base_runs_align: baselineSign * clamp((runsHome - runsAway) / 3, -3, 3),
  };

  let linear = MLB_BASELINE_OVERRIDE_INTERCEPT;
  MLB_BASELINE_OVERRIDE_FEATURES.forEach((feature, index) => {
    const standardized = (features[feature] - MLB_BASELINE_OVERRIDE_MEANS[index]) / MLB_BASELINE_OVERRIDE_SCALES[index];
    linear += standardized * MLB_BASELINE_OVERRIDE_COEFFICIENTS[index];
  });

  const flipProbability = round3(logistic(linear));
  const shouldOverride = flipProbability >= MLB_BASELINE_OVERRIDE_THRESHOLD;
  const winnerPick = shouldOverride
    ? (baselineIsHome ? params.awayTeam : params.homeTeam)
    : params.baselineWinner;
  const reason = [
    `mlb_algo_override_flip_prob=${flipProbability}`,
    `mlb_algo_override_baseline=${params.baselineWinner}`,
    `mlb_algo_override_log5_align=${round3(features.base_log5_align)}`,
    `mlb_algo_override_matchup_phase_gap=${round3(features.matchup_phase_gap)}`,
    `mlb_algo_override_injury_edge=${round3(features.injury_edge)}`,
  ];

  return {
    shouldOverride,
    winnerPick,
    flipProbability,
    features,
    reason,
  };
}

function shapeMlbDecision(decision: TeamPlusDecision): TeamPlusDecision {
  const matchup = getComponent(decision.components, 'mlb_matchup');
  const phase = getComponent(decision.components, 'mlb_phase');
  const fangraphs = getComponent(decision.components, 'fangraphs');
  if (!matchup?.available || matchup.homeScore == null || !phase?.available || phase.homeScore == null) {
    return decision;
  }

  let adjustedConfidence = decision.confidence;
  let adjustedMargin = decision.lockedMargin;
  const notes = [...decision.explanation];
  const matchupLeanHome = matchup.homeScore >= 0.5;
  const phaseLeanHome = phase.homeScore >= 0.5;
  const sameSide = matchupLeanHome === phaseLeanHome;
  const matchupEdge = Math.abs(matchup.homeScore - 0.5);
  const phaseEdge = Math.abs(phase.homeScore - 0.5);
  const firstFiveScore = Number(phase.rawData?.firstFive?.sideScore ?? 0.5);
  const bullpenScore = Number(phase.rawData?.bullpen?.sideScore ?? 0.5);
  const firstFiveLeanHome = firstFiveScore >= 0.5;
  const bullpenLeanHome = bullpenScore >= 0.5;
  const phaseInternalAgreement = firstFiveLeanHome === bullpenLeanHome;
  const starterSource = String(matchup.rawData?.starters?.source || 'none');
  const starterLinked = starterSource === 'espn';
  const fangraphsLeanHome = fangraphs?.available && fangraphs.homeScore != null ? fangraphs.homeScore >= 0.5 : null;

  if (!sameSide) {
    adjustedConfidence -= 0.07;
    adjustedMargin *= 0.7;
    notes.push('mlb_team_plus_confidence_penalty: matchup and phase disagree');
  } else {
    adjustedConfidence += 0.025;
  }

  if (!phaseInternalAgreement) {
    adjustedConfidence -= 0.045;
    adjustedMargin *= 0.8;
    notes.push('mlb_team_plus_confidence_penalty: first five and bullpen lean disagree');
  } else {
    adjustedConfidence += 0.015;
  }

  if (!starterLinked) {
    adjustedConfidence -= 0.035;
    adjustedMargin *= 0.82;
    notes.push('mlb_team_plus_confidence_penalty: starter linkage missing');
  } else {
    adjustedConfidence += 0.015;
  }

  if (fangraphsLeanHome != null && fangraphsLeanHome !== matchupLeanHome) {
    adjustedConfidence -= 0.02;
    notes.push('mlb_team_plus_confidence_penalty: fangraphs disagrees');
  }

  if (matchupEdge >= 0.1 && phaseEdge >= 0.1 && sameSide && phaseInternalAgreement) {
    adjustedConfidence += 0.03;
    adjustedMargin *= 1.08;
    notes.push('mlb_team_plus_confidence_boost: structural agreement strong');
  }

  decision.confidence = round3(clamp(adjustedConfidence, 0.5, 0.82));
  decision.lockedMargin = round3(
    decision.lockedMargin >= 0 ? Math.abs(adjustedMargin) : -Math.abs(adjustedMargin),
  );
  decision.explanation = notes;
  return decision;
}

function applyWeights(
  league: TeamPlusLeague,
  components: TeamPlusComponent[],
  params: { homeTeam: string; awayTeam: string },
  weightsOverride?: TeamPlusWeightProfile,
): TeamPlusDecision {
  const baseWeights = (weightsOverride || TEAM_PLUS_WEIGHTS)[league];
  const activeWeights = normalizeWeights(
    Object.fromEntries(
      components
        .filter((component) => component.available && component.homeScore != null && (baseWeights[component.signalId] || 0) > 0)
        .map((component) => [component.signalId, baseWeights[component.signalId]]),
    ),
  );

  let homeScore = 0.5 + TEAM_PLUS_HOME_BIAS[league];
  for (const component of components) {
    component.weight = activeWeights[component.signalId] || 0;
    if (component.weight > 0 && component.homeScore != null) {
      homeScore += (component.homeScore - 0.5) * component.weight;
    }
  }

  homeScore = clamp(homeScore, 0.08, 0.92);
  const awayScore = round3(1 - homeScore);
  const winnerPick = homeScore >= 0.5 ? params.homeTeam : params.awayTeam;
  const confidence = round3(0.5 + Math.abs(homeScore - 0.5) * 0.9);
  const lockedMarginBase = league === 'mlb' ? 0.6 : league === 'nhl' ? 1.2 : 2.0;
  const lockedMargin = round3(lockedMarginBase + Math.abs(homeScore - 0.5) * (league === 'nba' ? 10 : league === 'nhl' ? 4 : 3));
  const explanation = components
    .filter((component) => component.weight > 0 && component.homeScore != null)
    .sort((left, right) => right.weight - left.weight)
    .map((component) => {
      const lean = component.homeScore! >= 0.5 ? params.homeTeam : params.awayTeam;
      return `${component.signalId} leans ${lean} (${Math.round(component.homeScore! * 100)} home-score, wt ${Math.round(component.weight * 100)}%)`;
    });

  const decision: TeamPlusDecision = {
    league,
    available: explanation.length > 0,
    homeScore: round3(homeScore),
    awayScore,
    winnerPick,
    confidence,
    lockedMargin: winnerPick === params.homeTeam ? lockedMargin : -lockedMargin,
    weights: activeWeights,
    components,
    explanation,
  };

  if (league === 'mlb') {
    return shapeMlbDecision(decision);
  }

  return decision;
}

function findStoredSignal(rieSignals: SignalResult[], signalId: string): SignalResult | undefined {
  return rieSignals.find((signal) => signal.signalId === signalId);
}

export function computeHistoricalTeamPlusDecision(params: HistoricalDecisionParams): TeamPlusDecision {
  const eventDate = getEtDateKey(params.startsAt || '') || getCurrentEtDateKey();
  const piffProps = getPiffPropsForGame(
    params.homeShort,
    params.awayShort,
    loadPiffPropsForDate(eventDate),
    params.league,
  );
  const digimonPicks = params.league === 'nba'
    ? getDigimonForGame(params.homeShort, params.awayShort, loadDigimonPicksForDate(eventDate))
    : [];

  const components: TeamPlusComponent[] = [
    buildPiffTeamPlusComponent(params.league, piffProps, params.homeShort, params.awayShort),
    buildDigimonTeamPlusComponent(params.league, digimonPicks, params.homeShort, params.awayShort),
    buildDvpTeamPlusComponent(params.league, findStoredSignal(params.rieSignals, 'dvp')?.rawData),
  ];

  if (params.league === 'mlb') {
    components.push(
      buildStoredSignalComponent('mlb_matchup', findStoredSignal(params.rieSignals, 'mlb_matchup')),
      buildStoredSignalComponent('mlb_phase', findStoredSignal(params.rieSignals, 'mlb_phase')),
      buildStoredSignalComponent('fangraphs', findStoredSignal(params.rieSignals, 'fangraphs')),
    );
  }

  return applyWeights(params.league, components, {
    homeTeam: params.homeTeam,
    awayTeam: params.awayTeam,
  }, params.weightsOverride);
}

export async function buildLiveTeamPlusDecision(
  params: LiveDecisionParams & { weightsOverride?: TeamPlusWeightProfile },
): Promise<TeamPlusDecision | null> {
  const ctx: MatchupContext = {
    league: params.league,
    homeTeam: params.homeTeam,
    awayTeam: params.awayTeam,
    homeShort: params.homeShort,
    awayShort: params.awayShort,
    startsAt: params.startsAt,
    eventId: params.eventId,
    event: params.event,
  };
  const eventDate = getEtDateKey(params.startsAt || '') || getCurrentEtDateKey();
  const piffProps = getPiffPropsForGame(
    params.homeShort,
    params.awayShort,
    loadPiffPropsForDate(eventDate),
    params.league,
  );
  const digimonPicks = params.league === 'nba'
    ? getDigimonForGame(params.homeShort, params.awayShort, loadDigimonPicksForDate(eventDate))
    : [];
  const components: TeamPlusComponent[] = [
    buildPiffTeamPlusComponent(params.league, piffProps, params.homeShort, params.awayShort),
    buildDigimonTeamPlusComponent(params.league, digimonPicks, params.homeShort, params.awayShort),
  ];

  const dvp = await dvpSignal.collect(ctx);
  components.push(buildDvpTeamPlusComponent(params.league, dvp.rawData));

  if (params.league === 'mlb') {
    const [matchup, phase, fangraphs] = await Promise.all([
      mlbMatchupSignal.collect(ctx),
      mlbPhaseSignal.collect(ctx),
      fangraphsSignal.collect(ctx),
    ]);
    components.push(
      buildStoredSignalComponent('mlb_matchup', matchup),
      buildStoredSignalComponent('mlb_phase', phase),
      buildStoredSignalComponent('fangraphs', fangraphs),
    );
  }

  const decision = applyWeights(params.league, components, {
    homeTeam: params.homeTeam,
    awayTeam: params.awayTeam,
  }, params.weightsOverride);
  return decision.available ? decision : null;
}
