import { Body, Controller, Delete, HttpCode, HttpStatus, NotFoundException, Param, Post, UseGuards, } from "@nestjs/common"; import { getPrismaClient, withTenantContext } from "@b2bcall/database"; import { recordAuditEvent, type AccessTokenClaims } from "@b2bcall/auth"; import { JwtAuthGuard } from "../common/guards/jwt-auth.guard"; import { PermissionGuard } from "../common/guards/permission.guard"; import { RequirePermission } from "../common/decorators/require-permission.decorator"; import { CurrentUser } from "../common/decorators/current-user.decorator"; import { AddQueueAgentDto } from "./dto/add-queue-agent.dto"; import { notifyTierChanged } from "../agents/agent-sync.helper"; @UseGuards(JwtAuthGuard, PermissionGuard) @Controller("queues/:queueId/agents") export class QueueAgentsController { @RequirePermission("queues.manage") @Post() async add( @CurrentUser() user: AccessTokenClaims, @Param("queueId") queueId: string, @Body() dto: AddQueueAgentDto, ) { const prisma = getPrismaClient(); const tenantId = user.tenantId!; const [queue, agent] = await withTenantContext(prisma, tenantId, (tx) => Promise.all([ tx.queue.findFirst({ where: { id: queueId, tenantId, deletedAt: null } }), tx.agent.findFirst({ where: { id: dto.agentId, tenantId, deletedAt: null } }), ]), ); if (!queue || !agent) { throw new NotFoundException("Fila ou agente nao encontrado"); } const level = dto.level ?? 1; const position = dto.position ?? 1; const tier = await withTenantContext(prisma, tenantId, (tx) => tx.tier.upsert({ where: { queueId_agentId: { queueId, agentId: dto.agentId } }, update: { level, position }, create: { tenantId, queueId, agentId: dto.agentId, level, position }, }), ); await recordAuditEvent(prisma, { action: "TIER_ADD", tenantId, userId: user.sub, entityType: "tier", entityId: tier.id, after: { queueId, agentId: dto.agentId, level, position }, }); await notifyTierChanged(tenantId, queueId, dto.agentId, "upsert", level, position); return tier; } @RequirePermission("queues.manage") @Delete(":agentId") @HttpCode(HttpStatus.NO_CONTENT) async remove( @CurrentUser() user: AccessTokenClaims, @Param("queueId") queueId: string, @Param("agentId") agentId: string, ) { const prisma = getPrismaClient(); const tenantId = user.tenantId!; const result = await withTenantContext(prisma, tenantId, (tx) => tx.tier.deleteMany({ where: { tenantId, queueId, agentId } }), ); if (result.count === 0) { throw new NotFoundException(); } await recordAuditEvent(prisma, { action: "TIER_REMOVE", tenantId, userId: user.sub, entityType: "tier", entityId: `${queueId}:${agentId}`, }); await notifyTierChanged(tenantId, queueId, agentId, "delete"); } }