import { eq, asc } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; export type PlayoffMatchGame = typeof schema.playoffMatchGames.$inferSelect; export type NewPlayoffMatchGame = typeof schema.playoffMatchGames.$inferInsert; export type PlayoffMatchGameStatus = (typeof schema.playoffMatchGameStatusEnum.enumValues)[number]; // ─── Pure helpers ──────────────────────────────────────────────────────────── export interface SeriesScore { participant1Wins: number; participant2Wins: number; gamesPlayed: number; } /** * Tally wins from a list of completed games. * participant1Id / participant2Id on the *match* are the two series participants; * winnerId on each game must equal one of them. */ export function computeSeriesScore( games: Pick[], participant1Id: string, participant2Id: string ): SeriesScore { let participant1Wins = 0; let participant2Wins = 0; let gamesPlayed = 0; for (const game of games) { if (game.status !== "complete") continue; gamesPlayed++; if (game.winnerId === participant1Id) participant1Wins++; else if (game.winnerId === participant2Id) participant2Wins++; } return { participant1Wins, participant2Wins, gamesPlayed }; } /** * Returns true when one participant has enough wins to clinch a best-of-N series. * e.g. best-of-7 requires 4 wins. */ export function isSeriesComplete( games: Pick[], participant1Id: string, participant2Id: string, requiredWins: number ): boolean { const { participant1Wins, participant2Wins } = computeSeriesScore( games, participant1Id, participant2Id ); return participant1Wins >= requiredWins || participant2Wins >= requiredWins; } /** * Derive the series leader's participantId, or null if tied / no completed games. */ export function getSeriesLeader( games: Pick[], participant1Id: string, participant2Id: string ): string | null { const { participant1Wins, participant2Wins } = computeSeriesScore( games, participant1Id, participant2Id ); if (participant1Wins > participant2Wins) return participant1Id; if (participant2Wins > participant1Wins) return participant2Id; return null; } // ─── DB operations ─────────────────────────────────────────────────────────── export async function createGame(data: NewPlayoffMatchGame): Promise { const db = database(); const [game] = await db .insert(schema.playoffMatchGames) .values(data) .returning(); return game; } export async function findGamesByMatchId( playoffMatchId: string ): Promise { const db = database(); return await db.query.playoffMatchGames.findMany({ where: eq(schema.playoffMatchGames.playoffMatchId, playoffMatchId), orderBy: [asc(schema.playoffMatchGames.gameNumber)], with: { winner: true }, }); } export async function findGameById(id: string): Promise { const db = database(); return await db.query.playoffMatchGames.findFirst({ where: eq(schema.playoffMatchGames.id, id), with: { winner: true }, }); } export async function updateGame( id: string, data: Partial ): Promise { const db = database(); const [game] = await db .update(schema.playoffMatchGames) .set({ ...data, updatedAt: new Date() }) .where(eq(schema.playoffMatchGames.id, id)) .returning(); return game; } export async function deleteGame(id: string): Promise { const db = database(); await db.delete(schema.playoffMatchGames).where(eq(schema.playoffMatchGames.id, id)); } export async function deleteGamesByMatchId(playoffMatchId: string): Promise { const db = database(); await db .delete(schema.playoffMatchGames) .where(eq(schema.playoffMatchGames.playoffMatchId, playoffMatchId)); }