import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); interface EventFilters { status?: string; startDate?: Date; endDate?: Date; latitude?: number; longitude?: number; radius?: number; } interface CreateEventData { organizerId: string; title: string; description: string; latitude: number; longitude: number; locationName: string; scheduledDate: Date; duration: number; } interface UpdateEventData { title?: string; description?: string; scheduledDate?: Date; duration?: number; status?: 'upcoming' | 'completed' | 'cancelled'; litterCollected?: number; photos?: string[]; } export const eventService = { async getEvents(filters: EventFilters = {}) { const where: any = {}; // Filter by status if (filters.status && filters.status !== 'all') { where.status = filters.status; } // Filter by date range if (filters.startDate || filters.endDate) { where.scheduledDate = {}; if (filters.startDate) { where.scheduledDate.gte = filters.startDate; } if (filters.endDate) { where.scheduledDate.lte = filters.endDate; } } const events = await prisma.event.findMany({ where, orderBy: { scheduledDate: 'asc', }, include: { organizer: { select: { id: true, username: true, firstName: true, lastName: true, avatarUrl: true, }, }, _count: { select: { registrations: true, }, }, }, }); // Map to frontend format return events.map((event) => ({ id: event.id, organizerId: event.organizerId, title: event.title, description: event.description, latitude: parseFloat(event.latitude.toString()), longitude: parseFloat(event.longitude.toString()), locationName: event.locationName, scheduledDate: event.scheduledDate, duration: event.duration, status: event.status || 'upcoming', participantCount: event._count.registrations, litterCollected: event.litterCollected ? parseFloat(event.litterCollected.toString()) : undefined, photos: event.photos ? (Array.isArray(event.photos) ? event.photos : []) : undefined, createdAt: event.createdAt, updatedAt: event.updatedAt, })); }, async getEventById(id: string) { const event = await prisma.event.findUnique({ where: { id }, include: { organizer: { select: { id: true, username: true, firstName: true, lastName: true, avatarUrl: true, }, }, _count: { select: { registrations: true, }, }, }, }); if (!event) return null; return { id: event.id, organizerId: event.organizerId, title: event.title, description: event.description, latitude: parseFloat(event.latitude.toString()), longitude: parseFloat(event.longitude.toString()), locationName: event.locationName, scheduledDate: event.scheduledDate, duration: event.duration, status: event.status || 'upcoming', participantCount: event._count.registrations, litterCollected: event.litterCollected ? parseFloat(event.litterCollected.toString()) : undefined, photos: event.photos ? (Array.isArray(event.photos) ? event.photos : []) : undefined, createdAt: event.createdAt, updatedAt: event.updatedAt, }; }, async createEvent(data: CreateEventData) { // Create point geometry for spatial queries const locationPoint = `POINT(${data.longitude} ${data.latitude})`; const event = await prisma.$executeRaw` INSERT INTO events ( id, organizer_id, title, description, location, latitude, longitude, location_name, scheduled_date, duration, status, participant_count ) VALUES ( UUID(), ${data.organizerId}, ${data.title}, ${data.description}, ST_GeomFromText(${locationPoint}, 4326), ${data.latitude}, ${data.longitude}, ${data.locationName}, ${data.scheduledDate}, ${data.duration}, 'upcoming', 0 ) `; // Fetch the created event const createdEvent = await prisma.event.findFirst({ where: { organizerId: data.organizerId, title: data.title, scheduledDate: data.scheduledDate, }, orderBy: { createdAt: 'desc', }, include: { _count: { select: { registrations: true, }, }, }, }); if (!createdEvent) { throw new Error('Failed to retrieve created event'); } return { id: createdEvent.id, organizerId: createdEvent.organizerId, title: createdEvent.title, description: createdEvent.description, latitude: parseFloat(createdEvent.latitude.toString()), longitude: parseFloat(createdEvent.longitude.toString()), locationName: createdEvent.locationName, scheduledDate: createdEvent.scheduledDate, duration: createdEvent.duration, status: createdEvent.status || 'upcoming', participantCount: createdEvent._count.registrations, litterCollected: createdEvent.litterCollected ? parseFloat(createdEvent.litterCollected.toString()) : undefined, photos: createdEvent.photos ? (Array.isArray(createdEvent.photos) ? createdEvent.photos : []) : undefined, createdAt: createdEvent.createdAt, updatedAt: createdEvent.updatedAt, }; }, async updateEvent(id: string, data: UpdateEventData) { const updateData: any = {}; if (data.title !== undefined) updateData.title = data.title; if (data.description !== undefined) updateData.description = data.description; if (data.scheduledDate !== undefined) updateData.scheduledDate = data.scheduledDate; if (data.duration !== undefined) updateData.duration = data.duration; if (data.status !== undefined) updateData.status = data.status; if (data.litterCollected !== undefined) updateData.litterCollected = data.litterCollected; if (data.photos !== undefined) updateData.photos = data.photos; const event = await prisma.event.update({ where: { id }, data: updateData, include: { _count: { select: { registrations: true, }, }, }, }); return { id: event.id, organizerId: event.organizerId, title: event.title, description: event.description, latitude: parseFloat(event.latitude.toString()), longitude: parseFloat(event.longitude.toString()), locationName: event.locationName, scheduledDate: event.scheduledDate, duration: event.duration, status: event.status || 'upcoming', participantCount: event._count.registrations, litterCollected: event.litterCollected ? parseFloat(event.litterCollected.toString()) : undefined, photos: event.photos ? (Array.isArray(event.photos) ? event.photos : []) : undefined, createdAt: event.createdAt, updatedAt: event.updatedAt, }; }, async deleteEvent(id: string) { await prisma.event.delete({ where: { id }, }); }, async registerForEvent(eventId: string, userId: string) { // Check if already registered const existing = await prisma.eventRegistration.findUnique({ where: { userId_eventId: { userId, eventId, }, }, }); if (existing) { throw new Error('User is already registered for this event'); } // Create registration const registration = await prisma.eventRegistration.create({ data: { userId, eventId, }, }); return { id: registration.id, userId: registration.userId, eventId: registration.eventId, registeredAt: registration.registeredAt || new Date(), attended: registration.attended || false, }; }, async unregisterFromEvent(eventId: string, userId: string) { await prisma.eventRegistration.delete({ where: { userId_eventId: { userId, eventId, }, }, }); }, async getEventRegistrations(eventId: string) { const registrations = await prisma.eventRegistration.findMany({ where: { eventId }, include: { user: { select: { id: true, username: true, firstName: true, lastName: true, avatarUrl: true, }, }, }, }); return registrations.map((reg) => ({ id: reg.id, userId: reg.userId, eventId: reg.eventId, registeredAt: reg.registeredAt || new Date(), attended: reg.attended || false, user: reg.user, })); }, async isUserRegistered(eventId: string, userId: string): Promise { const registration = await prisma.eventRegistration.findUnique({ where: { userId_eventId: { userId, eventId, }, }, }); return !!registration; }, };