import {
  buildPlayerPropSignal,
  PlayerPropDirection,
  RankedPlayerPropSignal,
  rankQualifiedSignals,
} from './player-prop-signals';
import type { PublicTopPropCard } from '../contracts/forecast-public-contract';
import { getPublicMarketSourceMeta, hasNamedMarketSource } from '../lib/market-source';
import { assessPlayerPropClvFit } from './player-prop-clv-profile';

export interface TopPropRowInput {
  assetId: string;
  eventId: string;
  league: string | null;
  startsAt: string | null;
  playerName: string | null;
  team: string | null;
  opponent: string | null;
  teamSide: 'home' | 'away' | null;
  prop: string | null;
  statType: string | null;
  normalizedStatType: string | null;
  marketLine: number | null;
  odds: number | null;
  projectedProbability: number | null;
  projectedOutcome: number | null;
  edgePct?: number | null;
  recommendation: string | null;
  playerRole: string | null;
  confidenceFactor?: number | null;
  marketSource?: string | null;
  marketCompletenessStatus?: 'source_complete' | 'multi_source_complete' | 'incomplete' | null;
  sourceBacked?: boolean | null;
  marketQualityScore?: number | null;
  modelContext?: Record<string, any> | null;
  closingLineValue?: number | null;
}

function toCard(input: TopPropRowInput, ranked: RankedPlayerPropSignal): PublicTopPropCard {
  const forecastDirection = ranked.forecastDirection as Extract<PlayerPropDirection, 'OVER' | 'UNDER'>;
  const sourceMeta = getPublicMarketSourceMeta(input.marketSource);
  const clvFit = assessPlayerPropClvFit({
    league: input.league,
    statType: input.statType,
    normalizedStatType: input.normalizedStatType,
    recommendation: forecastDirection,
    projectedProbability: ranked.projectedProbability,
    confidenceFactor: input.confidenceFactor ?? null,
    odds: input.odds ?? null,
    marketLine: input.marketLine ?? null,
  });
  return {
    assetId: input.assetId,
    eventId: input.eventId,
    league: input.league,
    startsAt: input.startsAt,
    playerName: input.playerName || 'Unknown',
    team: input.team,
    opponent: input.opponent,
    propType: ranked.tableRow.propType,
    marketLine: ranked.tableRow.marketLine,
    odds: ranked.tableRow.odds,
    marketImpliedProbability: ranked.marketImpliedProbability,
    forecastDirection,
    projectedProbability: ranked.projectedProbability,
    projectedOutcome: ranked.projectedOutcome,
    edgePct: ranked.edgePct,
    signal: ranked.signalTier,
    agreementLabel: ranked.agreementLabel,
    marketQualityLabel: ranked.marketQualityLabel,
    clvFitLabel: clvFit.fitLabel,
    clvFitScore: clvFit.fitScore,
    clvTracked: clvFit.eligible,
    closingLineValue: input.closingLineValue ?? null,
    verificationLabel: sourceMeta.label,
    verificationType: sourceMeta.type,
    rankScore: ranked.rankScore,
    rankPosition: ranked.rankPosition,
  };
}

export function buildTopPropsOfDay(
  rows: TopPropRowInput[],
  options: { limit?: number; maxPropsPerPlayer?: number; maxPerPropTypePerPlayer?: number } = {},
): PublicTopPropCard[] {
  const rankedSignals = rankQualifiedSignals(
    rows
      .filter((row) => row.sourceBacked !== false && hasNamedMarketSource(row.marketSource))
      .map((row) => {
        const clvFit = assessPlayerPropClvFit({
          league: row.league,
          statType: row.statType,
          normalizedStatType: row.normalizedStatType,
          recommendation: row.recommendation,
          projectedProbability: row.projectedProbability,
          confidenceFactor: row.confidenceFactor ?? null,
          odds: row.odds ?? null,
          marketLine: row.marketLine ?? null,
        });
        if (!clvFit.eligible) return null;
        const built = buildPlayerPropSignal({
          player: row.playerName,
          team: row.team,
          teamSide: row.teamSide,
          league: row.league,
          prop: row.prop,
          statType: row.statType,
          normalizedStatType: row.normalizedStatType,
          marketLine: row.marketLine,
          odds: row.odds,
          projectedProbability: row.projectedProbability,
          projectedOutcome: row.projectedOutcome,
          edgePct: row.edgePct ?? null,
          recommendation: row.recommendation,
          playerRole: row.playerRole,
          marketSource: row.marketSource ?? null,
          marketCompletenessStatus: row.marketCompletenessStatus ?? null,
          sourceBacked: row.sourceBacked ?? null,
          marketQualityScore: row.marketQualityScore ?? null,
          modelContext: row.modelContext ?? null,
          clvFitScore: clvFit.fitScore,
        });
        if (!built) return null;
        return {
          ...built,
          confidenceFactor: row.confidenceFactor ?? null,
        };
      })
      .filter((row): row is NonNullable<typeof row> => Boolean(row)),
    options,
  );

  const byLookup = new Map<string, TopPropRowInput>();
  for (const row of rows) {
    const key = [
      row.playerName || '',
      row.team || '',
      row.prop || '',
      row.marketLine ?? '',
      row.recommendation || '',
      row.odds ?? '',
    ].join('|');
    byLookup.set(key, row);
  }

  return rankedSignals.map((signal) => {
    const key = [
      signal.player || '',
      signal.team || '',
      signal.prop || '',
      signal.marketLine ?? '',
      signal.recommendation || '',
      signal.odds ?? '',
    ].join('|');
    const source = byLookup.get(key);
    if (!source) {
      throw new Error(`Missing top-prop source row for ${key}`);
    }
    return toCard(source, signal);
  });
}
