import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { canManageTournament } from "@/lib/permissions"; import { generateRoundRobin, validateScheduleInput } from "@/lib/schedule-generator"; interface RouteParams { params: Promise<{ id: string; }>; } /** * GET /api/tournaments/[id]/schedule * * Fetch the tournament schedule (rounds with matchups). */ export async function GET(_request: Request, { params }: RouteParams) { try { const { id } = await params; const tournamentId = parseInt(id); if (isNaN(tournamentId)) { return NextResponse.json( { error: "Invalid tournament ID" }, { status: 400 } ); } const permission = await canManageTournament(tournamentId); if (!permission.allowed) { return NextResponse.json( { error: permission.reason || "Not authorized to view this tournament" }, { status: 403 } ); } const tournament = await prisma.event.findUnique({ where: { id: tournamentId }, include: { rounds: { orderBy: { roundNumber: "asc" }, include: { bracketMatchups: { include: { team1: { include: { player1: true, player2: true }, }, team2: { include: { player1: true, player2: true }, }, match: true, }, }, }, }, }, }); if (!tournament) { return NextResponse.json( { error: "Tournament not found" }, { status: 404 } ); } return NextResponse.json({ rounds: tournament.rounds }); } catch (error: unknown) { console.error("Error fetching schedule:", error); const message = error instanceof Error ? error.message : "Failed to fetch schedule"; return NextResponse.json({ error: message }, { status: 500 }); } } /** * POST /api/tournaments/[id]/schedule * * Generate a round-robin schedule for the tournament. * Creates TournamentRound and BracketMatchup records. */ export async function POST(_request: Request, { params }: RouteParams) { try { const { id } = await params; const tournamentId = parseInt(id); if (isNaN(tournamentId)) { return NextResponse.json( { error: "Invalid tournament ID" }, { status: 400 } ); } const permission = await canManageTournament(tournamentId); if (!permission.allowed) { return NextResponse.json( { error: permission.reason || "Not authorized to manage this tournament" }, { status: 403 } ); } // Check tournament exists const tournament = await prisma.event.findUnique({ where: { id: tournamentId }, include: { teams: true, rounds: true, }, }); if (!tournament) { return NextResponse.json( { error: "Tournament not found" }, { status: 404 } ); } // Check if schedule already exists if (tournament.rounds.length > 0) { return NextResponse.json( { error: "Schedule already exists. Delete existing rounds before regenerating.", existingRounds: tournament.rounds.length, }, { status: 409 } ); } // Get team IDs const teamIds = tournament.teams.map((t) => t.id); const validation = validateScheduleInput(teamIds); if (!validation.valid) { return NextResponse.json( { error: validation.error }, { status: 400 } ); } // Generate schedule const schedule = generateRoundRobin(teamIds); // Create rounds and matchups in a transaction const created = await prisma.$transaction( schedule.map((round) => prisma.tournamentRound.create({ data: { eventId: tournamentId, roundNumber: round.roundNumber, status: "pending", bracketMatchups: { create: round.matchups.map((matchup, idx) => ({ eventId: tournamentId, team1Id: matchup.team1Id, team2Id: matchup.team2Id, bracketPosition: idx + 1, status: "pending", })), }, }, include: { bracketMatchups: { include: { team1: { include: { player1: true, player2: true }, }, team2: { include: { player1: true, player2: true }, }, }, }, }, }) ) ); return NextResponse.json({ success: true, roundsCreated: created.length, matchupsCreated: created.reduce( (sum, r) => sum + r.bracketMatchups.length, 0 ), rounds: created, }); } catch (error: unknown) { console.error("Error generating schedule:", error); const message = error instanceof Error ? error.message : "Failed to generate schedule"; return NextResponse.json({ error: message }, { status: 500 }); } } /** * DELETE /api/tournaments/[id]/schedule * * Delete all rounds and matchups for a tournament. */ export async function DELETE(_request: Request, { params }: RouteParams) { try { const { id } = await params; const tournamentId = parseInt(id); if (isNaN(tournamentId)) { return NextResponse.json( { error: "Invalid tournament ID" }, { status: 400 } ); } const permission = await canManageTournament(tournamentId); if (!permission.allowed) { return NextResponse.json( { error: permission.reason || "Not authorized to manage this tournament" }, { status: 403 } ); } // Delete bracket matchups first (FK constraint) const deletedMatchups = await prisma.bracketMatchup.deleteMany({ where: { eventId: tournamentId }, }); // Delete rounds const deletedRounds = await prisma.tournamentRound.deleteMany({ where: { eventId: tournamentId }, }); return NextResponse.json({ success: true, deletedRounds: deletedRounds.count, deletedMatchups: deletedMatchups.count, }); } catch (error: unknown) { console.error("Error deleting schedule:", error); const message = error instanceof Error ? error.message : "Failed to delete schedule"; return NextResponse.json({ error: message }, { status: 500 }); } }