import type {
  PublicTopGameCard,
  PublicTopPickEntry,
  PublicTopPropCard,
} from '../contracts/forecast-public-contract';

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

function round1(value: number): number {
  return Math.round(value * 10) / 10;
}

function agreementPoints(label: PublicTopPropCard['agreementLabel']): number {
  if (label === 'HIGH') return 10;
  if (label === 'MEDIUM') return 6;
  return 2;
}

function marketPoints(label: PublicTopPropCard['marketQualityLabel']): number {
  if (label === 'STRONG') return 10;
  if (label === 'GOOD') return 7;
  if (label === 'FAIR') return 4;
  return 1;
}

function clvPoints(card: PublicTopPropCard): number {
  const fitScore = clamp(card.clvFitScore / 100, 0, 1) * 28;
  return card.clvTracked ? fitScore + 8 : fitScore;
}

function propSignalPoints(signal: PublicTopPropCard['signal']): number {
  if (signal === 'STRONG') return 10;
  if (signal === 'GOOD') return 6;
  return 3;
}

function propSignalMetric(signal: PublicTopPropCard['signal']): number {
  if (signal === 'STRONG') return 20;
  if (signal === 'GOOD') return 12;
  return 6;
}

export function computeTopPropBoardScore(card: PublicTopPropCard): number {
  const clvScore = clvPoints(card);
  const edgeScore = clamp(card.edgePct / 30, 0, 1) * 28;
  const confidenceScore = clamp((card.projectedProbability - 55) / 25, 0, 1) * 16;
  const signalScore = propSignalPoints(card.signal);
  const agreementScore = agreementPoints(card.agreementLabel);
  const marketScore = marketPoints(card.marketQualityLabel);
  const rankScore = clamp(card.rankScore / 100, 0, 1) * 10;
  return round1(clvScore + edgeScore + confidenceScore + signalScore + agreementScore + marketScore + rankScore);
}

export function computeTopGameBoardScore(card: PublicTopGameCard): number {
  const edge = Number(card.edge ?? 0);
  const confidencePct = Number(card.confidencePct ?? 0);
  const valueRating = Number(card.valueRating ?? 0);
  const edgeScore = clamp(edge / 5, 0, 1) * 42;
  const confidenceScore = clamp((confidencePct - 55) / 20, 0, 1) * 24;
  const valueScore = clamp(valueRating / 100, 0, 1) * 14;
  const signalScore = (card.hasSharpSignal ? 10 : 0) + (card.hasSteamSignal ? 5 : 0);
  return round1(edgeScore + confidenceScore + valueScore + signalScore);
}

export function buildTopPickEntries(
  props: PublicTopPropCard[],
  games: PublicTopGameCard[],
  limit = 12,
): PublicTopPickEntry[] {
  const entries = [
    ...props.map((prop) => ({
      kind: 'prop' as const,
      boardScore: computeTopPropBoardScore(prop),
      clvMetric: prop.clvFitScore,
      edgeMetric: prop.edgePct,
      confidenceMetric: prop.projectedProbability,
      signalMetric: propSignalMetric(prop.signal) + agreementPoints(prop.agreementLabel),
      prop,
    })),
    ...games.map((game) => ({
      kind: 'game' as const,
      boardScore: computeTopGameBoardScore(game),
      clvMetric: 0,
      edgeMetric: Number(game.edge ?? 0),
      confidenceMetric: Number(game.confidencePct ?? 0),
      signalMetric: (game.hasSharpSignal ? 20 : 0) + (game.hasSteamSignal ? 10 : 0),
      game,
    })),
  ]
    .sort((a, b) => {
      if (b.boardScore !== a.boardScore) return b.boardScore - a.boardScore;
      if (b.edgeMetric !== a.edgeMetric) return b.edgeMetric - a.edgeMetric;
      if (b.confidenceMetric !== a.confidenceMetric) return b.confidenceMetric - a.confidenceMetric;
      const aStart = new Date((a.kind === 'prop' ? a.prop.startsAt : a.game.startsAt) || '').getTime();
      const bStart = new Date((b.kind === 'prop' ? b.prop.startsAt : b.game.startsAt) || '').getTime();
      return aStart - bStart;
    })
    .slice(0, limit);

  return entries.map((entry, index) => ({
    kind: entry.kind,
    rankPosition: index + 1,
    boardScore: entry.boardScore,
    clvMetric: entry.clvMetric,
    edgeMetric: entry.edgeMetric,
    confidenceMetric: entry.confidenceMetric,
    signalMetric: entry.signalMetric,
    ...(entry.kind === 'prop' ? { prop: entry.prop } : { game: entry.game }),
  }));
}
