o
    ^•ji-  ã                   @   sž  U d dl m Z mZ d dlmZmZ d dlZd dlmZ i Zee	ee	ef f e
d< d dlZi Zee	ejf e
d< de	ddfd	d
„Zd@de	de	deddfdd„Zi Zee	ee	ef f e
d< 								dAde	de	dededB de	dB de	dB de	de	dB de	dB de	de	dB dee	ef fdd„Zde	ddfdd„ZdBde	dB dee	ee	ef f fd d!„ZdBde	d"e	d#e	dB ddfd$d%„Z	&dCd'e	dede	dee	ef fd(d)„ZdBd'e	dedB ddfd*d+„Zd'e	dee	ef dB fd,d-„Zdee	ee	ef f fd.d/„Zd'e	d"e	defd0d1„Zdee	ee	ef f fd2d3„Zdeddfd4d5„Zdeddfd6d7„ZdBd8ed9e dB ddfd:d;„Z!de	d8eddfd<d=„Z"d'e	ddfd>d?„Z#dS )Dé    )ÚdatetimeÚtimezone)ÚDictÚAnyN)Ú	WebSocketÚactive_sessionsÚgrace_period_timersÚ
session_idÚreturnc                 C   s,   t  | d ¡}|r| ¡ s| ¡  d S d S d S ©N)r   ÚpopÚdoneÚcancel)r	   Ú
timer_task© r   ú6/web/html/dev/chatbot/backend/app/services/presence.pyÚcancel_grace_period_timer   s   ÿr   ç      >@Úsite_idÚgrace_secondsc                 ƒ   s2   �t ˆƒ ‡ ‡‡fdd„}t |ƒ ¡}|tˆ< d S )Nc               
   “   s^  �z¦z‰t  ˆ ¡I d H  ˆtv r'dtˆ d< dtˆ d< d tˆ d< d tˆ d< ddlm}  | ƒ }|d urydd	lm}m} t 	t
j¡}||ˆdƒI d H  ||ˆdƒI d H  |d
  dˆidd d dœi¡I d H  |d  dˆiddd |dœi¡I d H  tdˆˆdddœƒI d H  tˆƒ W n
 t jy•   Y n
w W t ˆd ¡ d S W t ˆd ¡ d S t ˆd ¡ w )NÚofflineÚconnectionStatusÚ	ABANDONEDÚstatusÚassignedAgentIdÚ
assignedAtr   )Úget_database)Ú update_session_connection_statusÚupdate_session_statusÚsessionsÚ	sessionIdz$set)r   r   Úconversations)r   r   Ú	updatedAtÚvisitor_timeout)Útyper    ÚsiteIdr   r   )ÚasyncioÚsleepr   Úapp.databaser   Ú#app.repositories.session_repositoryr   r   r   Únowr   ÚutcÚ
update_oneÚbroadcast_to_agentsÚremove_active_sessionÚCancelledErrorr   r   )r   Údbr   r   r*   ©r   r	   r   r   r   Ú_timer_coro   sL   €
þ
þûÿÿþz-start_grace_period_timer.<locals>._timer_coro)r   r&   Úcreate_taskr   )r	   r   r   r2   Útaskr   r1   r   Ústart_grace_period_timer   s
   €'r5   Úconnected_agentsÚRINGINGÚonlineÚ	websocketÚ	user_dataÚ
ip_addressÚcountryÚ
status_valÚassigned_agent_idÚassigned_atÚconnection_statusÚdisconnected_atc                 C   s>   t  tj¡ ¡ }| |||	|
|||pd||||dœ}|t| < |S )NÚUnknown)r    r%   r   r   ÚdisconnectedAtÚuserÚ	ipAddressr<   ÚconnectedAtr9   r   r   )r   r*   r   r+   Ú	isoformatr   )r	   r   r9   r:   r;   r<   r=   r>   r?   r@   rA   Únow_isoÚsession_datar   r   r   Úadd_active_sessionF   s    ôrJ   c                 C   s   t | ƒ | tv rt| = d S d S r   )r   r   )r	   r   r   r   r.   e   s   
ÿr.   c                    s0   dd„ t  ¡ D ƒ}ˆ r‡ fdd„| ¡ D ƒS |S )Nc                 S   s$   i | ]\}}|  d ¡dvr||“qS )r   )ÚENDEDr   ©Úget©Ú.0ÚsidÚdatar   r   r   Ú
<dictcomp>k   ó   $ z'get_active_sessions.<locals>.<dictcomp>c                    s$   i | ]\}}|  d ¡ˆ kr||“qS )r%   rL   rN   ©r   r   r   rR   m   rS   )r   Úitems)r   Úactiver   rT   r   Úget_active_sessionsj   s   rW   Ú
new_statusÚended_byc                 C   s0   | t v r|t |  d< |r|t |  d< d S d S d S )Nr   ÚendedBy)r   )r	   rX   rY   r   r   r   Úupdate_active_session_statusp   s   ýr[   Ú	AVAILABLEÚagent_idc                 C   s>   | sd} t  tj¡ ¡ }| ||dv r|nd|dœ}|t| < |S )z—
    Registers an agent WebSocket connection in the multi-agent presence pool.
    Safely replaces stale connections if the same agent reconnects.
    Úagent_1©r\   ÚBUSYÚOFFLINEr\   )ÚagentIdr9   r   rF   )r   r*   r   r+   rG   r6   )r]   r9   r=   rH   Ú
agent_infor   r   r   Úadd_agent_connectiony   s   	ürd   c                 C   s8   | t v r|durt |   d¡}||krdS t | = dS dS )z¡
    Removes an agent connection. If a websocket is provided, only removes if it matches
    the currently registered active socket for that agent identity.
    Nr9   ©r6   rM   )r]   r9   Ú	active_wsr   r   r   Úremove_agent_connection�   s   
