/**
 * forecast-lifecycle.ts
 *
 * Canonical state machine for forecast display lifecycle.
 * Single source of truth for statuses, transitions, and classification logic.
 *
 * States:
 *   UPCOMING_QUALIFIED  — forecast qualifies, contest not started, pregame only
 *   LIVE_PENDING        — contest started, awaiting final result
 *   FINAL_GRADED        — contest final, graded with outcome
 *   CANCELLED           — contest cancelled/postponed/voided
 *   ERROR_REVIEW        — data mismatch requiring review
 */

// ─── Status Enum ────────────────────────────────────────────

export const ForecastStatus = {
  UPCOMING_QUALIFIED: 'UPCOMING_QUALIFIED',
  LIVE_PENDING: 'LIVE_PENDING',
  FINAL_GRADED: 'FINAL_GRADED',
  CANCELLED: 'CANCELLED',
  ERROR_REVIEW: 'ERROR_REVIEW',
} as const;

export type ForecastStatusType = typeof ForecastStatus[keyof typeof ForecastStatus];

// ─── Tier Classification ────────────────────────────────────

export const TIER_THRESHOLDS = [
  { tier: 'A+', min: 0.85 },
  { tier: 'A',  min: 0.70 },
  { tier: 'B+', min: 0.55 },
  { tier: 'B',  min: 0.00 },
] as const;

export const TIER_ORDER = ['A+', 'A', 'B+', 'B'] as const;

export const MIN_DISPLAY_CONFIDENCE = 0.55;

export function classifyTier(confidenceScore: number | null): string {
  if (confidenceScore == null) return 'B';
  for (const { tier, min } of TIER_THRESHOLDS) {
    if (confidenceScore >= min) return tier;
  }
  return 'B';
}

export function qualifiesForGradedFeed(confidenceScore: number | null): boolean {
  return confidenceScore != null && confidenceScore >= MIN_DISPLAY_CONFIDENCE;
}

// ─── Status Classification ──────────────────────────────────

export interface ForecastContext {
  startsAt: Date | string | null;
  hasAccuracyRecord: boolean;
  actualWinner: string | null;
  confidenceScore: number | null;
}

/**
 * Derive the canonical lifecycle status from a forecast's data.
 * This is the ONLY place status is determined — no ad-hoc logic elsewhere.
 */
export function classifyStatus(ctx: ForecastContext): ForecastStatusType {
  const { startsAt, hasAccuracyRecord, actualWinner, confidenceScore } = ctx;

  // Must qualify for display
  if (!qualifiesForGradedFeed(confidenceScore)) {
    return ForecastStatus.UPCOMING_QUALIFIED; // won't be shown anyway
  }

  // Has a graded result → final
  if (hasAccuracyRecord && actualWinner != null) {
    return ForecastStatus.FINAL_GRADED;
  }

  // Parse start time
  const start = startsAt ? new Date(startsAt) : null;
  const now = new Date();

  // No start time → treat as upcoming
  if (!start) return ForecastStatus.UPCOMING_QUALIFIED;

  // Contest hasn't started → upcoming
  if (start > now) {
    return ForecastStatus.UPCOMING_QUALIFIED;
  }

  // Contest started, no result yet → live pending
  return ForecastStatus.LIVE_PENDING;
}

// ─── Grade Derivation ───────────────────────────────────────

export type GradeOutcome = 'W' | 'L' | 'P';

export function deriveGrade(predictedWinner: string | null, actualWinner: string | null): GradeOutcome | null {
  if (!predictedWinner || !actualWinner) return null;

  // Fuzzy match — check if the last word of actual matches last word of predicted
  const predLast = predictedWinner.trim().split(/\s+/).pop()?.toLowerCase();
  const actualLast = actualWinner.trim().split(/\s+/).pop()?.toLowerCase();

  if (predLast && actualLast && predLast === actualLast) return 'W';

  // Also check full containment
  if (actualWinner.toLowerCase().includes(predictedWinner.toLowerCase()) ||
      predictedWinner.toLowerCase().includes(actualWinner.toLowerCase())) {
    return 'W';
  }

  return 'L';
}

// ─── DailyPick Tier (current prop systems only) ──────────────────────────

