import type { PrismaClient } from '@generated/prisma/client'; import type { CreateAttendee, CreateAttendeeResult, ProblemDetails, Result } from '@gruperly/shared'; import { err, ok } from '@gruperly/shared'; import { alreadyWaitlistedProblem, capacityReachedProblem, conflictProblem, noGroupAccessProblem, notFoundResourceProblem, } from '@/http/problem-builders'; import prisma from '@/lib/prisma'; import { type AttendeeRecord, normalizePhone, toAttendeeDto } from '../../lib/helpers'; type CreateAttendeeDeps = { db?: Pick; }; type CreateAttendeeOptions = { allowOverflow?: boolean; }; export class CreateAttendeeUseCase { constructor(private readonly deps: CreateAttendeeDeps = {}) {} async execute( groupId: string, userId: string, payload: CreateAttendee, options: CreateAttendeeOptions = {}, ): Promise> { const db = this.deps.db ?? prisma; const group = await db.group.findUnique({ where: { id: groupId }, include: { members: { where: { userId } }, }, }); if (!group) { return err(notFoundResourceProblem('Group', groupId)); } const isOwner = group.createdById === userId; const isMember = group.members.length > 0; if (!isOwner && !isMember) { return err(noGroupAccessProblem()); } const phone = normalizePhone(payload.phone); const existing = await db.attendee.findFirst({ where: { groupId, OR: [ { phone }, { phone: payload.phone.trim() }, ], }, }); if (existing) { return err( conflictProblem({ detail: 'Ya existe un alumno registrado con este número de teléfono en este grupo.', code: 'attendee_already_registered', }), ); } const fullName = `${payload.firstName.trim()} ${payload.lastName.trim()}`.trim(); if (group.capacity !== null) { const currentCount = await db.attendee.count({ where: { groupId } }); if (currentCount >= group.capacity) { if (isOwner && !options.allowOverflow) { return err(capacityReachedProblem(group.capacity)); } if (!isOwner) { const existingWaitlistEntry = await db.groupWaitlistEntry.findFirst({ where: { groupId, phone }, }); if (existingWaitlistEntry) { return err(alreadyWaitlistedProblem()); } await db.groupWaitlistEntry.create({ data: { groupId, fullName, phone, email: payload.email?.trim() || null, notes: payload.notes?.trim() || null, }, }); return ok({ outcome: 'waitlisted', message: `${fullName} fue agregado a la lista de espera del grupo.`, }); } } } const attendee = await db.attendee.create({ data: { groupId, fullName, phone, email: payload.email?.trim() || null, notes: payload.notes?.trim() || null, }, }); return ok({ outcome: 'created', attendee: toAttendeeDto(attendee as unknown as AttendeeRecord), }); } }