/** * Extracts the canonical tournament identity — `(name, year)` — from a scoring * event. Used both by the Phase 2 backfill and by the admin create-event flow * that auto-provisions a canonical tournament for new qualifying events. * * Rules: * - If the event name ends with a 4-digit year, strip it; that year wins. * - Otherwise, fall back to the year portion of eventDate (YYYY-MM-DD). * - If neither produces a year, throw. */ export interface ScoringEventIdentityInput { name: string; eventDate: string | Date | null | undefined; eventType: string; } export interface TournamentIdentity { name: string; year: number; } const YEAR_SUFFIX = /\s+(\d{4})\s*$/; export function extractTournamentIdentity(ev: ScoringEventIdentityInput): TournamentIdentity { const trimmed = ev.name.trim(); const yearMatch = trimmed.match(YEAR_SUFFIX); let cleanName = trimmed; let year: number | null = null; if (yearMatch) { year = parseInt(yearMatch[1], 10); cleanName = trimmed.replace(YEAR_SUFFIX, "").trim(); } if (year === null && ev.eventDate !== null && ev.eventDate !== undefined) { const iso = ev.eventDate instanceof Date ? ev.eventDate.toISOString() : String(ev.eventDate); year = parseInt(iso.slice(0, 4), 10); } if (year === null || Number.isNaN(year)) { throw new Error( `cannot determine year for event "${ev.name}" — no year in name and eventDate is null`, ); } return { name: cleanName, year }; }