export interface SportsResultsRecapOptions {
  clipSeconds?: number;
  clipCount?: number;
  aspectRatio?: string;
  recapDate?: string;
}

export interface SportsResultsRecapRow {
  league: string;
  homeTeam: string;
  awayTeam: string;
  winnerPick: string | null;
  outcome: 'win' | 'loss';
  actualWinner: string | null;
  actualScore: string | null;
  settledAt: string | null;
}

export interface SportsResultsRecapClip {
  index: number;
  durationSeconds: number;
  slug: string;
  visualGoal: string;
  voiceover: string;
  overlay: string;
  prompt: string;
}

export interface SportsResultsRecapPromptResult {
  sport: string;
  league: string;
  title: string;
  recapDate: string;
  totalDurationSeconds: number;
  clipSeconds: number;
  record: {
    wins: number;
    losses: number;
    total: number;
    winPct: number;
  };
  sourceSummary: string[];
  clips: SportsResultsRecapClip[];
  prompt: string;
}

type SportTemplate = {
  sport: string;
  title: string;
  introHook: string;
  boardLabel: string;
  boardStyle: string;
  recapCloser: string;
};

const MAX_CLIP_SECONDS = 8;

const SPORT_TEMPLATES: Record<string, SportTemplate> = {
  baseball: {
    sport: 'baseball',
    title: 'Rainmaker Diamond Recap',
    introHook: 'Last night left cleat marks all over the board.',
    boardLabel: 'Diamond Recap',
    boardStyle: 'green-screen baseball recap board with score bugs, inning bars, and graded-pick stamps',
    recapCloser: 'That was yesterday. Reset and get ready for the next card.',
  },
  basketball: {
    sport: 'basketball',
    title: 'Rainmaker Hardwood Recap',
    introHook: 'The final buzzer hit and the board told the truth.',
    boardLabel: 'Hardwood Recap',
    boardStyle: 'green-screen hardwood recap board with final-score tiles, cash/miss markers, and ticker graphics',
    recapCloser: 'Own the misses, bank the hits, and move clean into the next slate.',
  },
  football: {
    sport: 'football',
    title: 'Rainmaker Gridiron Recap',
    introHook: 'Yesterday closed with a few bruises and a few cash stamps.',
    boardLabel: 'Gridiron Recap',
    boardStyle: 'green-screen football recap board with final-score cards, helmet icons, and graded-ticket markers',
    recapCloser: 'No victory laps, no excuses. Just a clean recap and the next edge.',
  },
  hockey: {
    sport: 'hockey',
    title: 'Rainmaker Ice Recap',
    introHook: 'The horn sounded and the slip report is in.',
    boardLabel: 'Ice Recap',
    boardStyle: 'green-screen hockey recap board with scoreboard tiles, shot-counter bands, and graded-pick icons',
    recapCloser: 'Keep the process cold and the next board gets easier.',
  },
  soccer: {
    sport: 'soccer',
    title: 'Rainmaker Pitch Recap',
    introHook: 'The final whistles are in and the ledger is settled.',
    boardLabel: 'Pitch Recap',
    boardStyle: 'green-screen soccer recap board with score strips, badge overlays, and result markers',
    recapCloser: 'Take the honest read, then move to the next fixture.',
  },
  generic: {
    sport: 'generic',
    title: 'Rainmaker Sports Recap',
    introHook: 'Yesterday is settled. Here is what cashed and what slipped.',
    boardLabel: 'Sports Recap',
    boardStyle: 'green-screen sports recap board with score tiles, record badges, and graded-pick markers',
    recapCloser: 'Sharp recap, short memory, next board.',
  },
};

function inferSport(league: string): keyof typeof SPORT_TEMPLATES {
  const normalized = String(league || '').trim().toLowerCase();
  if (['mlb'].includes(normalized)) return 'baseball';
  if (['nba', 'wnba', 'ncaab'].includes(normalized)) return 'basketball';
  if (['nfl', 'ncaaf'].includes(normalized)) return 'football';
  if (['nhl'].includes(normalized)) return 'hockey';
  if (['epl', 'la_liga', 'bundesliga', 'serie_a', 'ligue_1', 'champions_league', 'mls'].includes(normalized)) {
    return 'soccer';
  }
  return 'generic';
}

function clampClipSeconds(value?: number): number {
  const parsed = Number(value ?? MAX_CLIP_SECONDS);
  if (!Number.isFinite(parsed)) return MAX_CLIP_SECONDS;
  return Math.max(4, Math.min(MAX_CLIP_SECONDS, Math.round(parsed)));
}

