import prisma from '../config/database'; import { Prisma } from '@prisma/client'; export interface CreateReportData { userId: string; latitude: number; longitude: number; locationSource: 'exif' | 'gps' | 'manual'; photoUrls: string[]; photoTimestamp?: Date; litterType: string; quantity: string; description?: string; environmentalConcerns?: Array<{ type: string; severity: string; description: string; }>; } export class ReportService { async createReport(data: CreateReportData) { // Create report using raw SQL to handle the POINT field const reportId = await prisma.$queryRaw>` INSERT INTO reports ( user_id, location, latitude, longitude, location_source, photo_urls, photo_timestamp, litter_type, quantity, description ) VALUES ( ${data.userId}, ST_GeomFromText(${`POINT(${data.longitude} ${data.latitude})`}, 4326), ${data.latitude}, ${data.longitude}, ${data.locationSource}, ${JSON.stringify(data.photoUrls)}, ${data.photoTimestamp || null}, ${data.litterType}, ${data.quantity}, ${data.description || null} ) `; // Get the created report ID const createdId = await prisma.$queryRaw>` SELECT id FROM reports ORDER BY created_at DESC LIMIT 1 `; // Create environmental concerns if provided if (data.environmentalConcerns && data.environmentalConcerns.length > 0) { for (const concern of data.environmentalConcerns) { await prisma.$queryRaw` INSERT INTO environmental_concerns (report_id, concern_type, severity, description) VALUES (${createdId[0].id}, ${concern.type}, ${concern.severity}, ${concern.description}) `; } } // Fetch and return the complete report const report = await prisma.report.findUnique({ where: { id: createdId[0].id }, include: { user: { select: { id: true, username: true, avatarUrl: true, }, }, environmentalConcerns: true, }, }); return report; } async getReports(filters?: { startDate?: Date; endDate?: Date; litterTypes?: string[]; quantities?: string[]; verificationStatus?: string[]; bounds?: { north: number; south: number; east: number; west: number }; }) { const where: any = {}; if (filters?.startDate || filters?.endDate) { where.createdAt = {}; if (filters.startDate) where.createdAt.gte = filters.startDate; if (filters.endDate) where.createdAt.lte = filters.endDate; } if (filters?.litterTypes?.length) { where.litterType = { in: filters.litterTypes }; } if (filters?.quantities?.length) { where.quantity = { in: filters.quantities }; } if (filters?.verificationStatus?.length) { where.verificationStatus = { in: filters.verificationStatus }; } if (filters?.bounds) { where.latitude = { gte: new Prisma.Decimal(filters.bounds.south), lte: new Prisma.Decimal(filters.bounds.north), }; where.longitude = { gte: new Prisma.Decimal(filters.bounds.west), lte: new Prisma.Decimal(filters.bounds.east), }; } const reports = await prisma.report.findMany({ where, include: { user: { select: { id: true, username: true, avatarUrl: true, }, }, environmentalConcerns: true, verifications: true, }, orderBy: { createdAt: 'desc', }, }); return reports; } async getReportById(id: string) { const report = await prisma.report.findUnique({ where: { id }, include: { user: { select: { id: true, username: true, avatarUrl: true, }, }, environmentalConcerns: true, verifications: { include: { user: { select: { id: true, username: true, }, }, }, }, }, }); return report; } async deleteReport(id: string, userId: string) { // Verify ownership const report = await prisma.report.findUnique({ where: { id }, select: { userId: true }, }); if (!report) { throw new Error('Report not found'); } if (report.userId !== userId) { throw new Error('Unauthorized to delete this report'); } await prisma.report.delete({ where: { id }, }); } } export const reportService = new ReportService();