import { and, eq, inArray, isNull } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { batchUpsertParticipantEVs, syncVorpForSeason } from "~/models/participant-expected-value"; import { recalculateStandings } from "~/models/scoring-calculator"; import { createDailySnapshot } from "~/models/standings"; import { BracktSimulator } from "~/services/simulations/brackt-simulator"; export const BRACKT_SLUG = "brackt"; type Db = ReturnType; function isBracktSport(sport: { slug: string } | null | undefined) { return sport?.slug === BRACKT_SLUG; } export async function seedOrRepairBracktTemplate(providedDb?: Db) { const db = providedDb || database(); return await db.transaction(async (tx) => { const existingSport = await tx.query.sports.findFirst({ where: eq(schema.sports.slug, BRACKT_SLUG), }); const [sport] = existingSport ? await tx .update(schema.sports) .set({ name: "Brackt", type: "team", simulatorType: "brackt", updatedAt: new Date() }) .where(eq(schema.sports.id, existingSport.id)) .returning() : await tx .insert(schema.sports) .values({ name: "Brackt", type: "team", slug: BRACKT_SLUG, description: "League meta-sport based on final Brackt standings", simulatorType: "brackt", }) .returning(); const existingTemplate = await tx.query.sportsSeasons.findFirst({ where: and(eq(schema.sportsSeasons.sportId, sport.id), isNull(schema.sportsSeasons.fantasySeasonId)), orderBy: (sportsSeasons, { desc }) => [desc(sportsSeasons.year)], }); const templateValues = { sportId: sport.id, name: "Brackt", year: 2026, startDate: "2026-01-01", endDate: "2099-12-31", status: "upcoming" as const, scoringType: "regular_season" as const, scoringPattern: "season_standings" as const, draftOn: "2026-01-01", draftOff: "2099-12-31", fantasySeasonId: null, updatedAt: new Date(), }; const [template] = existingTemplate ? await tx .update(schema.sportsSeasons) .set(templateValues) .where(eq(schema.sportsSeasons.id, existingTemplate.id)) .returning() : await tx.insert(schema.sportsSeasons).values(templateValues).returning(); return { sport, template }; }); } export async function findPrivateBracktSeason(fantasySeasonId: string, providedDb?: Db) { const db = providedDb || database(); return db.query.sportsSeasons.findFirst({ where: eq(schema.sportsSeasons.fantasySeasonId, fantasySeasonId), with: { sport: true }, }).then((season) => (isBracktSport(season?.sport) ? season : undefined)); } async function ensurePrivateBracktSeason(fantasySeasonId: string, templateId: string, db: Db) { const existing = await findPrivateBracktSeason(fantasySeasonId, db); if (existing) return existing; const template = await db.query.sportsSeasons.findFirst({ where: eq(schema.sportsSeasons.id, templateId), with: { sport: true }, }); if (!template || !isBracktSport(template.sport)) { throw new Error("Brackt template sports season not found"); } const fantasySeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, fantasySeasonId), with: { league: true }, }); const [privateSeason] = await db .insert(schema.sportsSeasons) .values({ sportId: template.sportId, fantasySeasonId, name: `${fantasySeason?.league.name ?? "League"} Brackt`, year: fantasySeason?.year ?? template.year, startDate: template.startDate, endDate: template.endDate, status: "upcoming", scoringType: "regular_season", scoringPattern: "season_standings", draftOn: template.draftOn, draftOff: template.draftOff, }) .returning(); return { ...privateSeason, sport: template.sport }; } export async function syncPrivateBracktParticipants(fantasySeasonId: string, providedDb?: Db) { const db = providedDb || database(); const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); if (!bracktSeason) return null; const teams = await db.query.teams.findMany({ where: eq(schema.teams.seasonId, fantasySeasonId), orderBy: (teamsTable, { asc }) => [asc(teamsTable.name)], }); const participants = await db.query.seasonParticipants.findMany({ where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id), }); const participantByTeamId = new Map(participants.map((p) => [p.externalId, p])); const teamIds = new Set(teams.map((team) => team.id)); for (const team of teams) { const participant = participantByTeamId.get(team.id); if (participant) { if (participant.name !== team.name || participant.shortName !== team.name) { await db .update(schema.seasonParticipants) .set({ name: team.name, shortName: team.name, updatedAt: new Date() }) .where(eq(schema.seasonParticipants.id, participant.id)); } } else { await db.insert(schema.seasonParticipants).values({ sportsSeasonId: bracktSeason.id, name: team.name, shortName: team.name, externalId: team.id, }); } } const obsolete = participants.filter((p) => p.externalId && !teamIds.has(p.externalId)); if (obsolete.length > 0) { await db .delete(schema.seasonParticipants) .where(inArray(schema.seasonParticipants.id, obsolete.map((p) => p.id))); } await runBracktHarvilleForFantasySeason(fantasySeasonId, db); return bracktSeason; } export async function applyBracktSportsForSeason( fantasySeasonId: string, selectedSportsSeasonIds: string[], providedDb?: Db ): Promise { const db = providedDb || database(); const selected = [...new Set(selectedSportsSeasonIds)]; if (selected.length === 0) return selected; const sportsSeasons = await db.query.sportsSeasons.findMany({ where: inArray(schema.sportsSeasons.id, selected), with: { sport: true }, }); const bracktTemplate = sportsSeasons.find((ss) => isBracktSport(ss.sport) && ss.fantasySeasonId === null); const existingPrivate = await findPrivateBracktSeason(fantasySeasonId, db); const nonBracktIds = selected.filter((id) => { const ss = sportsSeasons.find((s) => s.id === id); return !ss || !isBracktSport(ss.sport); }); if (!bracktTemplate && existingPrivate && !selected.includes(existingPrivate.id)) { await db.delete(schema.sportsSeasons).where(eq(schema.sportsSeasons.id, existingPrivate.id)); return nonBracktIds; } if (!bracktTemplate) return selected; const privateSeason = await ensurePrivateBracktSeason(fantasySeasonId, bracktTemplate.id, db); await syncPrivateBracktParticipants(fantasySeasonId, db); return [...nonBracktIds, privateSeason.id]; } export async function runBracktHarvilleForFantasySeason(fantasySeasonId: string, db: Db) { const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); if (!bracktSeason) return []; const fantasySeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, fantasySeasonId), }); if (!fantasySeason) return []; const simulator = new BracktSimulator(db); const results = await simulator.simulate(bracktSeason.id); await batchUpsertParticipantEVs( results.map((result) => ({ participantId: result.participantId, sportsSeasonId: bracktSeason.id, probabilities: result.probabilities, scoringRules: { pointsFor1st: fantasySeason.pointsFor1st, pointsFor2nd: fantasySeason.pointsFor2nd, pointsFor3rd: fantasySeason.pointsFor3rd, pointsFor4th: fantasySeason.pointsFor4th, pointsFor5th: fantasySeason.pointsFor5th, pointsFor6th: fantasySeason.pointsFor6th, pointsFor7th: fantasySeason.pointsFor7th, pointsFor8th: fantasySeason.pointsFor8th, }, source: "performance_model", })), db ); await syncVorpForSeason(bracktSeason.id, db); const participants = await db.query.seasonParticipants.findMany({ where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id), }); return participants.map((p) => ({ participantId: p.id, expectedValue: Number(p.expectedValue ?? 0), vorpValue: Number(p.vorpValue ?? 0), })); } async function resolveBracktForFantasySeason( fantasySeasonId: string, db: Db, trigger: "auto" | "manual", completedSportsSeasonId?: string ) { const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); if (!bracktSeason) return { status: "skipped" as const, reason: "no_brackt" }; const picks = await db.query.draftPicks.findMany({ where: eq(schema.draftPicks.seasonId, fantasySeasonId), with: { participant: { with: { sportsSeason: { with: { sport: true } }, results: true } } }, }); const nonBracktPicks = picks.filter((pick) => !isBracktSport(pick.participant.sportsSeason.sport)); const ready = nonBracktPicks.every((pick) => { const hasFinalizedResult = pick.participant.results.some( (r) => r.finalPosition !== null && !r.isPartialScore ); return hasFinalizedResult || pick.participant.sportsSeason.status === "completed"; }); if (!ready) return { status: "skipped" as const, reason: "incomplete" }; const [standings, bracktParticipants, fantasySeason] = await Promise.all([ db.query.teamStandings.findMany({ where: eq(schema.teamStandings.seasonId, fantasySeasonId) }), db.query.seasonParticipants.findMany({ where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id) }), db.query.seasons.findFirst({ where: eq(schema.seasons.id, fantasySeasonId), with: { league: true } }), ]); if (standings.length === 0 || !fantasySeason) { return { status: "skipped" as const, reason: "missing_standings" }; } const participantByTeamId = new Map(bracktParticipants.map((p) => [p.externalId, p])); const desired = standings.map((standing) => { const participant = participantByTeamId.get(standing.teamId); if (!participant) throw new Error(`Missing Brackt participant for team ${standing.teamId}`); return { participantId: participant.id, finalPosition: standing.currentRank }; }); const existingResults = await db.query.seasonParticipantResults.findMany({ where: eq(schema.seasonParticipantResults.sportsSeasonId, bracktSeason.id), }); if (existingResults.length > 0) { const existingByParticipantId = new Map(existingResults.map((r) => [r.participantId, r])); const differs = desired.some((placement) => { const existing = existingByParticipantId.get(placement.participantId); return existing && existing.finalPosition !== placement.finalPosition; }); if (differs) { return { status: "blocked" as const, reason: "existing_results_differ" }; } } const now = new Date(); const existingByParticipantId = new Map(existingResults.map((r) => [r.participantId, r])); for (const placement of desired) { const existing = existingByParticipantId.get(placement.participantId); if (existing) { await db .update(schema.seasonParticipantResults) .set({ finalPosition: placement.finalPosition, isPartialScore: false, updatedAt: now, }) .where(eq(schema.seasonParticipantResults.id, existing.id)); } else { await db.insert(schema.seasonParticipantResults).values({ participantId: placement.participantId, sportsSeasonId: bracktSeason.id, finalPosition: placement.finalPosition, isPartialScore: false, }); } } await db .update(schema.sportsSeasons) .set({ status: "completed", updatedAt: now }) .where(eq(schema.sportsSeasons.id, bracktSeason.id)); await recalculateStandings(fantasySeasonId, db); await createDailySnapshot(fantasySeasonId, db); await db.insert(schema.commissionerAuditLog).values({ seasonId: fantasySeasonId, leagueId: fantasySeason.leagueId, actorUserId: trigger === "auto" ? "system" : "commissioner", actorDisplayName: trigger === "auto" ? "System" : "Commissioner", action: "brackt_resolved", affectedTeamIds: standings.map((s) => s.teamId), details: { trigger, completedSportsSeasonId }, }); return { status: "resolved" as const }; } export async function maybeResolveCompletedBracktForSportsSeason(completedSportsSeasonId: string, providedDb?: Db) { const db = providedDb || database(); const completedSeason = await db.query.sportsSeasons.findFirst({ where: eq(schema.sportsSeasons.id, completedSportsSeasonId), with: { sport: true }, }); if (!completedSeason || isBracktSport(completedSeason.sport)) return []; const links = await db.query.seasonSports.findMany({ where: eq(schema.seasonSports.sportsSeasonId, completedSportsSeasonId), }); const results = []; for (const link of links) { results.push(await resolveBracktForFantasySeason(link.seasonId, db, "auto", completedSportsSeasonId)); } return results; }