import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq, and } from "drizzle-orm"; interface ExportData { version: string; exportedAt: string; sports: Array<{ name: string; type: string; slug: string; description: string | null; }>; sportsSeasons: Array<{ sportSlug: string; name: string; year: number; startDate: string | null; endDate: string | null; status: string; scoringType: string; }>; participants: Array<{ sportSlug: string; seasonName: string; seasonYear: number; name: string; shortName: string | null; externalId: string | null; }>; seasonTemplates: Array<{ name: string; year: number; description: string | null; isActive: boolean; sports: Array<{ sportSlug: string; seasonName: string; seasonYear: number; }>; }>; } export async function exportSportsDataToJSON(): Promise { const db = database(); // Export sports const sports = await db.query.sports.findMany({ orderBy: (sports, { asc }) => [asc(sports.name)], }); // Export sports seasons with sport relation const sportsSeasons = await db.query.sportsSeasons.findMany({ with: { sport: true, }, orderBy: (sportsSeasons, { desc, asc }) => [ desc(sportsSeasons.year), asc(sportsSeasons.name), ], }); // Export participants with relations const participants = await db.query.participants.findMany({ with: { sportsSeason: { with: { sport: true, }, }, }, orderBy: (participants, { asc }) => [asc(participants.name)], }); // Export season templates with sports const seasonTemplates = await db.query.seasonTemplates.findMany({ with: { seasonTemplateSports: { with: { sportsSeason: { with: { sport: true, }, }, }, }, }, orderBy: (templates, { desc }) => [desc(templates.year)], }); // Build export data structure return { version: "1.0", exportedAt: new Date().toISOString(), sports: sports.map((sport) => ({ name: sport.name, type: sport.type, slug: sport.slug, description: sport.description, })), sportsSeasons: sportsSeasons.map((season) => ({ sportSlug: season.sport.slug, name: season.name, year: season.year, startDate: season.startDate, endDate: season.endDate, status: season.status, scoringType: season.scoringType, })), participants: participants.map((participant) => ({ sportSlug: participant.sportsSeason.sport.slug, seasonName: participant.sportsSeason.name, seasonYear: participant.sportsSeason.year, name: participant.name, shortName: participant.shortName, externalId: participant.externalId, })), seasonTemplates: seasonTemplates.map((template) => ({ name: template.name, year: template.year, description: template.description, isActive: template.isActive, sports: template.seasonTemplateSports.map((ts) => ({ sportSlug: ts.sportsSeason.sport.slug, seasonName: ts.sportsSeason.name, seasonYear: ts.sportsSeason.year, })), })), }; } export async function importSportsDataFromJSON( jsonData: string, mode: "merge" | "replace" = "merge" ): Promise<{ created: number; updated: number; skipped: number }> { const db = database(); const importData: ExportData = JSON.parse(jsonData); let created = 0; let updated = 0; let skipped = 0; if (mode === "replace") { // Delete in reverse order of dependencies await db.delete(schema.seasonTemplateSports); await db.delete(schema.seasonTemplates); await db.delete(schema.participantResults); await db.delete(schema.participants); await db.delete(schema.seasonSports); await db.delete(schema.sportsSeasons); await db.delete(schema.sports); } // Track created IDs for relationships const sportIdMap = new Map(); const sportsSeasonIdMap = new Map(); // Import sports for (const sport of importData.sports) { const existing = await db.query.sports.findFirst({ where: eq(schema.sports.slug, sport.slug), }); if (existing) { if (mode === "merge") { await db .update(schema.sports) .set({ name: sport.name, type: sport.type as "team" | "individual", description: sport.description, }) .where(eq(schema.sports.slug, sport.slug)); sportIdMap.set(sport.slug, existing.id); updated++; } } else { const [createdSport] = await db .insert(schema.sports) .values({ name: sport.name, type: sport.type as "team" | "individual", slug: sport.slug, description: sport.description, }) .returning(); sportIdMap.set(sport.slug, createdSport.id); created++; } } // Import sports seasons for (const season of importData.sportsSeasons) { const sportId = sportIdMap.get(season.sportSlug); if (!sportId) { skipped++; continue; } const existing = await db.query.sportsSeasons.findFirst({ where: and( eq(schema.sportsSeasons.sportId, sportId), eq(schema.sportsSeasons.name, season.name), eq(schema.sportsSeasons.year, season.year) ), }); const seasonKey = `${season.sportSlug}:${season.name}:${season.year}`; if (existing) { if (mode === "merge") { await db .update(schema.sportsSeasons) .set({ startDate: season.startDate, endDate: season.endDate, status: season.status as "upcoming" | "active" | "completed", scoringType: season.scoringType as "playoffs" | "regular_season" | "majors", }) .where(eq(schema.sportsSeasons.id, existing.id)); sportsSeasonIdMap.set(seasonKey, existing.id); updated++; } } else { const [createdSeason] = await db .insert(schema.sportsSeasons) .values({ sportId, name: season.name, year: season.year, startDate: season.startDate, endDate: season.endDate, status: season.status as "upcoming" | "active" | "completed", scoringType: season.scoringType as "playoffs" | "regular_season" | "majors", }) .returning(); sportsSeasonIdMap.set(seasonKey, createdSeason.id); created++; } } // Import participants for (const participant of importData.participants) { const seasonKey = `${participant.sportSlug}:${participant.seasonName}:${participant.seasonYear}`; const sportsSeasonId = sportsSeasonIdMap.get(seasonKey); if (!sportsSeasonId) { skipped++; continue; } const existing = await db.query.participants.findFirst({ where: and( eq(schema.participants.sportsSeasonId, sportsSeasonId), eq(schema.participants.name, participant.name) ), }); if (!existing) { await db.insert(schema.participants).values({ sportsSeasonId, name: participant.name, shortName: participant.shortName, externalId: participant.externalId, }); created++; } else { skipped++; } } // Import season templates for (const template of importData.seasonTemplates) { const existing = await db.query.seasonTemplates.findFirst({ where: and( eq(schema.seasonTemplates.name, template.name), eq(schema.seasonTemplates.year, template.year) ), }); let templateId: string; if (existing) { if (mode === "merge") { await db .update(schema.seasonTemplates) .set({ description: template.description, isActive: template.isActive, }) .where(eq(schema.seasonTemplates.id, existing.id)); templateId = existing.id; updated++; // Remove existing template sports to re-add them await db .delete(schema.seasonTemplateSports) .where(eq(schema.seasonTemplateSports.templateId, templateId)); } else { continue; } } else { const [createdTemplate] = await db .insert(schema.seasonTemplates) .values({ name: template.name, year: template.year, description: template.description, isActive: template.isActive, }) .returning(); templateId = createdTemplate.id; created++; } // Add template sports for (const templateSport of template.sports) { const seasonKey = `${templateSport.sportSlug}:${templateSport.seasonName}:${templateSport.seasonYear}`; const sportsSeasonId = sportsSeasonIdMap.get(seasonKey); if (sportsSeasonId) { await db.insert(schema.seasonTemplateSports).values({ templateId, sportsSeasonId, }); } } } return { created, updated, skipped }; }