import { prisma } from '../../lib/prisma' export async function findPublicOffers() { return prisma.offer.findMany({ where: { isPublic: true, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } }, include: { company: { include: { brand: { select: { displayName: true, logoUrl: true, subdomain: true, primaryColor: true } } } } }, orderBy: [{ isFeatured: 'desc' }, { createdAt: 'desc' }], take: 50, }) } export async function findCitiesFromCompanies() { return prisma.company.findMany({ where: { status: { in: ['ACTIVE', 'TRIALING'] }, brand: { isListedOnCarplace: true, publicCity: { not: null } }, }, select: { brand: { select: { publicCity: true } } }, }) } export async function findListedCompanies(where: any, skip: number, take: number) { return prisma.company.findMany({ where, include: { brand: { select: { displayName: true, logoUrl: true, subdomain: true, publicCity: true, publicCountry: true, carplaceRating: true, primaryColor: true } }, _count: { select: { vehicles: { where: { isPublished: true, status: 'AVAILABLE' } } } }, }, skip, take, }) } export async function findPublishedVehicles(where: any) { return prisma.vehicle.findMany({ where, include: { company: { include: { brand: { select: { displayName: true, logoUrl: true, subdomain: true, carplaceRating: true, primaryColor: true } } } }, }, orderBy: { dailyRate: 'asc' }, }) } export async function findVehicleForCarplace(vehicleId: string, companySlug: string) { return prisma.vehicle.findFirst({ where: { id: vehicleId, isPublished: true, status: 'AVAILABLE', company: { status: { in: ['ACTIVE', 'TRIALING'] }, OR: [{ slug: companySlug }, { brand: { subdomain: companySlug } }] } }, include: { company: { include: { brand: true } } }, }) } export async function findVehicleForCarplaceById(vehicleId: string) { return prisma.vehicle.findFirst({ where: { id: vehicleId, isPublished: true, status: 'AVAILABLE', company: { status: { in: ['ACTIVE', 'TRIALING'] }, brand: { isListedOnCarplace: true } }, }, include: { company: { include: { brand: true } } }, }) } export async function upsertCarplaceCustomer(companyId: string, data: { email: string firstName: string lastName: string phone?: string dateOfBirth?: string nationality?: string identityDocumentNumber?: string fullAddress?: string driverLicense?: string licenseExpiry?: string licenseIssuedAt?: string licenseCountry?: string licenseCategory?: string internationalLicenseNumber?: string }) { const existing = await prisma.customer.findUnique({ where: { companyId_email: { companyId, email: data.email } }, select: { id: true, address: true }, }) // Only patch the address JSON with fields that were actually provided const prevAddress = (existing?.address && typeof existing.address === 'object' && !Array.isArray(existing.address)) ? existing.address as Record : {} const hasAddressPatch = data.fullAddress || data.identityDocumentNumber || data.internationalLicenseNumber !== undefined const address = hasAddressPatch ? { ...prevAddress, ...(data.fullAddress ? { fullAddress: data.fullAddress } : {}), ...(data.identityDocumentNumber ? { identityDocumentNumber: data.identityDocumentNumber } : {}), ...(data.internationalLicenseNumber !== undefined ? { internationalLicenseNumber: data.internationalLicenseNumber ?? null } : {}), } : undefined const payload = { firstName: data.firstName, lastName: data.lastName, email: data.email, ...(data.phone ? { phone: data.phone } : {}), ...(data.driverLicense ? { driverLicense: data.driverLicense, licenseNumber: data.driverLicense } : {}), ...(data.dateOfBirth ? { dateOfBirth: new Date(data.dateOfBirth) } : {}), ...(data.nationality ? { nationality: data.nationality } : {}), ...(address !== undefined ? { address } : {}), ...(data.licenseExpiry ? { licenseExpiry: new Date(data.licenseExpiry) } : {}), ...(data.licenseIssuedAt ? { licenseIssuedAt: new Date(data.licenseIssuedAt) } : {}), ...(data.licenseCountry ? { licenseCountry: data.licenseCountry } : {}), ...(data.licenseCategory ? { licenseCategory: data.licenseCategory } : {}), } if (!existing) { return prisma.customer.create({ data: { companyId, ...payload } }) } return prisma.customer.update({ where: { id: existing.id }, data: payload }) } export async function createCarplaceReservation(data: { companyId: string; vehicleId: string; customerId: string startDate: Date; endDate: Date; pickupLocation?: string | null; returnLocation?: string | null dailyRate: number; totalDays: number; totalAmount: number; notes?: string; bookingReference?: string }) { return prisma.reservation.create({ data: { ...data, source: 'CARPLACE', status: 'DRAFT' }, }) } export async function createCarplaceFunnelEvent(data: { eventName: string companySlug: string vehicleId: string renterId?: string | null sessionId?: string path?: string metadata?: Record }) { const vehicle = await prisma.vehicle.findFirst({ where: { id: data.vehicleId, isPublished: true, company: { status: { in: ['ACTIVE', 'TRIALING'] }, OR: [{ slug: data.companySlug }, { brand: { subdomain: data.companySlug } }] }, status: 'AVAILABLE', }, select: { companyId: true }, }) return prisma.carplaceFunnelEvent.create({ data: { eventName: data.eventName, companyId: vehicle?.companyId ?? null, companySlug: data.companySlug, vehicleId: data.vehicleId, renterId: data.renterId ?? null, sessionId: data.sessionId, path: data.path, metadata: (data.metadata ?? {}) as any, }, }) } export async function findCompanyPage(slug: string) { return prisma.company.findFirst({ where: { status: { in: ['ACTIVE', 'TRIALING'] }, OR: [{ slug }, { brand: { subdomain: slug } }] }, include: { brand: true, vehicles: { where: { isPublished: true, status: 'AVAILABLE' }, orderBy: { createdAt: 'desc' } }, offers: { where: { isPublic: true, isActive: true, validUntil: { gte: new Date() } }, orderBy: { isFeatured: 'desc' } }, }, }) } export async function findCompanyBySlug(slug: string) { return prisma.company.findFirstOrThrow({ where: { status: { in: ['ACTIVE', 'TRIALING'] }, OR: [{ slug }, { brand: { subdomain: slug } }] } }) } export async function findCompanyReviews(companyId: string) { return prisma.review.findMany({ where: { companyId, isPublished: true }, include: { renter: { select: { firstName: true, lastName: true } } }, orderBy: { createdAt: 'desc' }, take: 50, }) } export async function findCompanyVehicles(companyId: string) { return prisma.vehicle.findMany({ where: { companyId, isPublished: true, status: 'AVAILABLE' }, orderBy: { createdAt: 'desc' }, }) } export async function findVehicleById(slug: string, vehicleId: string) { const company = await prisma.company.findFirstOrThrow({ where: { status: { in: ['ACTIVE', 'TRIALING'] }, OR: [{ slug }, { brand: { subdomain: slug } }] } }) return prisma.vehicle.findFirstOrThrow({ where: { id: vehicleId, companyId: company.id, isPublished: true, status: 'AVAILABLE' }, include: { company: { include: { brand: true } } }, }) } export async function findCompanyOffers(companyId: string) { return prisma.offer.findMany({ where: { companyId, isPublic: true, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } }, orderBy: [{ isFeatured: 'desc' }, { createdAt: 'desc' }], }) } export async function findReservationByReviewToken(token: string) { return prisma.reservation.findUnique({ where: { reviewToken: token }, include: { vehicle: { select: { make: true, model: true, year: true, photos: true } }, company: { include: { brand: { select: { displayName: true, logoUrl: true } } } }, review: { select: { id: true } }, }, }) } export async function findReservationForReviewSubmit(token: string) { return prisma.reservation.findUnique({ where: { reviewToken: token }, include: { review: { select: { id: true } } }, }) } export async function createReview(data: { reservationId: string; companyId: string; renterId?: string | null overallRating: number; vehicleRating?: number; serviceRating?: number; comment?: string }) { return prisma.review.create({ data: { ...data, renterId: data.renterId ?? undefined, isPublished: true }, }) } export async function invalidateReviewToken(reservationId: string) { return prisma.reservation.update({ where: { id: reservationId }, data: { reviewToken: null } }) } export async function findOfferByCode(code: string) { return prisma.offer.findFirst({ where: { promoCode: code, isActive: true, validFrom: { lte: new Date() }, validUntil: { gte: new Date() } }, }) }