import { and, asc, eq, isNotNull, sql } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; export type SeasonMatch = typeof schema.seasonMatches.$inferSelect; export type MatchSubGame = typeof schema.matchSubGames.$inferSelect; export type MatchStatus = (typeof schema.matchStatusEnum.enumValues)[number]; export interface UpsertSeasonMatchData { sportsSeasonId: string; scoringEventId?: string | null; participant1Id?: string | null; participant2Id?: string | null; winnerId?: string | null; participant1Score?: number | null; participant2Score?: number | null; matchStage?: number | null; matchRound?: number | null; matchday?: number | null; isSeries?: boolean; status: MatchStatus; scheduledAt?: Date | null; startedAt?: Date | null; completedAt?: Date | null; externalMatchId: string; } export interface UpsertMatchSubGameData { seasonMatchId: string; gameNumber: number; gameLabel?: string | null; participant1Score?: number | null; participant2Score?: number | null; winnerId?: string | null; status: MatchStatus; startedAt?: Date | null; completedAt?: Date | null; externalGameId?: string | null; } export async function upsertSeasonMatch( data: UpsertSeasonMatchData ): Promise { const db = database(); const now = new Date(); const [result] = await db .insert(schema.seasonMatches) .values({ ...data, isSeries: data.isSeries ?? false, updatedAt: now, }) .onConflictDoUpdate({ target: schema.seasonMatches.externalMatchId, targetWhere: isNotNull(schema.seasonMatches.externalMatchId), set: { participant1Id: sql`excluded.participant1_id`, participant2Id: sql`excluded.participant2_id`, winnerId: sql`excluded.winner_id`, participant1Score: sql`excluded.participant1_score`, participant2Score: sql`excluded.participant2_score`, matchStage: sql`excluded.match_stage`, matchRound: sql`excluded.match_round`, matchday: sql`excluded.matchday`, isSeries: sql`excluded.is_series`, status: sql`excluded.status`, scheduledAt: sql`excluded.scheduled_at`, startedAt: sql`excluded.started_at`, completedAt: sql`excluded.completed_at`, updatedAt: now, }, }) .returning(); return result; } export async function upsertSeasonMatchBulk( records: UpsertSeasonMatchData[] ): Promise { if (records.length === 0) return []; const db = database(); const now = new Date(); return await db .insert(schema.seasonMatches) .values(records.map((r) => ({ ...r, isSeries: r.isSeries ?? false, updatedAt: now }))) .onConflictDoUpdate({ target: schema.seasonMatches.externalMatchId, targetWhere: isNotNull(schema.seasonMatches.externalMatchId), set: { participant1Id: sql`excluded.participant1_id`, participant2Id: sql`excluded.participant2_id`, winnerId: sql`excluded.winner_id`, participant1Score: sql`excluded.participant1_score`, participant2Score: sql`excluded.participant2_score`, matchStage: sql`excluded.match_stage`, matchRound: sql`excluded.match_round`, matchday: sql`excluded.matchday`, isSeries: sql`excluded.is_series`, status: sql`excluded.status`, scheduledAt: sql`excluded.scheduled_at`, startedAt: sql`excluded.started_at`, completedAt: sql`excluded.completed_at`, updatedAt: now, }, }) .returning(); } export async function findSeasonMatchesBySportsSeasonId( sportsSeasonId: string, filters?: { status?: MatchStatus; matchStage?: number } ) { const db = database(); return await db.query.seasonMatches.findMany({ where: and( eq(schema.seasonMatches.sportsSeasonId, sportsSeasonId), filters?.status ? eq(schema.seasonMatches.status, filters.status) : undefined, filters?.matchStage !== undefined ? eq(schema.seasonMatches.matchStage, filters.matchStage) : undefined ), orderBy: [ asc(schema.seasonMatches.matchStage), asc(schema.seasonMatches.matchRound), asc(schema.seasonMatches.matchday), asc(schema.seasonMatches.scheduledAt), asc(schema.seasonMatches.createdAt), ], with: { participant1: true, participant2: true, winner: true, subGames: { orderBy: [asc(schema.matchSubGames.gameNumber)] }, }, }); } export async function findSeasonMatchesByScoringEventId(scoringEventId: string) { const db = database(); return await db.query.seasonMatches.findMany({ where: eq(schema.seasonMatches.scoringEventId, scoringEventId), orderBy: [ asc(schema.seasonMatches.matchStage), asc(schema.seasonMatches.matchRound), asc(schema.seasonMatches.scheduledAt), asc(schema.seasonMatches.createdAt), ], with: { participant1: true, participant2: true, winner: true, subGames: { orderBy: [asc(schema.matchSubGames.gameNumber)] }, }, }); } export async function findSeasonMatchById(matchId: string) { const db = database(); return await db.query.seasonMatches.findFirst({ where: eq(schema.seasonMatches.id, matchId), with: { participant1: true, participant2: true, winner: true, subGames: { orderBy: [asc(schema.matchSubGames.gameNumber)] }, }, }); } export async function updateSeasonMatch( matchId: string, data: Partial<{ participant1Id: string | null; participant2Id: string | null; winnerId: string | null; participant1Score: number | null; participant2Score: number | null; status: MatchStatus; scheduledAt: Date | null; startedAt: Date | null; completedAt: Date | null; }> ): Promise { const db = database(); const [result] = await db .update(schema.seasonMatches) .set({ ...data, updatedAt: new Date() }) .where(eq(schema.seasonMatches.id, matchId)) .returning(); return result; } export async function deleteSeasonMatch(matchId: string): Promise { const db = database(); await db .delete(schema.seasonMatches) .where(eq(schema.seasonMatches.id, matchId)); } export async function upsertMatchSubGame( data: UpsertMatchSubGameData ): Promise { const db = database(); const now = new Date(); const [result] = await db .insert(schema.matchSubGames) .values({ ...data, updatedAt: now }) .onConflictDoUpdate({ target: [schema.matchSubGames.seasonMatchId, schema.matchSubGames.gameNumber], set: { gameLabel: sql`excluded.game_label`, participant1Score: sql`excluded.participant1_score`, participant2Score: sql`excluded.participant2_score`, winnerId: sql`excluded.winner_id`, status: sql`excluded.status`, startedAt: sql`excluded.started_at`, completedAt: sql`excluded.completed_at`, externalGameId: sql`excluded.external_game_id`, updatedAt: now, }, }) .returning(); return result; } export async function findMatchSubGamesByMatchId(matchId: string): Promise { const db = database(); return await db.query.matchSubGames.findMany({ where: eq(schema.matchSubGames.seasonMatchId, matchId), orderBy: [asc(schema.matchSubGames.gameNumber)], }); }