[globals]
MVPCC_QUEUE_CODE=voice_lab
MVPCC_AI_QUEUE_CODE=voice_lab_ai
MVPCC_IVR_QUEUE_CODE=voice_lab_ivr
MVPCC_IVR_FALLBACK_QUEUE_CODE=voice_lab
MVPCC_MONITOR_DIR=/var/spool/asterisk/monitor/mvpcc
MVPCC_HUMAN_DIAL_TARGETS=PJSIP/2001&PJSIP/2002
MVPCC_AI_AUDIOSOCKET_SERVICE=127.0.0.1:9024
MVPCC_IVR_FASTAGI_HOSTPORT=127.0.0.1:4573

[from-softphones]
exten => 7000,1,NoOp(MVPCC human baseline entrypoint)
 same => n,Answer()
 same => n,Set(MVPCC_QUEUE=${MVPCC_QUEUE_CODE})
 same => n,Gosub(mvpcc-call-init,s,1(${EXTEN}))
 same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}))
 same => n,Hangup()

exten => 7100,1,NoOp(MVPCC AI-first voice lab entrypoint)
 same => n,Set(MVPCC_QUEUE=${IF($["${MVPCC_AI_QUEUE_OVERRIDE}" != ""]?${MVPCC_AI_QUEUE_OVERRIDE}:${MVPCC_AI_QUEUE_CODE})})
 same => n,GotoIf($["${MVPCC_AI_REUSE_CALL}" = "1"]?ai-reuse,1)
 same => n,Answer()
 same => n,Gosub(mvpcc-call-init,s,1(${EXTEN}))
 same => n,Goto(ai-audiosocket,1)

exten => ai-reuse,1,NoOp(MVPCC AI handoff reusing active IVR call)
 same => n,Goto(ai-audiosocket,1)

exten => ai-audiosocket,1,NoOp(MVPCC AI audio bridge start)
 same => n,GotoIf($["${MVPCC_AI_AUDIOSOCKET_SERVICE}" = ""]?ai-human-fallback,1)
 same => n,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 same => n,Set(MVPCC_MEDIA_UUID=${SHELL(uuidgen | tr -d '\r\n')})
 same => n,UserEvent(MVPCCAIAudioBridgeRequested,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL})
 same => n,UserEvent(MVPCCAIAudioSocketAttempt,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL})
 same => n,TryExec(AudioSocket(${MVPCC_MEDIA_UUID},${MVPCC_AI_AUDIOSOCKET_SERVICE}))
 same => n,UserEvent(MVPCCAIAudioSocketResult,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},UuidLen:${LEN(${MVPCC_MEDIA_UUID})},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},TryExecStatus:${TRYSTATUS},Channel:${CHANNEL})
 same => n,GotoIf($["${TRYSTATUS}" = "SUCCESS"]?ai-bridge-ended,1)
 same => n,UserEvent(MVPCCAIFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Reason:audiosocket_failed,TryExecStatus:${TRYSTATUS},Channel:${CHANNEL})
 same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}))
 same => n,Hangup()

exten => ai-bridge-ended,1,UserEvent(MVPCCAIAudioBridgeEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},MediaUUID:${MVPCC_MEDIA_UUID},AudioSocketService:${MVPCC_AI_AUDIOSOCKET_SERVICE},Channel:${CHANNEL})
 same => n,Hangup()

exten => ai-human-fallback,1,UserEvent(MVPCCAIFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Reason:audiosocket_service_missing,Channel:${CHANNEL})
 same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}))
 same => n,Hangup()

exten => 7200,1,NoOp(MVPCC IVR staging entrypoint)
 same => n,Answer()
 same => n,Set(MVPCC_QUEUE=${MVPCC_IVR_QUEUE_CODE})
 same => n,Set(MVPCC_IVR_FALLBACK_QUEUE_CODE=${GLOBAL(MVPCC_IVR_FALLBACK_QUEUE_CODE)})
 same => n,Gosub(mvpcc-call-init,s,1(${EXTEN}))
 same => n,Set(MVPCC_IVR_RESULT=started)
 same => n,Set(MVPCC_IVR_TARGET_QUEUE_CODE=)
 same => n,Set(MVPCC_IVR_TARGET_EXTENSION=)
 same => n,Set(MVPCC_IVR_SESSION_ID=)
 same => n,GotoIf($["${MVPCC_IVR_FASTAGI_HOSTPORT}" = ""]?ivr-human-fallback,1)
 same => n,AGI(agi://${MVPCC_IVR_FASTAGI_HOSTPORT}/ivr,${MVPCC_CALL_ID},${MVPCC_LINKED_ID},${MVPCC_QUEUE},${MVPCC_IVR_FALLBACK_QUEUE_CODE},${EXTEN})
 same => n,GotoIf($["${MVPCC_IVR_TARGET_EXTENSION}" = ""]?ivr-human-fallback,1)
 same => n,Goto(mvpcc-transfer,${MVPCC_IVR_TARGET_EXTENSION},1)

exten => ivr-human-fallback,1,UserEvent(MVPCCIVRFallbackToHuman,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},QueueCode:${MVPCC_QUEUE},FallbackQueueCode:${MVPCC_IVR_FALLBACK_QUEUE_CODE},Extension:${MVPCC_ENTRY_EXTENSION},Result:${MVPCC_IVR_RESULT},TargetQueueCode:${MVPCC_IVR_TARGET_QUEUE_CODE},TargetExtension:${MVPCC_IVR_TARGET_EXTENSION},Channel:${CHANNEL})
 same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}))
 same => n,Hangup()

