import { db } from '@/lib/prisma'; import { buildUniqueSlug, slugify } from '@/lib/slug'; import type { CreateComplexInput } from '@repo/api-contract'; import { v7 as uuidv7 } from 'uuid'; type DayOfWeek = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY'; type CreateComplexInternalInput = CreateComplexInput & { adminEmail: string; userId?: string; }; function toMinutes(value: string): number { const [hours, minutes] = value.split(':').map((part) => Number(part)); return hours * 60 + minutes; } function assertAvailabilityRanges( availability: Array<{ dayOfWeek: DayOfWeek; startTime: string; endTime: string; }> ) { for (const range of availability) { const start = toMinutes(range.startTime); const end = toMinutes(range.endTime); if (start >= end) { throw new Error(`El rango ${range.startTime}-${range.endTime} es inválido.`); } } } export async function createComplex(input: CreateComplexInternalInput) { return db.$transaction(async (tx) => { const base = slugify(input.complexName); const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`; const complexSlug = await buildUniqueSlug(fallback, (slug) => tx.complex.findFirst({ where: { complexSlug: slug }, select: { id: true } }) ); const complex = await tx.complex.create({ data: { id: uuidv7(), complexName: input.complexName, physicalAddress: input.physicalAddress.trim(), city: input.city?.trim() || null, state: input.state?.trim() || null, country: input.country?.trim() || null, complexSlug, adminEmail: input.adminEmail, planCode: input.planCode, }, }); if (input.userId) { await tx.complexUser.create({ data: { complexId: complex.id, userId: input.userId, role: 'ADMIN', }, }); } if (input.setupCourts && input.courtSportId) { const sport = await tx.sport.findFirst({ where: { id: input.courtSportId, isActive: true }, select: { name: true }, }); if (!sport) { throw new Error('El deporte seleccionado no existe o esta inactivo.'); } const availability = (input.courtDaysOfWeek ?? []).map((day) => ({ dayOfWeek: day as DayOfWeek, startTime: input.courtStartTime ?? '08:00', endTime: input.courtEndTime ?? '22:00', })); assertAvailabilityRanges(availability); const court = await tx.court.create({ data: { id: uuidv7(), complexId: complex.id, sportId: input.courtSportId, name: `${sport.name} 1`, slotDurationMinutes: 60, basePrice: 0, }, }); await tx.courtAvailability.createMany({ data: availability.map((avail) => ({ id: uuidv7(), courtId: court.id, dayOfWeek: avail.dayOfWeek, startTime: avail.startTime, endTime: avail.endTime, })), }); } return complex; }); }