/**
 * Turns a revealed result (PrivateState.lastResult) into the itemised points the phone shows.
 * Only server-provided numbers are used; fixed bonuses (lines, full board, Double Down, table
 * challenge) come from the engine's POINTS table so the phone and the scorer never disagree.
 */
import { POINTS, type Difficulty, type PrivateState, type PublicState } from '@pintrush/engine';
import { LINE_NAMES } from '../../components/game/board-model';

export type LastResult = NonNullable<PrivateState['lastResult']>;

export type PointsTone = 'base' | 'speed' | 'bonus' | 'penalty' | 'team' | 'token';

export interface PointsItem {
  key: string;
  label: string;
  /** null for non-point rewards (a power-up token). */
  points: number | null;
  tone: PointsTone;
}

export interface BreakdownOptions {
  difficulty: Difficulty;
  /** TABLE_WARS: this player's table won the challenge (every participating member gets +200). */
  tableChallengeWon?: boolean;
}

export function pointsBreakdown(result: LastResult, opts: BreakdownOptions): PointsItem[] {
  const items: PointsItem[] = [];
  if (result.correct && result.points > 0) {
    items.push({
      key: 'correct',
      label: opts.difficulty === 'DIFFICULT' ? 'Correct · hard question' : 'Correct answer',
      points: result.points,
      tone: 'base',
    });
  }
  if (result.speedBonus > 0) items.push({ key: 'speed', label: 'Speed bonus', points: result.speedBonus, tone: 'speed' });
  if (result.doubleDown === 'WON') items.push({ key: 'dd', label: 'Double Down won', points: POINTS.DOUBLE_DOWN_WIN, tone: 'bonus' });
  if (result.doubleDown === 'LOST') items.push({ key: 'dd', label: 'Double Down lost', points: POINTS.DOUBLE_DOWN_LOSS, tone: 'penalty' });
  for (const line of result.newLines) {
    items.push({ key: `line-${line}`, label: `Line · ${LINE_NAMES[line] ?? 'Line'}`, points: POINTS.LINE, tone: 'bonus' });
  }
  if (result.fullBoard) items.push({ key: 'full', label: 'Full board', points: POINTS.FULL_BOARD, tone: 'bonus' });
  if (opts.tableChallengeWon) items.push({ key: 'table', label: 'Table challenge won', points: POINTS.TABLE_CHALLENGE, tone: 'team' });
  if (result.tokenGranted) items.push({ key: 'token', label: 'Power-up earned', points: null, tone: 'token' });
  return items;
}

export function totalPoints(items: readonly PointsItem[]): number {
  return items.reduce((sum, item) => sum + (item.points ?? 0), 0);
}

export type ResultKind = 'correct' | 'incorrect' | 'missed';

export function resultKind(result: LastResult): ResultKind {
  if (result.correct) return 'correct';
  return result.answered ? 'incorrect' : 'missed';
}

/**
 * TABLE_WARS: did this player's unit win the challenge? Teams are identified by team id; when the
 * host merges solos into the team board, a solo player's unit id is `solo:<playerId>`.
 */
export function wonTableChallenge(winners: readonly string[] | undefined, me: Pick<PrivateState, 'teamId' | 'playerId' | 'inRound'>): boolean {
  if (!winners || winners.length === 0 || !me.inRound) return false;
  return (me.teamId !== null && winners.includes(me.teamId)) || winners.includes(`solo:${me.playerId}`);
}

/** Display names of the winning tables, looked up on the public team boards. */
export function challengeWinnerNames(
  winners: readonly string[] | undefined,
  pub: Pick<PublicState, 'teamLeaderboard' | 'soloLeaderboard'>,
): string[] {
  if (!winners) return [];
  const names = new Map<string, string>();
  for (const t of [...pub.teamLeaderboard, ...(pub.soloLeaderboard ?? [])]) names.set(t.teamId, t.name);
  return winners.map((id) => names.get(id)).filter((n): n is string => typeof n === 'string');
}

/** One sentence for screen readers, e.g. "Correct! +138 points. Line complete, +250." */
export function resultAnnouncement(result: LastResult, items: readonly PointsItem[]): string {
  const kind = resultKind(result);
  const head = kind === 'correct' ? 'Correct!' : kind === 'incorrect' ? 'Not this time.' : "Time's up, no answer.";
  const total = totalPoints(items);
  const parts = [head];
  if (total !== 0) parts.push(`${total > 0 ? '+' : 'minus '}${Math.abs(total)} points.`);
  if (result.fullBoard) parts.push('Full board!');
  else if (result.newLines.length > 0) parts.push(result.newLines.length > 1 ? `${result.newLines.length} lines complete!` : 'Line complete!');
  if (result.tokenGranted) parts.push('You earned a power-up.');
  return parts.join(' ');
}
