/** Display formatting for the TV. All inputs are server epoch ms / durations; nothing reads the clock. */

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

/** 3844 → "3,844" (negative balances keep a real minus sign). */
export function formatPoints(points: number): string {
  const text = pointsFormat.format(Math.abs(Math.round(points)));
  return points < 0 && text !== '0' ? `−${text}` : text;
}

/** Team scores are means with one decimal; whole numbers drop the ".0" so they read cleanly. */
export function formatTeamScore(score: number): string {
  const text = teamScoreFormat.format(Math.abs(score));
  return score < 0 && text !== '0' ? `−${text}` : text;
}

/** Answer speed with one decimal: 5585 → "5.6s". */
export function formatSeconds(ms: number): string {
  return `${(Math.max(0, ms) / 1000).toFixed(1)}s`;
}

/** Whole seconds left, rounded up so "1" shows until the very end. */
export function secondsLeft(ms: number): number {
  return Math.max(0, Math.ceil(ms / 1000));
}

/** Countdown clock: 42_000 → "0:42", 3_725_000 → "1:02:05". Rounds up like `secondsLeft`. */
export function formatClock(ms: number): string {
  const total = secondsLeft(ms);
  const h = Math.floor(total / 3600);
  const m = Math.floor((total % 3600) / 60);
  const s = total % 60;
  const ss = String(s).padStart(2, '0');
  return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;
}

/** Option letters A, B, C, D… by position in the question's option list. */
export function optionLetter(index: number): string {
  return index >= 0 && index < 26 ? String.fromCharCode(65 + index) : String(index + 1);
}

interface CalendarDay {
  year: number;
  month: number;
  day: number;
}

function calendarDay(epochMs: number, timeZone: string): CalendarDay {
  const parts = new Intl.DateTimeFormat('en-US', { timeZone, year: 'numeric', month: 'numeric', day: 'numeric' }).formatToParts(epochMs);
  const get = (type: string) => Number(parts.find((p) => p.type === type)?.value ?? 0);
  return { year: get('year'), month: get('month'), day: get('day') };
}

function dayNumber(d: CalendarDay): number {
  return Math.round(Date.UTC(d.year, d.month - 1, d.day) / 86_400_000);
}

/** A valid IANA zone, falling back to UTC so a bad venue setting never crashes the TV. */
export function safeTimeZone(timeZone: string | null | undefined): string {
  if (!timeZone) return 'UTC';
  try {
    new Intl.DateTimeFormat('en-US', { timeZone });
    return timeZone;
  } catch {
    return 'UTC';
  }
}

/**
 * When a scheduled event starts, in the venue's time zone, relative to the server's "now":
 * "Tonight · 8:00 PM", "Tomorrow · 8:00 PM", "Fri · 8:00 PM" (within a week) or "Nov 7 · 8:00 PM".
 */
export function formatEventTime(epochMs: number, nowMs: number, timeZone: string): string {
  const zone = safeTimeZone(timeZone);
  const time = new Intl.DateTimeFormat('en-US', { timeZone: zone, hour: 'numeric', minute: '2-digit' }).format(epochMs);
  const diff = dayNumber(calendarDay(epochMs, zone)) - dayNumber(calendarDay(nowMs, zone));
  let day: string;
  if (diff === 0) {
    const hour = Number(new Intl.DateTimeFormat('en-US', { timeZone: zone, hour: 'numeric', hourCycle: 'h23' }).format(epochMs));
    day = hour >= 17 ? 'Tonight' : 'Today';
  } else if (diff === 1) {
    day = 'Tomorrow';
  } else if (diff > 1 && diff < 7) {
    day = new Intl.DateTimeFormat('en-US', { timeZone: zone, weekday: 'short' }).format(epochMs);
  } else {
    day = new Intl.DateTimeFormat('en-US', { timeZone: zone, month: 'short', day: 'numeric' }).format(epochMs);
  }
  return `${day} · ${time}`;
}

/** "pintrush.com/join/HARBOR" — the short, typeable form of the join link printed under QR codes. */
export function displayJoinUrl(siteUrl: string, joinCode: string): string {
  let host: string;
  try {
    host = new URL(siteUrl).host;
  } catch {
    host = siteUrl.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
  }
  return `${host.replace(/^www\./, '')}/join/${joinCode}`;
}
