import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { db } from '@/lib/db'; import { z } from 'zod'; import { Decimal } from '@prisma/client/runtime/library'; import { Prisma } from '@prisma/client'; interface RouteContext { params: Promise<{ id: string }>; } // Validation schema for updating a tournament const updateTournamentSchema = z.object({ name: z.string().min(1).max(100).optional(), description: z.string().max(500).optional().nullable(), date: z.string().datetime().optional(), endDate: z.string().datetime().optional().nullable(), type: z.enum(['SINGLE_ELIMINATION', 'DOUBLE_ELIMINATION', 'ROUND_ROBIN', 'LEAGUE']).optional(), category: z.string().max(50).optional().nullable(), maxTeams: z.number().int().min(2).optional(), price: z.number().nonnegative().optional(), status: z.enum(['DRAFT', 'REGISTRATION_OPEN', 'REGISTRATION_CLOSED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED']).optional(), rules: z.string().max(2000).optional().nullable(), isPublic: z.boolean().optional(), }); // Map API types to database types function mapTournamentType(type: string): 'AMERICANO' | 'MEXICANO' | 'BRACKET' | 'ROUND_ROBIN' | 'LEAGUE' { const mapping: Record = { 'SINGLE_ELIMINATION': 'BRACKET', 'DOUBLE_ELIMINATION': 'BRACKET', 'ROUND_ROBIN': 'ROUND_ROBIN', 'LEAGUE': 'LEAGUE', }; return mapping[type] || 'BRACKET'; } // GET /api/tournaments/[id] - Get tournament with inscriptions count and matches export async function GET( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const { id } = await context.params; const tournament = await db.tournament.findFirst({ where: { id, organizationId: session.user.organizationId, }, include: { site: { select: { id: true, name: true, slug: true, address: true, phone: true, email: true, }, }, inscriptions: { include: { client: { select: { id: true, firstName: true, lastName: true, email: true, phone: true, level: true, }, }, }, orderBy: { registeredAt: 'asc', }, }, matches: { include: { court: { select: { id: true, name: true, }, }, }, orderBy: [ { round: 'asc' }, { position: 'asc' }, ], }, _count: { select: { inscriptions: true, matches: true, }, }, }, }); if (!tournament) { return NextResponse.json( { error: 'Tournament not found' }, { status: 404 } ); } return NextResponse.json(tournament); } catch (error) { console.error('Error fetching tournament:', error); return NextResponse.json( { error: 'Failed to fetch tournament' }, { status: 500 } ); } } // PUT /api/tournaments/[id] - Update tournament details export async function PUT( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // Check if user has admin role const allowedRoles = ['SUPER_ADMIN', 'ORG_ADMIN', 'SITE_ADMIN']; if (!allowedRoles.includes(session.user.role)) { return NextResponse.json( { error: 'Forbidden: Insufficient permissions' }, { status: 403 } ); } const { id } = await context.params; // Verify tournament exists and belongs to user's organization const existingTournament = await db.tournament.findFirst({ where: { id, organizationId: session.user.organizationId, }, }); if (!existingTournament) { return NextResponse.json( { error: 'Tournament not found' }, { status: 404 } ); } // If user is SITE_ADMIN, verify they have access to this site if (session.user.role === 'SITE_ADMIN' && existingTournament.siteId && session.user.siteId !== existingTournament.siteId) { return NextResponse.json( { error: 'Forbidden: You do not have access to this tournament' }, { status: 403 } ); } const body = await request.json(); // Validate input const validationResult = updateTournamentSchema.safeParse(body); if (!validationResult.success) { return NextResponse.json( { error: 'Invalid tournament data', details: validationResult.error.flatten().fieldErrors, }, { status: 400 } ); } const { name, description, date, endDate, type, category, maxTeams, price, status, rules, isPublic } = validationResult.data; // Build update data const updateData: Prisma.TournamentUpdateInput = {}; if (name !== undefined) updateData.name = name; if (description !== undefined) updateData.description = description; if (date !== undefined) updateData.startDate = new Date(date); if (endDate !== undefined) updateData.endDate = endDate ? new Date(endDate) : null; // Build settings update let newSettings = existingTournament.settings as Prisma.JsonObject | null; if (type !== undefined) { updateData.type = mapTournamentType(type); // Also update settings with the format newSettings = { ...(newSettings || {}), tournamentFormat: type, }; } if (category !== undefined) { newSettings = { ...(newSettings || {}), category, }; } if (newSettings && (type !== undefined || category !== undefined)) { updateData.settings = newSettings; } if (maxTeams !== undefined) updateData.maxPlayers = maxTeams; if (price !== undefined) updateData.entryFee = new Decimal(price); if (status !== undefined) updateData.status = status; if (rules !== undefined) updateData.rules = rules; if (isPublic !== undefined) updateData.isPublic = isPublic; const tournament = await db.tournament.update({ where: { id }, data: updateData, include: { site: { select: { id: true, name: true, slug: true, }, }, _count: { select: { inscriptions: true, matches: true, }, }, }, }); return NextResponse.json(tournament); } catch (error) { console.error('Error updating tournament:', error); return NextResponse.json( { error: 'Failed to update tournament' }, { status: 500 } ); } } // DELETE /api/tournaments/[id] - Delete tournament (only if DRAFT status) export async function DELETE( request: NextRequest, context: RouteContext ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // Check if user has admin role const allowedRoles = ['SUPER_ADMIN', 'ORG_ADMIN', 'SITE_ADMIN']; if (!allowedRoles.includes(session.user.role)) { return NextResponse.json( { error: 'Forbidden: Insufficient permissions' }, { status: 403 } ); } const { id } = await context.params; // Verify tournament exists and belongs to user's organization const existingTournament = await db.tournament.findFirst({ where: { id, organizationId: session.user.organizationId, }, include: { _count: { select: { inscriptions: true, matches: true, }, }, }, }); if (!existingTournament) { return NextResponse.json( { error: 'Tournament not found' }, { status: 404 } ); } // If user is SITE_ADMIN, verify they have access to this site if (session.user.role === 'SITE_ADMIN' && existingTournament.siteId && session.user.siteId !== existingTournament.siteId) { return NextResponse.json( { error: 'Forbidden: You do not have access to this tournament' }, { status: 403 } ); } // Only allow deletion of DRAFT tournaments if (existingTournament.status !== 'DRAFT') { return NextResponse.json( { error: 'Only tournaments in DRAFT status can be deleted. Consider cancelling instead.' }, { status: 400 } ); } // Delete the tournament (cascades to inscriptions and matches) await db.tournament.delete({ where: { id }, }); return NextResponse.json({ message: 'Tournament deleted successfully', }); } catch (error) { console.error('Error deleting tournament:', error); return NextResponse.json( { error: 'Failed to delete tournament' }, { status: 500 } ); } }