"""Scene provenance service — scene_image_service에서 분리된 이력/메타데이터.

W5 F22 Phase B.10 (2026-04-22): scene_image_service.generate_images()의
PNG metadata embedding 로직을 이관.

## 공개 API

| 메서드 | 역할 |
|--------|------|
| `embed_scene_variations_metadata(var_results_list, still_data, still_index_fallback, dep_scene_idx, prev_file)` | N variation PNG에 컨텍스트 metadata embed |
| `compute_and_embed_scene_metadata(var_results_list, still_data, stills, si, scene_paths_by_index, scene_paths_by_index_by_id)` | dep_scene_idx + prev_file 계산 후 embed |
| `embed_fal_angle_metadata(*, file_path, source_image_name, horizontal_angle, vertical_angle, zoom, created_at)` | fal.ai angle-edited PNG에 angle/source metadata embed |

두 메서드는 모두 내부적으로 예외를 포착하여 warning으로 로깅한다 (non-fatal).
embedding 실패가 이미지 생성 파이프라인을 중단시키지 않는다.

향후 확장: GenerationTrace 기록, lineage 필드 빌더 등을 점진 이관 예정.
"""
from __future__ import annotations

import logging
from pathlib import Path
from typing import Any, Dict, List, Union

from sqlalchemy.orm import Session as OrmSession

__all__ = ["SceneProvenanceService"]

logger = logging.getLogger(__name__)


class SceneProvenanceService:
    """씬 이미지 provenance 전담 서비스 (W5 F22 Phase B.10)."""

    def __init__(self, db: OrmSession, project_id: str) -> None:
        self._db = db
        self._project_id = project_id

    def embed_scene_variations_metadata(
        self,
        var_results_list: List[Dict[str, Any]],
        still_data: Dict[str, Any],
        still_index_fallback: int,
        dep_scene_idx: str,
        prev_file: str,
    ) -> None:
        """Embed PNG metadata for each of the N scene variations.

        Each PNG receives: prompt (truncated 2000), scene_description,
        scene_index (still_index with fallback), beat_title, variant_type,
        dependent_scene, previous_scene, model, created_at.
        All exceptions — including `png_metadata` import failure — are
        caught and logged (non-fatal). PNG metadata embedding is strictly
        best-effort and must never break the image pipeline.
        """
        try:
            # Lazy import inside try — contract "import failure is non-fatal"
            # (preserved from the original inline block).
            from app.modules.png_metadata import embed_png_metadata
            for vi, vr in enumerate(var_results_list):
                embed_png_metadata(vr["file_path"], {
                    "prompt": (vr.get("prompt_used") or "")[:2000],
                    "scene_description": still_data.get("still_frame_prompt", ""),
                    "scene_index": str(still_data.get("still_index", still_index_fallback)),
                    "beat_title": still_data.get("beat_title", ""),
                    "variant_type": vr.get("variant_type", f"var_{vi}"),
                    "dependent_scene": dep_scene_idx,
                    "previous_scene": prev_file,
                    "model": vr.get("generation_model", ""),
                    "created_at": vr.get("created_at", ""),
                })
        except Exception as exc:
            logger.warning("PNG metadata embedding failed: %s", exc)

    def compute_and_embed_scene_metadata(
        self,
        var_results_list: List[Dict[str, Any]],
        still_data: Dict[str, Any],
        stills: List[Dict[str, Any]],
        si: int,
        scene_paths_by_index: Dict[int, Path],
        scene_paths_by_index_by_id: Dict[str, Path],
    ) -> None:
        """dep_scene_idx + prev_file을 계산한 뒤 N variation PNG에 embed.

        W5 F22 Phase B.22.8 (2026-04-23): scene_image_service.generate_images의
        PNG 메타데이터 컨텍스트 계산 블록(~16 LOC)을 이관.

        dep_scene_idx:
          still_data.dependent_scene_id가 있으면 stills에서 해당 still을 찾아
          still_index를 문자열로. 없으면 "".

        prev_file:
          1순위: scene_paths_by_index[si].name
          2순위: scene_paths_by_index_by_id[still_data.dependent_scene_id].name
          없으면 "".

        계산 후 `embed_scene_variations_metadata`에 위임.
        """
        dep_scene_idx = ""
        if still_data.get("dependent_scene_id"):
            for _ss in stills:
                if _ss.get("id") == still_data["dependent_scene_id"]:
                    dep_scene_idx = str(_ss.get("still_index", ""))
                    break
        prev_file = ""
        if si in scene_paths_by_index:
            prev_file = scene_paths_by_index[si].name
        elif still_data.get("dependent_scene_id") in scene_paths_by_index_by_id:
            prev_file = scene_paths_by_index_by_id[still_data["dependent_scene_id"]].name

        self.embed_scene_variations_metadata(
            var_results_list, still_data, si, dep_scene_idx, prev_file,
        )

    def embed_fal_angle_metadata(
        self,
        *,
        file_path: Union[str, Path],
        source_image_name: str,
        horizontal_angle: float,
        vertical_angle: float,
        zoom: float,
        created_at: str,
    ) -> None:
        """Embed PNG metadata for a fal.ai angle-edited scene image.

        All exceptions — including `png_metadata` import failure — are
        caught and logged (non-fatal).
        """
        try:
            from app.modules.png_metadata import embed_png_metadata
            embed_png_metadata(file_path, {
                "source_image": source_image_name,
                "angle_horizontal": str(horizontal_angle),
                "angle_vertical": str(vertical_angle),
                "angle_zoom": str(zoom),
                "model": "fal-ai/qwen-image-edit-2511-multiple-angles",
                "created_at": created_at,
            })
        except Exception as exc:
            logger.warning("PNG metadata embedding failed: %s", exc)
