89 lines
3.6 KiB
TypeScript
89 lines
3.6 KiB
TypeScript
|
|
/**
|
||
|
|
* Shared helpers for "shared major" simulators (CS2, tennis).
|
||
|
|
*
|
||
|
|
* A major (a real tournament) can be linked to several sports_season windows.
|
||
|
|
* The bracket/stage structure is built and scored once on the PRIMARY window;
|
||
|
|
* sibling windows hold no structure of their own. When simulating a sibling, the
|
||
|
|
* simulator must read the structure from the primary and translate the primary
|
||
|
|
* window's season_participant ids into the local window's ids via the shared
|
||
|
|
* canonical participant. These helpers centralise that resolution + translation.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { getPrimaryEventForTournament } from "~/models/scoring-event";
|
||
|
|
import { findParticipantsBySportsSeasonId } from "~/models/season-participant";
|
||
|
|
|
||
|
|
/** Maps a participant id from one window to another (or passes it through). */
|
||
|
|
export type IdTranslator = (id: string | null) => string | null;
|
||
|
|
|
||
|
|
/** Identity translation — used when structure lives in the event's own window. */
|
||
|
|
export const IDENTITY_TR: IdTranslator = (id) => id;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build a translator that maps a PRIMARY window's season_participant id to the
|
||
|
|
* equivalent id in a LOCAL (sibling) window, bridging via the shared canonical
|
||
|
|
* participant. Ids that can't be bridged (no canonical link, or no local
|
||
|
|
* counterpart) pass through unchanged so structure is never silently dropped.
|
||
|
|
*/
|
||
|
|
export function buildSpTranslator(
|
||
|
|
primaryParticipants: Array<{ id: string; participantId: string | null }>,
|
||
|
|
localParticipants: Array<{ id: string; participantId: string | null }>
|
||
|
|
): IdTranslator {
|
||
|
|
const primaryToCanonical = new Map(
|
||
|
|
primaryParticipants
|
||
|
|
.filter((p) => p.participantId)
|
||
|
|
.map((p) => [p.id, p.participantId as string])
|
||
|
|
);
|
||
|
|
const canonicalToLocal = new Map(
|
||
|
|
localParticipants
|
||
|
|
.filter((p) => p.participantId)
|
||
|
|
.map((p) => [p.participantId as string, p.id])
|
||
|
|
);
|
||
|
|
return (id: string | null): string | null => {
|
||
|
|
if (!id) return id;
|
||
|
|
const canonical = primaryToCanonical.get(id);
|
||
|
|
if (!canonical) return id;
|
||
|
|
return canonicalToLocal.get(canonical) ?? id;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface StructureSource {
|
||
|
|
/** Event id whose bracket/stage rows hold this event's structure. */
|
||
|
|
sourceId: string;
|
||
|
|
/** Translate the source window's participant ids into the local window's. */
|
||
|
|
tr: IdTranslator;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resolve where an event's bracket/stage structure lives and how to map its
|
||
|
|
* participant ids into the local window. For a primary/standalone/non-linked
|
||
|
|
* event this is the event itself with identity translation. For a sibling
|
||
|
|
* (tournament-linked, non-primary) event, the structure is the primary event and
|
||
|
|
* ids are translated primary→local.
|
||
|
|
*
|
||
|
|
* `translatorCache` is keyed by primary sports_season id so a window's majors
|
||
|
|
* (whose primaries may live in different windows) each build their translator
|
||
|
|
* once.
|
||
|
|
*/
|
||
|
|
export async function resolveStructureSource(
|
||
|
|
event: { id: string; tournamentId: string | null; isPrimary: boolean },
|
||
|
|
localParticipants: Array<{ id: string; participantId: string | null }>,
|
||
|
|
translatorCache: Map<string, IdTranslator>
|
||
|
|
): Promise<StructureSource> {
|
||
|
|
if (!event.tournamentId || event.isPrimary) {
|
||
|
|
return { sourceId: event.id, tr: IDENTITY_TR };
|
||
|
|
}
|
||
|
|
const primary = await getPrimaryEventForTournament(event.tournamentId);
|
||
|
|
if (!primary || primary.id === event.id) {
|
||
|
|
return { sourceId: event.id, tr: IDENTITY_TR };
|
||
|
|
}
|
||
|
|
let tr = translatorCache.get(primary.sportsSeasonId);
|
||
|
|
if (!tr) {
|
||
|
|
const primaryParticipants = await findParticipantsBySportsSeasonId(
|
||
|
|
primary.sportsSeasonId
|
||
|
|
);
|
||
|
|
tr = buildSpTranslator(primaryParticipants, localParticipants);
|
||
|
|
translatorCache.set(primary.sportsSeasonId, tr);
|
||
|
|
}
|
||
|
|
return { sourceId: primary.id, tr };
|
||
|
|
}
|