
    Si9              	         U d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	m
Z
  e
e      Z eej                  j                  dd            Z	 dd	lmZ  ej&                         Zd
Z	 ddlmZ  e       Zded<   d
Zd#dZd$dZg dZded<   h dZ ded<   dddddddddZ!ded <    G d! d"      Z"y# e$ r dZdZY Uw xY w# e$ r i ZdZY Ow xY w)%uD  BotStatusManager — 봇 상태 관리 단일 소스 모듈.

dispatch.py, whisper-compile.py 에 분산되어 있던 봇 상태 관련 로직을 하나로 통합.

Usage:
    from utils.bot_status import BotStatusManager

    mgr = BotStatusManager()
    busy  = mgr.get_busy_bots()
    idle  = mgr.get_idle_bots()
    avail = mgr.is_bot_available("bot-e")
    status = mgr.get_team_status("dev4-team")
    occ   = mgr.get_bot_occupation()
    team  = mgr.suggest_team("배너 이미지 디자인 작업")
    warn  = mgr.validate_routing("dev1-team", "카피 마케팅 전략")
    )annotationsN)Path)Optional)
get_loggerWORKSPACE_ROOTz/home/jay/workspace)ConfigManagerTF)build_team_to_bot_id_mapdict[str, str]_TEAM_TO_BOT_IDc                 
   t         dz  dz  } | j                         si S 	 t        j                  | j	                  d            S # t        j
                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)u+   config/constants.json 파일 직접 읽기.configconstants.jsonutf-8encoding   constants.json 읽기 실패: N)		WORKSPACEexistsjsonloads	read_textJSONDecodeErrorOSErrorloggerwarning)pathexcs     A/home/jay/workspace/.worktrees/task-2117-dev1/utils/bot_status.py_load_constants_jsonr   <   ss    x"22D;;=	zz$..'.:;;  '* 7u=>	s   $A BA=7B=Bc            	        t         rt        rt        S t        r*t        r$	 t        j	                  d      } | rt        |       S 	 t               }t        |j                  di             xs dddddddd	d
S # t        $ r Y =w xY w)uM   팀→봇 매핑을 반환. org_loader 우선, fallback으로 constants.json.team_to_botbot-bbot-cbot-dbot-ebot-fbot-gbot-hbot-iz	dev1-teamz	dev2-teamz	dev3-teamz	dev4-teamz	dev5-teamz	dev6-teamz	dev7-teamz	dev8-team)	_ORG_LOADER_AVAILABLEr   _CONFIG_AVAILABLE_cfgget_constantdict	Exceptionr   get)result	constantss     r   _resolve_team_to_bot_idr4   H   s    T	&&}5FF|# 
 %&I	mR01 		6 		  		s   !A5 5	B B)r"   r#   r$   r%   r&   r'   r(   r)   	list[str]ALL_BOTS>   designcontent	marketing
consulting
publishingzset[str]DYNAMIC_BOT_TEAMSr"   r#   r$   r%   r&   r'   r(   r)   r*   _FALLBACK_TEAM_TO_BOTc                      e Zd ZdZ	 ddd	 	 	 	 	 ddZddZddZddZddZdd	Z	dd
Z
dddZddZddZddZddZddZ	 d	 	 	 	 	 	 	 ddZy)BotStatusManageru   모든 봇의 현재 상태를 관리하는 단일 소스.

    task-timers.json 및 config/constants.json을 기반으로 봇 점유/유휴 상태,
    팀 라우팅 추천, 라우팅 검증 기능을 제공합니다.
    N)task_timersc                   |xs t         | _        | j                  dz  dz  | _        | j                  dz  dz  | _        || _        y )Nmemoryztask-timers.jsonr   r   )r   
_workspace_timer_file_constants_file_injected_task_timers)selfworkspace_rootr@   s      r   __init__zBotStatusManager.__init__   sF     )5I!%8!;>P!P%)__x%?BR%R5@"    c                   | j                   j                         si S 	 t        j                  | j                   j	                  d            S # t        j
                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)uB   인스턴스 workspace 기준 constants.json 파일 직접 읽기.r   r   r   N)	rE   r   r   r   r   r   r   r   r   )rG   r   s     r   _load_constantsz BotStatusManager._load_constants   su    ##**,I	::d22<<g<NOO$$g. 	NN;C5ABI	s   .A B
