"""W22 야외 직행 체인 공용 헬퍼 — 선택 샷/loc 매핑 (스텝 간 드리프트 방지).

outdoor_place_spec(①)과 outdoor_shot_grounding(③)이 동일한 규칙으로
"그룹 관련 선택 샷"을 골라야 한다 (Codex W1 NARROW_1 계약):
  - shot_selection 기준 선택 샷만 (staging cp 는 생성 시점 선택이라
    토글 해제 잔존 가능 → 현재 선택으로 재필터)
  - 샷 location_id ∈ 그룹 loc / location_id 없으면 scene primary 로 판정
  - 미선택 샷의 location 은 씬 매핑을 만들지 않음
"""

from typing import Any, Dict, List, Optional, Set, Tuple

ShotKey = Tuple[int, int]


def build_selected_keys(
    selection_cp: Optional[Dict[str, Any]],
) -> Optional[Set[ShotKey]]:
    """shot_selection cp → 선택 샷 키 집합. cp 부재/빈 데이터면 None(필터 없음)."""
    sel_scenes = (selection_cp or {}).get("data", {}).get("scenes", []) or []
    if not sel_scenes:
        return None
    keys: Set[ShotKey] = set()
    for sc in sel_scenes:
        si = sc.get("scene_index")
        if si is None:
            continue
        for shi in sc.get("selected_shot_indices", []) or []:
            keys.add((int(si), int(shi)))
    return keys


def build_shot_loc_map(
    validator_cp: Optional[Dict[str, Any]],
) -> Dict[ShotKey, str]:
    """shot_validator cp → (scene_index, shot_index) → location_id ('' 허용)."""
    out: Dict[ShotKey, str] = {}
    for sc in (validator_cp or {}).get("data", {}).get("scenes", []) or []:
        si = sc.get("scene_index")
        if si is None:
            continue
        for sh in sc.get("shots", []) or []:
            shi = sh.get("shot_index")
            if shi is None:
                continue
            out[(int(si), int(shi))] = sh.get("location_id") or ""
    return out


def is_selected(key: ShotKey, selected_keys: Optional[Set[ShotKey]]) -> bool:
    return selected_keys is None or key in selected_keys


def scene_indices_for_locs(
    loc_ids: Set[str],
    scene_primary: Dict[int, str],
    shot_loc_by_key: Dict[ShotKey, str],
    selected_keys: Optional[Set[ShotKey]],
) -> List[int]:
    """loc 집합이 등장하는 scene_index — scene primary + 선택 샷 loc 합집합."""
    out = {si for si, lid in scene_primary.items() if lid in loc_ids}
    for key, lid in shot_loc_by_key.items():
        if lid and lid in loc_ids and is_selected(key, selected_keys):
            out.add(key[0])
    return sorted(out)


def filter_group_shots(
    staging_shots: List[Dict[str, Any]],
    *,
    scene_indices: List[int],
    loc_ids: Set[str],
    scene_primary: Dict[int, str],
    shot_loc_by_key: Dict[ShotKey, str],
    selected_keys: Optional[Set[ShotKey]],
) -> List[Dict[str, Any]]:
    """그룹 관련 선택 샷만 — staging×validator (si,shi) join.

    location_id 있으면 그룹 loc 일 때만, 없으면 scene primary 가 그룹 loc
    일 때만 포함.
    """
    si_set = set(scene_indices)
    out: List[Dict[str, Any]] = []
    for sh in staging_shots or []:
        sh_si = sh.get("scene_index")
        sh_shi = sh.get("shot_index")
        if sh_si not in si_set or sh_shi is None:
            continue
        key = (int(sh_si), int(sh_shi))
        if not is_selected(key, selected_keys):
            continue
        lid = shot_loc_by_key.get(key, "")
        if lid:
            if lid in loc_ids:
                out.append(sh)
        elif scene_primary.get(key[0]) in loc_ids:
            out.append(sh)
    return out
