import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; export type Season = typeof schema.seasons.$inferSelect; export type NewSeason = Omit & { inviteCode?: string; }; export type SeasonStatus = "pre_draft" | "draft" | "active" | "completed"; export interface SeasonWithSportsSeasons extends Season { seasonSports: Array<{ id: string; sportsSeason: { id: string; name: string; year: number; status: string; scoringType: string; scoringPattern: string | null; sport: { id: string; name: string; type: string; slug: string; }; }; }>; } export async function createSeason(data: NewSeason): Promise { const db = database(); // Generate invite code if not provided const seasonData = { ...data, inviteCode: data.inviteCode || generateInviteCode(), }; const [season] = await db .insert(schema.seasons) .values(seasonData) .returning(); return season; } export async function findSeasonById(id: string): Promise { const db = database(); return await db.query.seasons.findFirst({ where: eq(schema.seasons.id, id), }); } export async function findSeasonsByLeagueId(leagueId: string): Promise { const db = database(); return await db.query.seasons.findMany({ where: eq(schema.seasons.leagueId, leagueId), orderBy: (seasons, { desc }) => [desc(seasons.year)], }); } export async function findCurrentSeason( leagueId: string ): Promise { const db = database(); return await db.query.seasons.findFirst({ where: eq(schema.seasons.leagueId, leagueId), orderBy: (seasons, { desc }) => [desc(seasons.year)], }); } export async function findCurrentSeasonWithSports( leagueId: string ): Promise { const db = database(); return await db.query.seasons.findFirst({ where: eq(schema.seasons.leagueId, leagueId), orderBy: (seasons, { desc }) => [desc(seasons.year)], with: { seasonSports: { with: { sportsSeason: { with: { sport: true, }, }, }, }, }, }) as SeasonWithSportsSeasons | undefined; } export async function findSeasonByLeagueAndYear( leagueId: string, year: number ): Promise { const db = database(); return await db.query.seasons.findFirst({ where: and( eq(schema.seasons.leagueId, leagueId), eq(schema.seasons.year, year) ), }); } export async function updateSeason( id: string, data: Partial ): Promise { const db = database(); const [season] = await db .update(schema.seasons) .set({ ...data, updatedAt: new Date() }) .where(eq(schema.seasons.id, id)) .returning(); return season; } export async function updateSeasonStatus( id: string, status: SeasonStatus ): Promise { return await updateSeason(id, { status }); } export async function deleteSeason(id: string): Promise { const db = database(); await db.delete(schema.seasons).where(eq(schema.seasons.id, id)); } export async function findSeasonWithSportsSeasons( id: string ): Promise { const db = database(); return await db.query.seasons.findFirst({ where: eq(schema.seasons.id, id), with: { seasonSports: { with: { sportsSeason: { with: { sport: true, }, }, }, }, }, }) as SeasonWithSportsSeasons | undefined; } /** * Generate a short, URL-safe invite code * Uses base62 encoding (alphanumeric) for readability */ export function generateInviteCode(): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const length = 10; let result = ''; const randomValues = new Uint8Array(length); crypto.getRandomValues(randomValues); for (let i = 0; i < length; i++) { result += chars[randomValues[i] % chars.length]; } return result; } /** * Find a season by its invite code */ export async function findSeasonByInviteCode( inviteCode: string ): Promise { const db = database(); return await db.query.seasons.findFirst({ where: eq(schema.seasons.inviteCode, inviteCode), }); } /** * Regenerate invite code for a season */ export async function regenerateInviteCode(seasonId: string): Promise { const newInviteCode = generateInviteCode(); return await updateSeason(seasonId, { inviteCode: newInviteCode }); }