import type { PrismaClient } from '@generated/prisma/client'; import type { AttendeeDto, ProblemDetails, Result } from '@gruperly/shared'; import { err, ok } from '@gruperly/shared'; import { capacityReachedProblem, conflictProblem } from '@/http/problem-builders'; import { type AttendeeRecord, toAttendeeDto } from '@/modules/attendees/lib/helpers'; import { type GroupWaitlistEntryRecord } from './helpers'; export type PromoteDb = Pick; type PromoteGroup = { id: string; capacity: number | null; }; export async function findFirstPendingWaitlistEntry( db: PromoteDb, groupId: string, ): Promise { const record = await db.groupWaitlistEntry.findFirst({ where: { groupId, status: 'PENDING' }, orderBy: { createdAt: 'asc' }, }); return record as GroupWaitlistEntryRecord | null; } export async function promoteWaitlistEntryRecord( db: PromoteDb, group: PromoteGroup, entry: GroupWaitlistEntryRecord, ): Promise> { if (group.capacity !== null) { const currentCount = await db.attendee.count({ where: { groupId: group.id, status: 'ACTIVE' }, }); if (currentCount >= group.capacity) { return err(capacityReachedProblem(group.capacity)); } } const existing = await db.attendee.findFirst({ where: { groupId: group.id, phone: entry.phone }, }); 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 attendee = await db.attendee.create({ data: { groupId: group.id, fullName: entry.fullName, phone: entry.phone, email: entry.email, notes: entry.notes, }, }); return ok(toAttendeeDto(attendee as unknown as AttendeeRecord)); } export type PromoteFirstResult = { attendee: AttendeeDto; entry: GroupWaitlistEntryRecord; }; export async function promoteFirstPendingWaitlistEntry( db: PromoteDb, group: PromoteGroup, ): Promise> { const entry = await findFirstPendingWaitlistEntry(db, group.id); if (!entry) { return ok(null); } const result = await promoteWaitlistEntryRecord(db, group, entry); if (!result.ok) { return result; } return ok({ attendee: result.value, entry }); }