import { useCallback, useEffect, useRef, useState } from 'react'; import { disconnectSocket, getSocket } from '@/lib/socket'; import type { CallDisposition } from '@/types/entities'; type CallState = 'idle' | 'ringing' | 'active' | 'completed'; type EnrichedLead = { id: string; firstName: string; lastName: string; phone: string; email?: string; source?: string; status?: string; campaign?: string; interestedService?: string; age: number; aiSummary?: string; aiSuggestedAction?: string; recentActivities: { activityType: string; summary: string; occurredAt: string; performedBy: string }[]; }; type IncomingCallEvent = { callSid: string; eventType: 'ringing' | 'answered' | 'ended'; lead: EnrichedLead | null; callerPhone: string; agentName: string; timestamp: string; }; export const useCallEvents = (agentName: string) => { const [callState, setCallState] = useState('idle'); const [activeLead, setActiveLead] = useState(null); const [activeCallSid, setActiveCallSid] = useState(null); const [callStartTime, setCallStartTime] = useState(null); const [isConnected, setIsConnected] = useState(false); const completedTimerRef = useRef(null); // Connect to WebSocket and register agent useEffect(() => { const socket = getSocket(); socket.on('connect', () => { setIsConnected(true); socket.emit('agent:register', agentName); }); socket.on('disconnect', () => { setIsConnected(false); }); socket.on('agent:registered', (data: { agentName: string }) => { console.log(`Registered as agent: ${data.agentName}`); }); socket.on('call:incoming', (event: IncomingCallEvent) => { if (event.eventType === 'ringing' || event.eventType === 'answered') { setActiveCallSid(event.callSid); setActiveLead(event.lead); setCallStartTime(event.timestamp); if (event.eventType === 'ringing') { setCallState('ringing'); // Auto-transition to active after 1.5s (call is answered) setTimeout(() => setCallState('active'), 1500); } else { setCallState('active'); } } else if (event.eventType === 'ended') { // Call ended from Exotel side (e.g. customer hung up) setCallState('completed'); completedTimerRef.current = window.setTimeout(() => { setCallState('idle'); setActiveLead(null); setActiveCallSid(null); setCallStartTime(null); }, 3000); } }); socket.on('call:disposition:ack', () => { // Disposition saved on server }); socket.connect(); return () => { if (completedTimerRef.current) { clearTimeout(completedTimerRef.current); } disconnectSocket(); }; }, [agentName]); // Send disposition to server const sendDisposition = useCallback( (disposition: CallDisposition, notes: string) => { const socket = getSocket(); const duration = callStartTime ? Math.floor((Date.now() - new Date(callStartTime).getTime()) / 1000) : 0; socket.emit('call:disposition', { callSid: activeCallSid, leadId: activeLead?.id ?? null, disposition, notes, agentName, callerPhone: activeLead?.phone ?? '', startedAt: callStartTime, duration, }); setCallState('completed'); completedTimerRef.current = window.setTimeout(() => { setCallState('idle'); setActiveLead(null); setActiveCallSid(null); setCallStartTime(null); }, 3000); }, [activeCallSid, activeLead, agentName, callStartTime], ); return { callState, activeLead, activeCallSid, isConnected, sendDisposition, }; };