exten => h,1,StopMixMonitor()
 same => n,UserEvent(MVPCCCallEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},HangupCause:${HANGUPCAUSE},DurationSeconds:${CDR(duration)})
 same => n,UserEvent(MVPCCRecordingReady,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},RemotePath:${MONITOR_FILENAME},FileName:${MVPCC_RECORD_FILE},MimeType:audio/wav,DurationSeconds:${CDR(duration)})

[mvpcc-call-init]
exten => s,1,Set(MVPCC_ENTRY_EXTENSION=${ARG1})
 same => n,Set(MVPCC_CALL_ID=${UNIQUEID})
 same => n,Set(MVPCC_LINKED_ID=${CHANNEL(linkedid)})
 same => n,Set(MVPCC_RECORD_FILE=${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${UNIQUEID}.wav)
 same => n,Set(MONITOR_FILENAME=${MVPCC_MONITOR_DIR}/${MVPCC_RECORD_FILE})
 same => n,UserEvent(MVPCCCallStarted,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},CallerNumber:${CALLERID(num)},CallerName:${CALLERID(name)},QueueCode:${MVPCC_QUEUE},Extension:${MVPCC_ENTRY_EXTENSION},Context:${CONTEXT},Direction:inbound,Channel:${CHANNEL})
 same => n,MixMonitor(${MONITOR_FILENAME},a)
 same => n,Return()

[mvpcc-connected]
exten => s,1,Set(MVPCC_OPERATOR=${IF($["${ARG3}" != ""]?${ARG3}:${CHANNEL(peername)})})
 same => n,Set(MVPCC_OPERATOR=${IF($["${MVPCC_OPERATOR}" = ""]?${EXTEN}:${MVPCC_OPERATOR})})
 same => n,UserEvent(MVPCCOperatorConnected,CallID:${ARG1},LinkedID:${ARG2},OperatorExtension:${MVPCC_OPERATOR},Context:${CONTEXT},Channel:${CHANNEL},ClaimedByUser:${MVPCC_OPERATOR})
 same => n,Return()

[mvpcc-claim]
exten => _X.,1,NoOp(Claimed call routed to operator ${EXTEN})
 same => n,Set(MVPCC_CALL_ID=${CHANNEL(linkedid)})
 same => n,Set(MVPCC_LINKED_ID=${CHANNEL(linkedid)})
 same => n,Dial(PJSIP/${EXTEN},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}^${EXTEN}))
 same => n,Hangup()

[mvpcc-transfer]
exten => 7000,1,NoOp(Queue transfer target ${EXTEN})
 same => n,Dial(${MVPCC_HUMAN_DIAL_TARGETS},40)
 same => n,Hangup()

exten => 7100,1,NoOp(Queue transfer target ${EXTEN})
 same => n,Goto(from-softphones,7100,1)

exten => 7200,1,NoOp(Queue transfer target ${EXTEN})
 same => n,Goto(from-softphones,7200,1)

exten => _X.,1,NoOp(Blind transfer target ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN},40,U(mvpcc-connected^${MVPCC_CALL_ID}^${MVPCC_LINKED_ID}^${EXTEN}))
 same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?transfer-finished,1)
 same => n,GotoIf($["${MVPCC_IVR_FALLBACK_QUEUE_CODE}" = ""]?transfer-finished,1)
 same => n,Set(MVPCC_IVR_RESULT=fallback_transfer_unavailable)
 same => n,Goto(from-softphones,ivr-human-fallback,1)

exten => transfer-finished,1,Hangup()

exten => h,1,StopMixMonitor()
 same => n,UserEvent(MVPCCCallEnded,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},HangupCause:${HANGUPCAUSE},DurationSeconds:${CDR(duration)})
 same => n,UserEvent(MVPCCRecordingReady,CallID:${MVPCC_CALL_ID},LinkedID:${MVPCC_LINKED_ID},RemotePath:${MONITOR_FILENAME},FileName:${MVPCC_RECORD_FILE},MimeType:audio/wav,DurationSeconds:${CDR(duration)})

[mvpcc-operators]
exten => _X.,1,Dial(PJSIP/${EXTEN},30)
 same => n,Hangup()

[from-telecom]
; Incoming calls from Telecom.kz SIP account 86205414 / DID 87273901485.
; Route them directly to the AI-first entrypoint 7100.
exten => 86205414,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI)
 same => n,Set(CALLERID(dnid)=87273901485)
 same => n,Goto(from-softphones,7100,1)

exten => 87273901485,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI)
 same => n,Goto(from-softphones,7100,1)

exten => _X.,1,NoOp(Incoming from telecom.kz, DID=${EXTEN})
 same => n,Goto(from-softphones,7100,1)