export function classifyDailyPickTier(
  mcProb: number | null,
  algoVersion?: string | null,
  lockLevel?: string | null,
): string {
  if ((algoVersion || '').startsWith('piff3.0_')) {
    if (mcProb == null) return 'B';
    if (mcProb >= 0.85) return 'A+';
    if (mcProb >= 0.70) return 'A';
    if (mcProb >= 0.55) return 'B+';
    return 'B';
  }

  switch ((lockLevel || '').trim()) {
    case 'SUPER LOCK':
      return 'A+';
    case 'STRONG LOCK':
      return 'A';
    case 'LOCK':
      return 'B+';
  }

  if (mcProb == null) return 'B';
  if (mcProb >= 0.85) return 'A+';
  if (mcProb >= 0.70) return 'A';
  if (mcProb >= 0.55) return 'B+';
  return 'B';
}

// ─── Score Derivation ───────────────────────────────────────

export function deriveScores(actualSpread: number | null, actualTotal: number | null): { homeScore: number; awayScore: number } | null {
  if (actualSpread == null || actualTotal == null) return null;
  return {
    homeScore: Math.round((actualTotal + actualSpread) / 2),
    awayScore: Math.round((actualTotal - actualSpread) / 2),
  };
}

// ─── Closing Line Formatter ─────────────────────────────────

export function formatClosingLine(
  oddsData: any,
  predictedWinner: string | null,
  homeTeam: string | null
): string | null {
  if (!oddsData || !predictedWinner) return null;

  try {
    const odds = typeof oddsData === 'string' ? JSON.parse(oddsData) : oddsData;
    const isHome = predictedWinner === homeTeam;
    const side = isHome ? 'home' : 'away';
    const teamShort = predictedWinner.split(' ').pop() || predictedWinner;

    let result: string | null = null;

    if (odds.spread?.[side]?.line != null) {
      const line = odds.spread[side].line;
      const spreadOdds = odds.spread[side].odds;
      const lineStr = line > 0 ? `+${line}` : line === 0 ? 'PK' : `${line}`;
      const oddsStr = spreadOdds != null ? ` (${spreadOdds > 0 ? '+' : ''}${spreadOdds})` : '';
      result = `${teamShort} ${lineStr}${oddsStr}`;
    }

    if (odds.moneyline?.[side] != null) {
      const ml = odds.moneyline[side];
      const mlStr = ml > 0 ? `+${ml}` : `${ml}`;
      result = result ? `${result} / ML ${mlStr}` : `${teamShort} ML ${mlStr}`;
    }

    return result;
  } catch {
    return null;
  }
}

// ─── Forecast Line Formatter ────────────────────────────────

export function formatForecastLine(
  forecastData: any,
  predictedWinner: string | null,
  homeTeam: string | null
): string | null {
  if (!forecastData || !predictedWinner) return null;

  try {
    const fd = typeof forecastData === 'string' ? JSON.parse(forecastData) : forecastData;
    const isHome = predictedWinner === homeTeam;
    const side = isHome ? 'home' : 'away';
    const teamShort = predictedWinner.split(' ').pop() || predictedWinner;

    let result: string | null = null;

    if (fd.projected_lines?.spread?.[side] != null) {
      const projSpread = fd.projected_lines.spread[side];
      const lineStr = projSpread > 0 ? `+${projSpread}` : projSpread === 0 ? 'PK' : `${projSpread}`;
      result = `${teamShort} ${lineStr}`;
    }

    if (fd.projected_lines?.moneyline?.[side] != null) {
      const ml = fd.projected_lines.moneyline[side];
      const mlStr = ml > 0 ? `+${ml}` : `${ml}`;
      result = result ? `${result} / ML ${mlStr}` : `${teamShort} ML ${mlStr}`;
    }

    return result;
  } catch {
    return null;
  }
}

// ─── Player Prop Formatters ─────────────────────────────────

export function formatPlayerForecastLine(actionType: string, direction: string | null, actionValue: number | null): string | null {
  const dir = direction === 'over' ? 'Over' : direction === 'under' ? 'Under' : '';
  const stat = actionType || '';
  if (dir && actionValue != null) {
    return `${stat} ${dir} ${actionValue}`;
  }
  return null;
}

export function formatPlayerClosingLine(actionType: string, actionValue: number | null): string | null {
  if (actionValue != null && actionType) {
    return `${actionType} ${actionValue}`;
  }
  return null;
}
