import { PrismaClient } from '@b2bcall/database'; import { estimateAgentsFreeingSoon, type LiveCounts } from './predictive-engine'; /** * Consulta o estado real (Postgres) para montar o LiveCounts que o motor * preditivo usa a cada tick. Nunca lê do Asterisk diretamente — o Asterisk * não é fonte de verdade de negócio (agente.md seção 97). */ export async function getLiveCounts( prisma: PrismaClient, campaignId: string, queueId: string, avgTalkTimeSeconds: number, ): Promise { const members = await prisma.queueMember.findMany({ where: { queueId }, select: { agentId: true } }); const agentIds = members.map((m) => m.agentId); const openStates = agentIds.length ? await prisma.agentStateEvent.findMany({ where: { agentId: { in: agentIds }, endedAt: null }, select: { agentId: true, state: true, startedAt: true }, }) : []; const availableAgents = openStates.filter((s) => s.state === 'AVAILABLE').length; const inCallStartedAt = openStates.filter((s) => s.state === 'IN_CALL').map((s) => s.startedAt); const agentsLikelyToFreeSoon = estimateAgentsFreeingSoon(inCallStartedAt, avgTalkTimeSeconds, 15); const [dialingCalls, ringingCalls, connectedWaitingAgent, agentConnectedCalls] = await Promise.all([ prisma.dialAttempt.count({ where: { campaignId, state: 'ORIGINATING' } }), prisma.dialAttempt.count({ where: { campaignId, state: 'RINGING' } }), prisma.dialAttempt.count({ where: { campaignId, state: 'QUEUED' } }), prisma.dialAttempt.count({ where: { campaignId, state: 'AGENT_CONNECTED' } }), ]); return { availableAgents, agentsLikelyToFreeSoon, dialingCalls, ringingCalls, connectedWaitingAgent, agentConnectedCalls, }; }