function toLeagueLabel(league: string): string {
  const normalized = String(league || '').trim().toLowerCase();
  if (!normalized || normalized === 'all') return 'ALL SPORTS';
  return normalized.toUpperCase().replace(/_/g, ' ');
}

function parseScore(score: string | null): { awayScore: number; homeScore: number } {
  if (!score) return { awayScore: 0, homeScore: 0 };
  const match = score.match(/(\d+)\s*[-–]\s*(\d+)/);
  if (!match) return { awayScore: 0, homeScore: 0 };
  return {
    awayScore: Number(match[1]) || 0,
    homeScore: Number(match[2]) || 0,
  };
}

function formatMatchup(row: SportsResultsRecapRow): string {
  return `${row.awayTeam} at ${row.homeTeam}`;
}

function buildSourceLine(row: SportsResultsRecapRow): string {
  const { awayScore, homeScore } = parseScore(row.actualScore);
  return [
    toLeagueLabel(row.league),
    `${row.awayTeam} ${awayScore} - ${row.homeTeam} ${homeScore}`,
    `Graded result: ${row.outcome.toUpperCase()}`,
  ].join(' | ');
}

function buildRecord(rows: SportsResultsRecapRow[]) {
  const wins = rows.filter((row) => row.outcome === 'win').length;
  const losses = rows.filter((row) => row.outcome === 'loss').length;
  const total = wins + losses;
  const winPct = total > 0 ? Math.round((wins / total) * 100) : 0;
  return { wins, losses, total, winPct };
}

function selectStoryRows(rows: SportsResultsRecapRow[], targetCount: number): SportsResultsRecapRow[] {
  const wins = rows.filter((row) => row.outcome === 'win');
  const losses = rows.filter((row) => row.outcome === 'loss');
  const selected: SportsResultsRecapRow[] = [];

  for (const row of wins) {
    if (selected.length >= targetCount) break;
    selected.push(row);
  }
  for (const row of losses) {
    if (selected.length >= targetCount) break;
    selected.push(row);
  }
  for (const row of rows) {
    if (selected.length >= targetCount) break;
    if (selected.includes(row)) continue;
    selected.push(row);
  }

  return selected;
}

function outcomeTag(row: SportsResultsRecapRow): string {
  return row.outcome === 'win' ? 'Cash stamp' : 'Slip alert';
}

function sceneVisual(row: SportsResultsRecapRow): string {
  const resultStyle = row.outcome === 'win' ? 'green cash stamp' : 'red miss stamp';
  return `${resultStyle} lands over the ${formatMatchup(row)} scoreboard tile while the presenter taps the final score and the graded-pick marker.`;
}

function buildOverlay(row: SportsResultsRecapRow): string {
  const { awayScore, homeScore } = parseScore(row.actualScore);
  const verdict = row.outcome === 'win' ? 'WIN' : 'LOSS';
  return `${formatMatchup(row)} | ${awayScore}-${homeScore} | ${verdict}`;
}

function buildVoiceover(row: SportsResultsRecapRow): string {
  const { awayScore, homeScore } = parseScore(row.actualScore);
  const base = `${row.awayTeam} finished ${awayScore}, ${row.homeTeam} finished ${homeScore}.`;
  if (row.outcome === 'win') {
    return `${outcomeTag(row)} on ${formatMatchup(row)}. ${base} Rainmaker graded this one as a win.`;
  }
  return `${outcomeTag(row)} on ${formatMatchup(row)}. ${base} Rainmaker graded this one as a loss. Own it and move on.`;
}

function buildScenePrompt(params: {
  template: SportTemplate;
  clipSeconds: number;
  aspectRatio: string;
  row: SportsResultsRecapRow;
  clipIndex: number;
}): string {
  const { template, clipSeconds, aspectRatio, row, clipIndex } = params;
  return [
    `Create clip ${clipIndex} for a stitched ${template.title} sequence.`,
    `Hard cap: ${clipSeconds} seconds. Aspect ratio: ${aspectRatio}.`,
    'The bot will stitch this clip with the others, so end on a clean hold with no fade to black.',
    'Presenter: glamorous, confident, green-screen sports forecaster; broadcast-safe, polished, non-explicit.',
    `Background: ${template.boardStyle}.`,
    `Featured recap: ${formatMatchup(row)}.`,
    `Visual action: ${sceneVisual(row)}`,
    `Voiceover target: ${buildVoiceover(row)}`,
    `Overlay text: ${buildOverlay(row)}.`,
    'Camera: medium shot, crisp scoreboard gestures, one clean beat at the end for stitching.',
  ].join(' ');
}