&B?B
B
c                    | j                   t        k7  r3| j                         }t        |j	                  di             xs t
        S t               S )u6   인스턴스 workspace 기준 팀→봇 매핑 반환.r!   )rC   r   rL   r/   r1   r=   r4   )rG   r3   s     r   r4   z(BotStatusManager._resolve_team_to_bot_id   sC     ??i',,.I	mR89R=RR&((rJ   c           	         | j                         }|j                         D ci c]  \  }}||j                  dd       c}}S c c}}w )u8   인스턴스 workspace 기준 봇→dev short id 매핑.-team )r4   itemsreplace)rG   r!   tidbots       r   _resolve_bot_to_devz$BotStatusManager._resolve_bot_to_dev   sA    224>I>O>O>QR(#sS[["--RRRs   Ac                l    | j                         }|D cg c]  }|j                  dd       c}S c c}w )u2   인스턴스 workspace 기준 dev short id 목록.rO   rP   )r4   rR   )rG   r!   rS   s      r   _resolve_dev_short_idsz'BotStatusManager._resolve_dev_short_ids   s/    2244?@SGR(@@@s   1c                n   | j                   | j                   S | j                  j                         si S 	 t        j                  | j                  j                  d            }|j                  di       S # t        j                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)u   task-timers.json의 tasks 섹션 반환. 실패 시 빈 dict.

        주입된 task_timers가 있으면 파일 읽기 없이 그것을 반환.
        Nr   r   tasksu    task-timers.json 읽기 실패: )rF   rD   r   r   r   r   r1   r   r   r   r   )rG   datar   s      r   _load_task_timersz"BotStatusManager._load_task_timers   s    
 %%1---&&(I	::d..88'8JKD88GR(($$g. 	NN=cUCDI	s   A A7 7B4B/)B4/B4c                    t         r*t        r$	 t        j                  d      }|rt        |      S 	 | j                         }|j                  di       S # t        $ r Y -w xY w)u   config/constants.json의 logical_teams 섹션 반환.

        ConfigManager 우선, fallback으로 파일 직접 읽기.
        logical_teams)r,   r-   r.   r/   r0   rL   r1   )rG   r2   r3   s      r   _load_logical_teamsz$BotStatusManager._load_logical_teams   se    
 **?;<' 
 ((*	}}_b11	  s   !A 	AAc                4   | j                         }| j                         }i }|j                         D ]b  \  }}|j                  d      dk7  r|r||k(  r#|j                  dd      }||v r||   }||d||<   |j                  dd      }	|	s[||d||	<   d |S )u  running 상태 태스크에서 점유된 봇 정보 반환.

        dev팀 running task → TEAM_TO_BOT_ID 매핑으로 봇 추출.
        논리적 팀(design 등) running task → bot 필드 직접 사용.

        Args:
            exclude_task_id: 결과에서 제외할 task_id (자기 자신 제외용).

        Returns:
            {"bot-b": {"task_id": "...", "team_id": "..."}, ...}
        statusrunningteam_idrP   )task_idrb   rT   )r4   r[   rQ   r1   )
rG   exclude_task_idr!   rY   busyrc   entryrb   rT   	bot_fields
             r   get_busy_botszBotStatusManager.get_busy_bots   s     224&&(*,#kkm 	KNGUyy"i/7o#= 99Y3G +%!'*(/GDS	 #YYub1I.5'"JY!	K$ rJ   c                    t        | j                         j                               }t        D cg c]	  }||vs| c}S c c}w )u   전체 봇 목록에서 busy 봇을 제외한 유휴 봇 ID 목록 반환.

        Returns:
            ["bot-c", "bot-f", ...] (우선순위 순서 유지)
        )setrh   keysr6   )rG   re   rT   s      r   get_idle_botszBotStatusManager.get_idle_bots   s:     4%%',,./';3d?;;;s
   	AAc                &    || j                         vS )u   특정 봇이 현재 유휴 상태인지 확인.

        Args:
            bot_id: 확인할 봇 ID (예: "bot-e").

        Returns:
            True이면 유휴, False이면 점유 중.
        )rh   )rG   bot_ids     r   is_bot_availablez!BotStatusManager.is_bot_available  s     T//111rJ   c                ^   | j                         }|j                         D ]-  }|j                  d      dk(  s|j                  d      |k(  s- y | j                         }|j                  |      }|r:| j	                         }|j                  dd      }||v r||   }d|d    d	|d
    dS y)ud  dev팀의 현재 상태 문자열 반환.

        - running task 있음 → "작업중"
        - 봇이 논리적 팀에 점유됨 → "봇점유({team}:{task_id})"
        - 그 외 → "유휴"

        Args:
            team_id: dev팀 ID (예: "dev4-team").

        Returns:
            "작업중" | "봇점유(design:task-123)" | "유휴"
        r`   ra   rb   u	   작업중rO   rP   u
   봇점유(team:rc   )u   유휴)r[   valuesr1   r4   get_bot_occupationrR   )	rG   rb   rY   rf   r!   rn   
occupation	dev_shortocc_infos	            r   get_team_statusz BotStatusManager.get_team_status  s     &&( \\^ 	#Eyy"i/EIIi4HG4S"	#
 224)002J4IJ&%i0#HV$4#5Qx	7J6K1MMrJ   c                   | j                         }t        | j                               }| j                         }i }|j	                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|j                  dd      }|sB|j                  dd      }	|	|v rY|j                  |      }
|
sm||j                  d|      |d||
<    |S )	u  논리적 팀이 물리 봇을 점유하는 경우를 탐지.

        dev팀 자체 작업은 제외하고, marketing/design/content 등 논리적 팀이
        dev봇을 빌려 사용하는 경우만 반환합니다.

        Returns:
            {dev_short_id: {"team": team_id, "task_id": task_id, "bot_id": bot_id}}
            예: {"dev4": {"team": "design", "task_id": "task-99", "bot_id": "bot-e"}}
        r`   ra   rb   rP   rT   rO   rc   )rq   rc   rn   )rU   rj   rW   r[   rQ   r1   rR   )rG   
