import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common'; import { UserStatus, UserRole, Prisma, VerificationStatus } from '@prisma/client'; import { PrismaService } from '../prisma/prisma.service'; type UserWithProfiles = Prisma.UserGetPayload<{ include: { userProfile: true; agentProfile: { include: { agentType: true } } }; }>; @Injectable() export class UsersService { constructor(private readonly prisma: PrismaService) {} // ============================================= // USER PROFILE METHODS (for regular users) // ============================================= async getMyUserProfile(userId: string) { const user = await this.prisma.user.findUnique({ where: { id: userId }, include: { userProfile: true }, }); if (!user) { throw new NotFoundException('User not found'); } if (user.role !== UserRole.USER) { throw new BadRequestException('This endpoint is for regular users only'); } // Create user profile if it doesn't exist let profile = user.userProfile; if (!profile) { profile = await this.prisma.userProfile.create({ data: { userId: user.id, }, }); } return { id: profile.id, userId: user.id, email: user.email, firstName: profile.firstName, lastName: profile.lastName, phone: profile.phone, avatar: profile.avatar, city: profile.city, state: profile.state, country: profile.country, createdAt: profile.createdAt, updatedAt: profile.updatedAt, }; } async updateMyUserProfile( userId: string, data: { firstName?: string; lastName?: string; phone?: string; avatar?: string | null; city?: string; state?: string; country?: string; }, ) { const user = await this.prisma.user.findUnique({ where: { id: userId }, include: { userProfile: true }, }); if (!user) { throw new NotFoundException('User not found'); } if (user.role !== UserRole.USER) { throw new BadRequestException('This endpoint is for regular users only'); } // Create or update user profile const profile = await this.prisma.userProfile.upsert({ where: { userId }, create: { userId, ...data, }, update: data, }); return { id: profile.id, userId: user.id, email: user.email, firstName: profile.firstName, lastName: profile.lastName, phone: profile.phone, avatar: profile.avatar, city: profile.city, state: profile.state, country: profile.country, createdAt: profile.createdAt, updatedAt: profile.updatedAt, }; } // ============================================= // ADMIN METHODS // ============================================= async findAll(page = 1, limit = 10, role?: UserRole) { const skip = (page - 1) * limit; // Build where clause for optional role filter const where: Prisma.UserWhereInput = role ? { role } : {}; const [users, total] = await Promise.all([ this.prisma.user.findMany({ where, skip, take: limit, orderBy: { createdAt: 'desc' }, include: { userProfile: true, agentProfile: { include: { agentType: true, }, }, }, }) as Promise, this.prisma.user.count({ where }), ]); return { users: users.map((user) => { // Get the appropriate profile based on role const profile = user.role === UserRole.AGENT ? user.agentProfile : user.userProfile; return { id: user.id, email: user.email, role: user.role, status: user.status, emailVerified: user.emailVerified, authProvider: user.authProvider, createdAt: user.createdAt, lastLoginAt: user.lastLoginAt, profile: profile ? { firstName: profile.firstName, lastName: profile.lastName, avatar: profile.avatar, phone: profile.phone, city: profile.city, state: profile.state, country: profile.country, } : null, }; }), total, page, limit, totalPages: Math.ceil(total / limit), }; } async findOne(id: string) { const user = (await this.prisma.user.findUnique({ where: { id }, include: { userProfile: true, agentProfile: { include: { agentType: true, }, }, }, })) as UserWithProfiles | null; if (!user) { return null; } // Base user data const baseData = { id: user.id, email: user.email, role: user.role, status: user.status, emailVerified: user.emailVerified, emailVerifiedAt: user.emailVerifiedAt, authProvider: user.authProvider, createdAt: user.createdAt, lastLoginAt: user.lastLoginAt, }; // For agents, return full agent profile if (user.role === UserRole.AGENT && user.agentProfile) { return { ...baseData, profile: { firstName: user.agentProfile.firstName, lastName: user.agentProfile.lastName, avatar: user.agentProfile.avatar, phone: user.agentProfile.phone, city: user.agentProfile.city, state: user.agentProfile.state, country: user.agentProfile.country, }, agentProfile: { id: user.agentProfile.id, slug: user.agentProfile.slug, bio: user.agentProfile.bio, headline: user.agentProfile.headline, companyName: user.agentProfile.companyName, licenseNumber: user.agentProfile.licenseNumber, yearsOfExperience: user.agentProfile.yearsOfExperience, isProfileComplete: user.agentProfile.isProfileComplete, profileCompleteness: user.agentProfile.profileCompleteness, agentTypeId: user.agentProfile.agentTypeId, agentType: user.agentProfile.agentType, // Verification fields isVerified: user.agentProfile.isVerified, verificationStatus: user.agentProfile.verificationStatus, verificationNote: user.agentProfile.verificationNote, verifiedAt: user.agentProfile.verifiedAt, verifiedBy: user.agentProfile.verifiedBy, }, }; } // For regular users return { ...baseData, profile: user.userProfile ? { firstName: user.userProfile.firstName, lastName: user.userProfile.lastName, avatar: user.userProfile.avatar, phone: user.userProfile.phone, city: user.userProfile.city, state: user.userProfile.state, country: user.userProfile.country, } : null, }; } async updateStatus(id: string, status: UserStatus) { return this.prisma.user.update({ where: { id }, data: { status }, }); } async updateAgentType(userId: string, agentTypeId: string) { // Get user to verify they are an agent const user = await this.prisma.user.findUnique({ where: { id: userId }, include: { agentProfile: true }, }); if (!user) { throw new NotFoundException('User not found'); } if (user.role !== UserRole.AGENT) { throw new BadRequestException('User is not an agent'); } if (!user.agentProfile) { throw new NotFoundException('Agent profile not found'); } // Verify agent type exists const agentType = await this.prisma.agentType.findUnique({ where: { id: agentTypeId }, }); if (!agentType) { throw new BadRequestException('Invalid agent type'); } // Update agent profile with new agent type const updatedProfile = await this.prisma.agentProfile.update({ where: { id: user.agentProfile.id }, data: { agentTypeId }, include: { agentType: true }, }); return { id: user.id, agentTypeId: updatedProfile.agentTypeId, agentType: updatedProfile.agentType, message: 'Agent type updated successfully', }; } async getVerificationDocuments(userId: string) { // Get user to verify they are an agent const user = await this.prisma.user.findUnique({ where: { id: userId }, include: { agentProfile: true }, }); if (!user) { throw new NotFoundException('User not found'); } if (user.role !== UserRole.AGENT) { throw new BadRequestException('User is not an agent'); } if (!user.agentProfile) { throw new NotFoundException('Agent profile not found'); } // Find the upload-documents section field const field = await this.prisma.profileField.findFirst({ where: { section: { slug: 'upload-documents' }, slug: 'documents', }, }); if (!field) return []; const fieldValue = await this.prisma.agentProfileFieldValue.findUnique({ where: { agentProfileId_fieldId: { agentProfileId: user.agentProfile.id, fieldId: field.id, }, }, }); return fieldValue?.jsonValue || []; } async updateVerification( userId: string, data: { status: VerificationStatus; note?: string; }, adminId: string, ) { // Get user to verify they are an agent const user = await this.prisma.user.findUnique({ where: { id: userId }, include: { agentProfile: true }, }); if (!user) { throw new NotFoundException('User not found'); } if (user.role !== UserRole.AGENT) { throw new BadRequestException('User is not an agent'); } if (!user.agentProfile) { throw new NotFoundException('Agent profile not found'); } const updateData: Prisma.AgentProfileUpdateInput = { verificationStatus: data.status, verificationNote: data.note || null, }; if (data.status === VerificationStatus.APPROVED) { updateData.isVerified = true; updateData.verifiedAt = new Date(); updateData.verifiedBy = adminId; } else if (data.status === VerificationStatus.REJECTED) { updateData.isVerified = false; updateData.verifiedAt = null; updateData.verifiedBy = null; } const updatedProfile = await this.prisma.agentProfile.update({ where: { id: user.agentProfile.id }, data: updateData, include: { agentType: true, }, }); return { id: user.id, agentProfileId: updatedProfile.id, isVerified: updatedProfile.isVerified, verificationStatus: updatedProfile.verificationStatus, verificationNote: updatedProfile.verificationNote, verifiedAt: updatedProfile.verifiedAt, verifiedBy: updatedProfile.verifiedBy, message: `Verification ${data.status.toLowerCase()} successfully`, }; } }