"""ReferencePipelineContext — Phase 1/2/3 간 공유 상태.

F24.4.2 (2026-04-24): `ReferencePipelineOrchestrator.run()`이 setup 후 ctx를 구성해
Phase별 서비스(Phase1/2/3)에 전달한다. 각 Phase 서비스가 counters 및
`ref_image_map` 등 mutable 상태를 in-place로 업데이트한다.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Set


@dataclass
class ReferencePipelineContext:
    """Phase 1/2/3 간 공유되는 runtime 상태."""

    # ── inputs (orchestrator setup에서 확정) ──
    episode_id: str
    episode: Any  # app.models.project.Episode
    mode: str
    skip_composite: bool
    ip: Optional[str]
    entities: List[Dict[str, Any]]
    entity_by_id: Dict[str, Dict[str, Any]]
    deps: Dict[str, Set[str]]
    batches: List[List[str]]
    reference_dir: Path
    gemini_client: Any  # GeminiImageClient
    max_concurrent: int
    progress: Any  # ProgressTracker
    ref_cp: Any  # ImageCheckpointManager

    # ── mutable shared state ──
    ref_image_map: Dict[str, bytes] = field(default_factory=dict)
    already_done: Set[str] = field(default_factory=set)
    low_freq_skip_ids: Set[str] = field(default_factory=set)
    #: final_id(short_id) → 사람이 확인한 조사 사진들(bytes 포함). ★정책 갈래(C/P)만.
    #: `grounding_canonical_ref_inputs.verified_policy_references` 가 채운다.
    grounding_references: Dict[str, List[Dict[str, Any]]] = field(default_factory=dict)
    o00_char_ids: Set[str] = field(default_factory=set)
    # ★몸이 곧 신원인 인물(로봇·사이보그 등)의 **entity id**. O00 과 다른 축 —
    #  이 인물들은 아웃룩을 입어도 남고, 기본 참조가 **전신**이어야 한다.
    body_identity_char_ids: Set[str] = field(default_factory=set)

    # ── counters (Phase 1/2/3가 갱신) ──
    generated_count: int = 0
    failed_count: int = 0
    low_freq_skipped: int = 0
    skipped_count: int = 0
    outlook_generated: int = 0
    outlook_skipped: int = 0
    composite_generated: int = 0
    composite_skipped: int = 0

    @property
    def total_to_gen(self) -> int:
        return len(self.entities) - self.skipped_count
