import { eq, and, asc, desc } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; export type Tournament = typeof schema.tournaments.$inferSelect; export type NewTournament = typeof schema.tournaments.$inferInsert; export type TournamentStatus = Tournament["status"]; export async function createTournament( data: NewTournament ): Promise { const db = database(); const [tournament] = await db .insert(schema.tournaments) .values(data) .returning(); return tournament; } export async function getTournamentById( id: string ): Promise { const db = database(); const results = await db .select() .from(schema.tournaments) .where(eq(schema.tournaments.id, id)) .limit(1); return results[0] ?? null; } export async function findTournamentsBySport( sportId: string ): Promise { const db = database(); return await db .select() .from(schema.tournaments) .where(eq(schema.tournaments.sportId, sportId)) .orderBy(asc(schema.tournaments.year), asc(schema.tournaments.startsAt)); } export async function findTournamentBySportNameYear( sportId: string, name: string, year: number ): Promise { const db = database(); const results = await db .select() .from(schema.tournaments) .where( and( eq(schema.tournaments.sportId, sportId), eq(schema.tournaments.name, name), eq(schema.tournaments.year, year) ) ) .limit(1); return results[0] ?? null; } export async function upsertTournament( data: NewTournament ): Promise { if (!data.sportId || !data.name || !data.year) { throw new Error("sportId, name, and year are required for upsert"); } const existing = await findTournamentBySportNameYear( data.sportId, data.name, data.year ); if (existing) { return existing; } return await createTournament(data); } export async function findAllTournamentsGroupedBySport(): Promise< Array<{ sport: { id: string; name: string }; tournaments: Tournament[]; }> > { const db = database(); const rows = await db .select({ tournament: schema.tournaments, sport: { id: schema.sports.id, name: schema.sports.name, }, }) .from(schema.tournaments) .innerJoin(schema.sports, eq(schema.tournaments.sportId, schema.sports.id)) .orderBy( asc(schema.sports.name), desc(schema.tournaments.year), asc(schema.tournaments.startsAt) ); const groupMap = new Map< string, { sport: { id: string; name: string }; tournaments: Tournament[] } >(); for (const row of rows) { const existing = groupMap.get(row.sport.id); if (existing) { existing.tournaments.push(row.tournament); } else { groupMap.set(row.sport.id, { sport: row.sport, tournaments: [row.tournament], }); } } return Array.from(groupMap.values()); } export async function updateTournamentStatus( id: string, status: TournamentStatus ): Promise { const db = database(); const [tournament] = await db .update(schema.tournaments) .set({ status, updatedAt: new Date() }) .where(eq(schema.tournaments.id, id)) .returning(); return tournament; } /** * Delete a canonical tournament. The DB cascades remove its tournament_results; * any surviving scoring_events have their tournamentId set to NULL (onDelete: * "set null"). Callers should only use this once the last linked window has been * removed, so no scoring events are left pointing at it. */ export async function deleteTournament( id: string, providedDb?: ReturnType ): Promise { const db = providedDb || database(); await db.delete(schema.tournaments).where(eq(schema.tournaments.id, id)); }