import { Controller, Get, Post, Patch, Body, Param, Query, UseGuards, HttpCode, HttpStatus, Inject, forwardRef, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery, ApiParam, } from '@nestjs/swagger'; import { SupportChatService } from './support-chat.service'; import { SendSupportMessageDto } from './dto'; import { JwtAuthGuard } from '../auth/guards'; import { CurrentUser } from '../auth/decorators'; import { Roles } from '../auth/decorators/roles.decorator'; import { UserRole, SupportChatStatus } from '@prisma/client'; import { MessagesGateway } from '../messages/messages.gateway'; @ApiTags('Support Chat') @Controller('support-chat') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') export class SupportChatController { constructor( private readonly supportChatService: SupportChatService, @Inject(forwardRef(() => MessagesGateway)) private readonly messagesGateway: MessagesGateway, ) {} // ========================================== // GET OR CREATE SUPPORT CHAT (User/Agent) // ========================================== @Post() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Get or create a support chat for the current user' }) async getOrCreateChat(@CurrentUser('id') userId: string) { return this.supportChatService.getOrCreateChat(userId); } // ========================================== // GET MY SUPPORT CHAT (User/Agent) // ========================================== @Get('my') @ApiOperation({ summary: 'Get current user\'s open support chat' }) async getMyChat(@CurrentUser('id') userId: string) { return this.supportChatService.getOrCreateChat(userId); } // ========================================== // GET MESSAGES // ========================================== @Get(':id/messages') @ApiOperation({ summary: 'Get messages for a support chat' }) @ApiParam({ name: 'id', description: 'Support Chat ID' }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'limit', required: false, type: Number }) async getMessages( @Param('id') chatId: string, @CurrentUser('id') userId: string, @CurrentUser('role') role: UserRole, @Query('page') page?: string, @Query('limit') limit?: string, ) { // Validate access const isAdmin = role === UserRole.ADMIN; await this.supportChatService.getChat(chatId, userId, isAdmin); return this.supportChatService.getMessages( chatId, page ? parseInt(page, 10) : 1, limit ? parseInt(limit, 10) : 50, ); } // ========================================== // SEND MESSAGE (REST fallback) // ========================================== @Post(':id/messages') @HttpCode(HttpStatus.CREATED) @ApiOperation({ summary: 'Send a message in a support chat' }) @ApiParam({ name: 'id', description: 'Support Chat ID' }) async sendMessage( @Param('id') chatId: string, @CurrentUser('id') userId: string, @CurrentUser('role') role: UserRole, @Body() dto: SendSupportMessageDto, ) { // Validate access const isAdmin = role === UserRole.ADMIN; await this.supportChatService.getChat(chatId, userId, isAdmin); const senderRole = role === UserRole.ADMIN ? 'ADMIN' : 'USER'; const message = await this.supportChatService.sendMessage(chatId, userId, senderRole, dto.content); // Emit WebSocket events for real-time updates this.messagesGateway.server.to(`support:${chatId}`).emit('support_new_message', message); // If user/agent sent the message, notify admins about unread count if (senderRole !== 'ADMIN') { const unreadTotal = await this.supportChatService.getAdminUnreadTotal(); this.messagesGateway.server.to('admin_room').emit('support_unread_update', { unreadTotal }); } // If admin sent the message, notify the user directly if (senderRole === 'ADMIN') { const chat = await this.supportChatService.getChat(chatId, userId, true); this.messagesGateway.sendToUser(chat.userId, 'support_new_message', message); } return message; } // ========================================== // MARK AS READ // ========================================== @Patch(':id/read') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Mark support chat messages as read' }) @ApiParam({ name: 'id', description: 'Support Chat ID' }) async markAsRead( @Param('id') chatId: string, @CurrentUser('role') role: UserRole, ) { const result = await this.supportChatService.markAsRead(chatId, role); // If admin marked as read, update sidebar badge for all admins if (role === UserRole.ADMIN) { const unreadTotal = await this.supportChatService.getAdminUnreadTotal(); this.messagesGateway.server.to('admin_room').emit('support_unread_update', { unreadTotal }); } return result; } // ========================================== // ADMIN: GET TOTAL UNREAD COUNT // ========================================== @Get('admin/unread-total') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'Admin: Get total unread support message count' }) @ApiResponse({ status: 200, description: 'Total unread count returned' }) async getUnreadTotal() { return this.supportChatService.getAdminUnreadTotal(); } // ========================================== // ADMIN: GET ALL SUPPORT CHATS // ========================================== @Get('admin/all') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'Admin: Get all support chats' }) @ApiQuery({ name: 'status', required: false, enum: SupportChatStatus }) async getAllChats(@Query('status') status?: SupportChatStatus) { return this.supportChatService.getAllChats(status); } // ========================================== // ADMIN: CLOSE SUPPORT CHAT // ========================================== @Patch('admin/:id/close') @Roles(UserRole.ADMIN) @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Admin: Close a support chat' }) @ApiParam({ name: 'id', description: 'Support Chat ID' }) async closeChat(@Param('id') chatId: string) { return this.supportChatService.closeChat(chatId); } }