export function buildSportsResultsRecapPrompt(
  rows: SportsResultsRecapRow[],
  options: SportsResultsRecapOptions = {},
): SportsResultsRecapPromptResult {
  const league = String(rows[0]?.league || 'all').trim().toLowerCase() || 'all';
  const sportKey = inferSport(league);
  const template = SPORT_TEMPLATES[sportKey];
  const clipSeconds = clampClipSeconds(options.clipSeconds);
  const clipCount = Math.max(2, Math.min(4, Math.round(Number(options.clipCount) || 4)));
  const aspectRatio = String(options.aspectRatio || '9:16').trim() || '9:16';
  const recapDate = String(options.recapDate || '').trim() || 'previous day';
  const record = buildRecord(rows);
  const selected = selectStoryRows(rows, Math.max(1, clipCount - 1));

  const clips: SportsResultsRecapClip[] = [
    {
      index: 1,
      durationSeconds: Math.max(5, clipSeconds - 1),
      slug: 'intro',
      visualGoal: `Open on the ${template.boardLabel} and establish the record fast.`,
      voiceover: `${template.introHook} Yesterday closed ${record.wins}-${record.losses}, ${record.winPct} percent.`,
      overlay: `${template.title} | ${toLeagueLabel(league)} | ${record.wins}-${record.losses}`,
      prompt: [
        `Create clip 1 of a stitched ${template.title} sequence.`,
        `Hard cap: ${Math.max(5, clipSeconds - 1)} seconds. Aspect ratio: ${aspectRatio}.`,
        'The bot will stitch this clip with the others, so end on a crisp hold for the next cut.',
        'Presenter: glamorous, confident, green-screen sports forecaster; broadcast-safe, polished, non-explicit.',
        `Background: ${template.boardStyle}.`,
        `Voiceover target: ${template.introHook} Yesterday closed ${record.wins}-${record.losses}, ${record.winPct} percent.`,
        `On-screen board should show the day record, date ${recapDate}, and preview the next ${selected.length} graded plays with win/loss markers.`,
        'Camera: medium-wide opening, one clean step toward the board, finish centered for stitching.',
      ].join(' '),
    },
  ];

  selected.forEach((row, offset) => {
    const clipIndex = offset + 2;
    clips.push({
      index: clipIndex,
      durationSeconds: clipSeconds,
      slug: row.outcome === 'win' ? `cash-${clipIndex - 1}` : `miss-${clipIndex - 1}`,
      visualGoal: sceneVisual(row),
      voiceover: buildVoiceover(row),
      overlay: buildOverlay(row),
      prompt: buildScenePrompt({
        template,
        clipSeconds,
        aspectRatio,
        row,
        clipIndex,
      }),
    });
  });

  if (clips.length > 1) {
    const lastClip = clips[clips.length - 1];
    lastClip.voiceover = `${lastClip.voiceover} ${template.recapCloser}`;
    lastClip.prompt = `${lastClip.prompt} End with a direct-to-camera recap closer and a clean 1-beat hold.`;
  }

  const sourceSummary = rows.slice(0, Math.max(clipCount, 6)).map(buildSourceLine);
  const prompt = [
    `${template.title}`,
    '',
    `League: ${toLeagueLabel(league)}`,
    `Recap date: ${recapDate}`,
    `Clip format: ${clips.length} stitched clips, each ${clipSeconds} seconds max, ${aspectRatio}`,
    '',
    `Record: ${record.wins}-${record.losses} (${record.winPct}%)`,
    '',
    'Use these real settled Rainmaker results only:',
    ...sourceSummary.map((line) => `- ${line}`),
    '',
    'Clip plan:',
    ...clips.flatMap((clip) => [
      `Clip ${clip.index} (${clip.durationSeconds}s max)`,
      `Goal: ${clip.visualGoal}`,
      `Voiceover: ${clip.voiceover}`,
      `Overlay: ${clip.overlay}`,
      `Prompt: ${clip.prompt}`,
      '',
    ]),
    'Stitch notes:',
    '- Keep every clip self-contained and end on a clean hold for seamless stitching.',
    '- Reuse the same presenter, wardrobe palette, studio lighting, and recap-board style across all clips.',
    '- Keep the tone sharp and honest. Flex the hits, own the misses, do not fabricate results.',
    '- The final stitched piece should feel like one recap segment built from real settled Rainmaker plays.',
  ].join('\n');

  return {
    sport: template.sport,
    league,
    title: template.title,
    recapDate,
    totalDurationSeconds: clips.reduce((sum, clip) => sum + clip.durationSeconds, 0),
    clipSeconds,
    record,
    sourceSummary,
    clips,
    prompt,
  };
}
