import type { ReactNode } from 'react';
import type { Standing, TeamStanding } from '@pintrush/engine';
import { cn } from '@/lib/cn';

export interface LeaderboardRow {
  key: string;
  rank: number;
  name: string;
  /** Pre-formatted score ("2,310", "1,234.5"). */
  score: string;
  /** Small second line (team name, member count). */
  detail?: string | null;
  /** This viewer's row. */
  highlight?: boolean;
  /** Extra marker after the name (e.g. "You", "Finalist"). */
  badge?: ReactNode;
}

const INT = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });
const ONE = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 });

/** Player standings → rows. `teamNames` maps team ids to names for the detail line. */
export function standingRows(
  standings: readonly Standing[],
  opts: { highlightId?: string | null; teamNames?: ReadonlyMap<string, string>; limit?: number } = {},
): LeaderboardRow[] {
  return standings.slice(0, opts.limit ?? standings.length).map((st) => ({
    key: st.playerId,
    rank: st.rank,
    name: st.nickname,
    score: INT.format(st.points),
    detail: st.teamId ? (opts.teamNames?.get(st.teamId) ?? null) : null,
    highlight: st.playerId === opts.highlightId,
  }));
}

/** Team standings (mean score with one decimal) → rows. */
export function teamRows(teams: readonly TeamStanding[], opts: { highlightId?: string | null; limit?: number } = {}): LeaderboardRow[] {
  return teams.slice(0, opts.limit ?? teams.length).map((t) => ({
    key: t.teamId,
    rank: t.rank,
    name: t.name,
    score: ONE.format(t.score),
    detail: t.isSolo ? 'Solo' : `${t.members} ${t.members === 1 ? 'player' : 'players'}`,
    highlight: t.teamId === opts.highlightId,
  }));
}

const rankTone = (rank: number) =>
  rank === 1
    ? 'bg-amber text-on-bright'
    : rank === 2
      ? 'bg-cyan-soft text-on-bright'
      : rank === 3
        ? 'bg-violet-soft text-on-bright'
        : 'bg-raised text-muted';

/**
 * Ranked list. Ranks are printed (and medal colours are only an extra), so order never relies on
 * colour. `size="tv"` scales everything up for 30 ft viewing.
 */
export function LeaderboardList({
  rows,
  label,
  emptyText = 'No scores yet — answer a question to get on the board.',
  size = 'phone',
  className,
}: {
  rows: readonly LeaderboardRow[];
  label: string;
  emptyText?: string;
  size?: 'phone' | 'tv';
  className?: string;
}) {
  if (rows.length === 0) {
    return (
      <p className={cn('rounded-2xl border border-dashed border-line-strong/70 px-4 py-5 text-center text-sm text-muted', className)}>{emptyText}</p>
    );
  }
  const tv = size === 'tv';
  return (
    <ol aria-label={label} className={cn('flex flex-col', tv ? 'gap-3' : 'gap-1.5', className)}>
      {rows.map((row) => (
        <li
          key={row.key}
          className={cn(
            'flex items-center rounded-xl border',
            tv ? 'min-h-20 gap-5 px-5' : 'min-h-12 gap-3 px-2.5 py-1.5',
            row.highlight ? 'border-violet/70 bg-violet/20' : 'border-transparent bg-surface/70',
          )}
        >
          <span
            className={cn(
              'num grid shrink-0 place-items-center rounded-lg font-display font-bold',
              tv ? 'size-14 text-3xl' : 'size-8 text-sm',
              rankTone(row.rank),
            )}
          >
            <span className="sr-only">Rank </span>
            {row.rank}
          </span>
          <span className="min-w-0 flex-1">
            <span className={cn('flex items-center gap-2 font-semibold', tv ? 'text-3xl' : 'text-base')}>
              <span className={cn('truncate', row.highlight && 'text-violet-soft')}>{row.name}</span>
              {row.badge}
            </span>
            {row.detail && <span className={cn('block truncate text-subtle', tv ? 'text-xl' : 'text-xs')}>{row.detail}</span>}
          </span>
          <span className={cn('num shrink-0 font-display font-bold', tv ? 'text-3xl' : 'text-base')}>
            {row.score}
            <span className="sr-only"> points</span>
          </span>
        </li>
      ))}
    </ol>
  );
}