bot_to_devdev_short_idsrY   rv   rc   rf   rb   rn   
team_short	dev_owners              r   ru   z#BotStatusManager.get_bot_occupation3  s     --/
D779:&&(02
#kkm 	NGUyy"i/ 99Y3G))E2.F !"5J]* #v.I#$yyG<$)
9%#	. rJ   c                .   | j                         }|syd}d}|j                         D ]b  \  }}|dk(  r|j                  dg       }|j                  dg       }t        fd|D              rEt	        fd|D              }	|	|kD  s_|	}|}d |dkD  r|S dS )uv  작업 설명에서 키워드 매칭으로 적합한 논리적 팀 추천.

        constants.json의 logical_teams 키워드를 사용하며 anti_keywords가
        포함된 팀은 후보에서 제외합니다.

        Args:
            task_desc: 작업 설명 문자열.

        Returns:
            추천 팀 ID (예: "design") 또는 None (매칭 없음).
        Nr   	compositekeywordsanti_keywordsc              3  &   K   | ]  }|v  
 y wN ).0ak	task_descs     r   	<genexpr>z0BotStatusManager.suggest_team.<locals>.<genexpr>v  s     ;r2?;s   c              3  ,   K   | ]  }|v sd   yw)   Nr   )r   kwr   s     r   r   z0BotStatusManager.suggest_team.<locals>.<genexpr>z  s     @bi@s   	)r^   rQ   r1   anysum)
rG   r   r]   	best_team
best_scorerb   r   r   r   scores
    `        r   suggest_teamzBotStatusManager.suggest_team[  s     002#'	
,224 	$OGV+%#ZZ
B7H"(**_b"AM ;];; @H@@Ez!"
#	!	$$ 'Ny44rJ   c                    |t         v ry| j                  |      }|y|rt        j                  d| d| d       y| j	                         }|j                  |i       j                  dd      }d| d| d	S )
u  dev팀에 논리적 팀 소관 작업이 위임될 때 경고 메시지 반환.

        논리적 팀(design, marketing 등)에 직접 위임된 경우는 검증 생략.

        Args:
            team_id: 위임 대상 팀 ID (예: "dev1-team").
            task_desc: 작업 설명 문자열.
            override_routing: True이면 경고를 무시하고 None 반환.

        Returns:
            경고 메시지 문자열 또는 None (문제 없음).
        Nz[routing-override] u   에 u-    소관 작업 위임 (override_routing=True)descriptionrP   u   이 작업은 --team u   이 적합합니다 (u<   ). 계속하려면 override_routing=True를 사용하세요.)r<   r   r   r   r^   r1   )rG   rb   r   override_routing	suggestedr]   r   s          r   validate_routingz!BotStatusManager.validate_routing  s    & ''%%i0	NN0	i[Huvw002#''	26::="M#I;.CK= QH I	
rJ   r   )rH   zOptional[Path]r@   zOptional[dict]returnNoner   r/   r   r
   )r   r5   )rd   Optional[str]r   dict[str, dict[str, str]])rn   strr   bool)rb   r   r   r   )r   r   )r   r   r   r   )F)rb   r   r   r   r   r   r   r   )__name__
__module____qualname____doc__rI   rL   r4   rU   rW   r[   r^   rh   rl   ro   ry   ru   r   r   r   rJ   r   r?   r?      s     *.	A '+		A&	A $		A
 
	A)S
A
 2("H<	2@&P%5V "'	#
#
 #
 	#

 
#
rJ   r?   r   r   )#r   
__future__r   r   ospathlibr   typingr   utils.loggerr   r   r   environr1   r   config.loaderr   _ConfigManagerget_instancer-   r,   r0   utils.org_loaderr	   _build_team_to_bot_id_mapr   __annotations__r+   r   r4   r6   r<   r=   r?   r   rJ   r   <module>r      s  " #  	   #	H	  02GHI	
=&>&&(D"V&?&AO^A 	<	) 	 ] 8 \ 	) ~ 	"a
 a
s  D  "O!"s$   B2 )C 2	B>=B>	CC