
    -ǚj=                        d Z ddlmZ ddlZddlmZmZ ddlmZmZm	Z	 ddl
mZ ddlmZ  ej                   e      Z ed      Z ed	      Z ed
      Zddd	 	 	 	 	 	 	 	 	 	 	 ddZddZy)u  체크포인트 → DB 도메인 Service 오케스트레이터.

기존 `steps.py:_sync_checkpoints_to_db`의 5-way 호출 순서를 보존.
단일 commit로 원자성 유지. 단일 now timestamp로 Service간 일관성 보장.

예외 처리 계약 (Claude Phase 2 M7):
- 이 함수는 try/except를 내부에 두지 않음 — 예외는 호출자에게 전파.
- baseline `_sync_checkpoints_to_db`와 동일 (steps.py/image_steps.py에서
  rollback 책임을 담당).

W4 P3-2: `step_id` 주입 시 sync_status를 step_run 테이블에 기록.
- 성공: sync_status='synced' (주 세션, db.commit과 함께)
- 실패: sync_status='failed' + sync_error (별도 세션 — 호출자 rollback과 독립)
    )annotationsN)datetimetimezone)DictIterableOptional)text)SessionzUPDATE step_run SET sync_status = 'synced', sync_error = NULL, synced_at = :now WHERE project_id = :pid AND episode_id = :eid AND step_id = :sidz~UPDATE step_run SET sync_status = 'failed', sync_error = :err WHERE project_id = :pid AND episode_id = :eid AND step_id = :sidzUPDATE step_run SET sync_status = 'synced', sync_error = NULL, synced_at = :now WHERE project_id = :pid AND episode_id = :eid AND step_id = :sid AND sync_status = 'failed' AND (sync_error = :prev_err OR (sync_error IS NULL AND :prev_err IS NULL)))step_idrepair_step_idsc          
        ddl m} ddlm} ddlm} ddlm} ddlm	}	 ddl
m}
 t        j                  t        j                        j!                         }i }	  ||| ||      j#                         |d	<    ||| ||      j#                         |d
<    ||| ||      j#                         |d<    ||| ||      j#                         |d<    |	|| ||      j#                         |d<    |
|| ||      j#                         |d<   |r|j+                  t,        | |||d       d}|rG|j/                         D ]4  \  }}|j+                  t0        | ||||d      }||j2                  xs dz  }6 |j5                          t6        j9                  d|dd |xs d|       |S # t$        $ r}|rt'        | ||t)        |              d}~ww xY w)u  5개 도메인 Service를 순차 호출 후 단일 commit.

    순서:
        1. EntitySyncService   (C/L/P canon + link)
        2. RelationSyncService (visual_variant)
        3. SceneStillSyncService (scene_still + scene_summary + shot_type)
        4. OutlookSyncService  (outlook canon + character_outlook link)
        5. ShelfSyncService    (저빈도 보류 canon + shelved link)
        6. EpisodeProjectionService (episode.status='analyzed' + appearance_count)

    Args:
        step_id: W4 P3-2 — 제공되면 step_run.sync_status를 갱신.
                 Post-step sync에서 전달 (per-step 관측성).
                 Pre-sync / snapshot restore / global sync에선 None.
        repair_step_ids: W4 P3-3 — {step_id: original_sync_error} 형태.
                 sync 성공 시 동일 트랜잭션 내에서 각 step의 sync_status를 'failed'→'synced'
                 로 전환. sync_error가 우리가 캡처한 original_sync_error와 일치할 때만
                 전환 — concurrent 프로세스가 같은 step을 다른 이유로 failed 재기록한
                 경우를 덮어쓰지 않기 위함 (Codex P3-3 High).

    Returns: 각 Service 결과 dict {"entity": {...}, "relation": {...}, ...}
    r   )EntitySyncService)RelationSyncService)SceneStillSyncService)OutlookSyncService)ShelfSyncService)EpisodeProjectionService)nowentityrelationscene_stilloutlookshelfepisodeN)pideidsidr   )r   r   r   r   prev_errzZorchestrate_full_sync: all changes committed atomically (episode %s, step=%s, repaired=%d)   -)0app.services.checkpoint_sync.entity_sync_servicer   2app.services.checkpoint_sync.relation_sync_servicer   5app.services.checkpoint_sync.scene_still_sync_servicer   1app.services.checkpoint_sync.outlook_sync_servicer   /app.services.checkpoint_sync.shelf_sync_servicer   7app.services.checkpoint_sync.episode_projection_servicer   r   r   r   utc	isoformatsync_from_checkpoint	Exception_record_sync_failurestrexecute_SYNC_SUCCESS_UPDATEitems_SYNC_REPAIR_GUARDEDrowcountcommitloggerinfo)
project_id
episode_iddbr   r   r   r   r   r   r   r   r   resultexcrepaired_countr   r   ress                     _/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/checkpoint_sync/orchestrator.pyorchestrate_full_syncr=   4   s   < SV[TP` ,,x||
$
.
.
0C(*F,RZSQffhx0ZQTUjjlz 5b*jVY Z o o q}.r:zsShhjy +2z:3Oddfw4RZUXYnnpy 


'j*
 	 N,224MC**1!*S4 C cll/a/N 5 IIK
KK	-2A3
 M;   Z#c(K	s   B.F   	G)GGc           	     *   	 ddl m}  |       }	 |j                  t        | |||dd d       |j	                          |j                          y# |j                          w xY w# t        $ r%}t        j                  d||dd |       Y d}~yd}~ww xY w)u  주 세션과 독립된 세션에서 sync_status='failed' + sync_error를 UPDATE + commit.

    호출자가 주 세션을 rollback해도 실패 기록은 보존됨.
    synced_at은 건드리지 않음 (직전 성공 sync 시각 보존).
    SessionLocal은 orchestrator 임포트 시점에 바인딩된 전역 engine을 사용.
    recorder 자체가 실패하면 로깅만 하고 propagate하지 않음 — 주 실패 예외를 덮지 않기 위함.
    r   )SessionLocalNi  )r   r   r   errz8Failed to record sync_status=failed for step %s (%s): %sr   )	app.core.databaser?   r-   _SYNC_FAILURE_UPDATEr2   closer*   r3   warning)r5   r6   r   	error_msgr?   db2rec_excs          r<   r+   r+      s    
2n	KK,!*W $'/  JJLIIKCIIK 
FZ^W	
 	

s-   A$ .A A$ A!!A$ $	B-BB)r5   r,   r6   r,   r7   
OrmSessionr   zOptional[str]r   z"Optional[Dict[str, Optional[str]]]returnzDict[str, Dict[str, int]])
r5   r,   r6   r,   r   r,   rE   r,   rI   None)__doc__
__future__r   loggingr   r   typingr   r   r   
sqlalchemyr	   sql_textsqlalchemy.ormr
   rH   	getLogger__name__r3   r.   rB   r0   r=   r+        r<   <module>rV      s    #  ' + + ' 0			8	$  G   G   Q  ":>PPP 	P
 P 8P Pf
rU   