
    =i                        d Z ddlmZ ddlZddlmZ ddlmZ ddlm	Z	 ddl
mZ ddlmZ dd	lmZ  ej                   e      Z G d
 de      Zy)u5  SceneStillSyncService — scene_detail + shot 체크포인트 → SceneStill UPSERT.

W4 P3-1: thin orchestrator. 실제 로직은 3개 도우미에 위임.
  - SceneStillCheckpointLoader  — 8개 체크포인트 → CheckpointBundle
  - SceneStillNormalizer        — bundle + EntityMaps → list[PlannedStill]
  - SceneStillWriter            — planned + bundle → DB UPSERT + summary/shot_type

외부 계약(sync_from_checkpoint 반환 `{"stills": n}`)은 변경 없음.
UPSERT 정책 (baseline): DELETE → INSERT 금지. 이미지 still_id 참조 보존.
    )annotationsN)Dict)BaseSyncService)
EntityMaps)SceneStillCheckpointLoader)SceneStillNormalizer)SceneStillWriterc                      e Zd ZddZddZy)SceneStillSyncServicec                   t        | j                        j                         }|j                  st        j                  d       ddiS | j                         }t        |      j                  |      }t        | j                  | j                  | j                  | j                        j                  ||      }ddlm} 	  || j                  | j                        }|j#                  | j                        }|rt        j%                  d|       d|iS # t&        $ r#}t        j)                  d|       Y d}~d|iS d}~ww xY w)uF  scene_detail + shot 체크포인트 → SceneStill UPSERT.

        Returns: {"stills": n}

        scene_detail이 sync 가능하지 않은 상태(상류 force cascade로 invalidate되어 빈
        scenes 또는 running/error 상태)에서는 scene_still을 건드리지 않고 일찍 반환한다.
        그렇지 않으면 Normalizer가 빈 planned 리스트를 반환하고 Writer가 "계획에 없는
        모든 row"를 stale로 마킹해 기존 active scene_still 전체가 손상된다
        (validate_episode_ready가 거부).

        M2 Fix 2 (2026-05-01): partial 상태(일부 씬 fail, 나머지 완료)는 sync 허용 —
        loader의 `is_cp_syncable("scenes")`이 partial+데이터있음을 syncable로 판단.
        scene_detail 재완료 시 다음 sync가 정확한 plan key로 정상화한다.
        ul   Skipping scene_still sync: scene_detail 데이터 없음 (cascade or pre-analysis). Existing rows preserved.stillsr   )ScenePersistenceServiceuo   scene_still sync: cleaned %d orphan image_asset row(s) (unexpected — investigate parent still removal source)z7scene_still sync: orphan cleanup failed (non-fatal): %sN)r   _load_cploadsd_completedloggerinfo_load_entity_mapsr   	normalizer	   db
project_id
episode_idnowwrite&app.services.scene_persistence_servicer   delete_orphan_scene_assetswarning	Exceptionerror)	selfbundleentity_mapsplannedr   r   spsremovedexcs	            k/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/checkpoint_sync/scene_still_sync_service.pysync_from_checkpointz*SceneStillSyncService.sync_from_checkpoint   s$    ,DMM:??A""KKF a= ,,.&{3==fE!GGT__dootxx

%
  	 	S	)$''4??CC44T__EGO &!!  	LLI3  &!!	s    AD 	E D;;E c                   ddl m} | j                  j                  |      j	                  |j
                  | j
                  k(        j                         }t               }|D ]  }|j                  |j                  |j                  <   |j                  s3|j                  |j                  |j                  <   |j                  |j                  |j                  <   |j                  |j                  |j                  <    |S )u   EntityCanon에서 name↔id↔short_id 매핑 조회.

        Normalizer가 visible_entities 해석 시 사용. sync_from_checkpoint의
        sd_completed 가드 이후에만 호출된다 (가드에서 조기 반환).
        r   )EntityCanon)app.models.projectr*   r   queryfilterr   allr   id
name_to_idnameshort_idname_to_shortshort_to_idshort_to_name)r    r*   rowsmapses        r'   r   z'SceneStillSyncService._load_entity_mapsO   s     	3ww}}[)00""doo5

#% 	 |A&'ddDOOAFF#zz-.ZZ""166*/0tt  ,12""1::.      N)returnzDict[str, int])r:   r   )__name__
__module____qualname__r(   r    r9   r'   r   r      s    0"dr9   r   )__doc__
__future__r   loggingtypingr   "app.services.checkpoint_sync._baser   3app.services.checkpoint_sync._scene_still_contractsr   :app.services.checkpoint_sync.scene_still_checkpoint_loaderr   3app.services.checkpoint_sync.scene_still_normalizerr   /app.services.checkpoint_sync.scene_still_writerr	   	getLoggerr;   r   r   r>   r9   r'   <module>rI      sK   	 #   > J U L 
		8	$EO Er9   