ûrg   c                 C   s
   t  | ¡S )z8Returns presence info for a specific connected agent ID.re   ©r]   r   r   r   Úget_agent_connectionœ   s   
ri   c                   C   s   t S )z1Returns a dict of all currently connected agents.©r6   r   r   r   r   Úget_connected_agents    s   rk   c                 C   s(   |dvrdS | t v r|t |  d< dS dS )zIUpdates presence status (AVAILABLE, BUSY, OFFLINE) for a connected agent.r_   Fr   Trj   )r]   rX   r   r   r   Úset_agent_status¤   s   rl   c                   C   s   dd„ t  ¡ D ƒS )zAReturns all connected agents whose status is currently AVAILABLE.c                 S   s$   i | ]\}}|  d ¡dkr||“qS )r   r\   rL   ©rO   ÚaidrQ   r   r   r   rR   ¯   rS   z(get_available_agents.<locals>.<dictcomp>)r6   rU   r   r   r   r   Úget_available_agents­   s   ro   c                 C   ó   t d| ƒ d S ©Nr^   )rd   ©r9   r   r   r   Úadd_agent_websocket³   ó   rs   c                 C   rp   rq   )rg   rr   r   r   r   Úremove_agent_websocket¶   rt   ru   Ú
event_dataÚtarget_agent_idsc              	   ƒ   s¬   �g }t  | ¡}tt ¡ ƒ}|durt|ƒ‰ ‡ fdd„|D ƒ}|D ]&\}}| d¡}|rGz
| |¡I dH  W q! tyF   | 	||f¡ Y q!w q!|D ]	\}}t
||ƒ qJdS )z€
    Broadcasts a JSON event payload to connected agent dashboards in the pool.
    Optionally filters by target_agent_ids.
    Nc                    s    g | ]\}}|ˆ v r||f‘qS r   r   rm   ©Ú
target_setr   r   Ú
<listcomp>Ç   s     z'broadcast_to_agents.<locals>.<listcomp>r9   )ÚjsonÚdumpsÚlistr6   rU   ÚsetrM   Ú	send_textÚ	ExceptionÚappendrg   )rv   rw   Ú	to_removeÚmessage_strÚagents_to_notifyrn   rQ   Úwsr   rx   r   r-   ¼   s&   €

ÿýÿr-   c              
   Ã   sâ   �t dƒ t d| › �ƒ | tv r`t|   d¡}|rZt dƒ t dƒ t dƒ t dtj|dd	�› �ƒ z| t |¡¡I d
H  W d
S  tyY } zt d|› �ƒ W Y d
}~d
S d
}~ww t dƒ d
S t d| › dtt ¡ ƒ› d�ƒ d
S )zHSends a JSON event payload to a specific visitor's WebSocket connection.z0[VIDEO][TRACE][BACKEND] resolving visitor socketz$[VIDEO][TRACE][BACKEND] sessionId = r9   z3[VIDEO][TRACE][BACKEND] visitor socket found = truez+[VIDEO][TRACE][BACKEND] socket state = openz([VIDEO][TRACE][BACKEND] forwarding eventz,[VIDEO][TRACE][BACKEND] sending to visitor:
é   )ÚindentNz9[VIDEO][TRACE][BACKEND] Error sending to visitor socket: z[[VIDEO][TRACE][BACKEND] visitor socket found = false (websocket is None in active_sessions)z6[VIDEO][TRACE][BACKEND] visitor socket found = false (z not in active_sessions: ú))	Úprintr   rM   r{   r|   r   r€   r}   Úkeys)r	   rv   r…   Úer   r   r   Únotify_visitor_socketÔ   s&   €€ÿ"rŒ   c              
   Ã   st  �|sdS ddl m} i }| durNz | d  |ddœ¡}|2 z3 dH W }| dd¡||d	 < q6 W n tyM } ztd
|› d|› �ƒ W Y d}~nd}~ww tt ¡ ƒD ]\}}| d¡dkrn| d¡|krn| dd¡||< qT| ¡ D ]D\}}	| dur…|| ||d�I dH nd}
|
r·t	|dƒ |tv rždt| d< dt| d< t
|ddddœƒI dH  td||	dddœƒI dH  qsdS )a.  
    Handles agent WebSocket disconnection.
    1. Finds active IN_CHAT sessions assigned to that agent in DB and in memory.
    2. Requeues each IN_CHAT session back to RINGING state atomically via requeue_session(db, sid, agent_id).
    3. Notifies visitor socket and remaining connected agents.
    Nr   )Úrequeue_sessionr   ÚIN_CHAT)r   r   r%   Ú r    z/Error querying sessions for disconnected agent z: r   r   rh   Tr7   r   Ústatus_changedzAYour agent disconnected. We're reconnecting you to another agent.)r$   r   ÚmessageÚvisitor_requeuedÚagent_disconnected)r$   r    r%   r   Úreason)r)   r�   ÚfindrM   r€   r‰   r}   r   rU   r[   rŒ   r-   )r0   r]   r�   Úsessions_to_requeueÚcursorÚdocr‹   rP   Úsdatar   Úrequeued_docr   r   r   Úhandle_agent_disconnecté   sP   €ÿ €ÿ€ 
ýû€îr›   )r   )NNNr7   NNr8   Nr   )r\   )$r   r   Útypingr   r   r{   Úfastapir   r   ÚstrÚ__annotations__r&   r   ÚTaskr   Úfloatr5   r6   ÚdictrJ   r.   rW   r[   rd   rg   ri   rk   Úboolrl   ro   rs   ru   r}   r-   rŒ   r›   r   r   r   r   Ú<module>   s„    /õÿþýüûúùø	÷
öõ

ô( ýÿþý

ü	