"""T2I 프롬프트 변환 모듈 — 씬 설명을 2가지 T2I 프롬프트로 변환."""

import json
import logging
from pathlib import Path
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)

PROMPTS_DIR = (
    Path(__file__).resolve().parent.parent.parent.parent
    / "prompts" / "_base" / "t2i_composer" / "v1"
)

# Structured output schema for OpenAI
_RESPONSE_SCHEMA = {
    "type": "object",
    "properties": {
        "angle_a_description": {
            "type": "string",
            "description": "First angle choice and why (e.g. 'high angle looking down — shows power dynamic')",
        },
        "cinematic": {
            "type": "string",
            "description": "T2I prompt for angle A — 16:9 format, English",
        },
        "angle_b_description": {
            "type": "string",
            "description": "Second angle choice and why (must differ from A)",
        },
        "closeup": {
            "type": "string",
            "description": "T2I prompt for angle B — 16:9 format, English",
        },
        "key_visual_elements": {
            "type": "array",
            "items": {"type": "string"},
            "description": "Key visual elements identified in the scene",
        },
        "removed_narrative_elements": {
            "type": "array",
            "items": {"type": "string"},
            "description": "Non-visual narrative elements that were removed",
        },
    },
    "required": [
        "angle_a_description",
        "cinematic",
        "angle_b_description",
        "closeup",
        "key_visual_elements",
        "removed_narrative_elements",
    ],
    "additionalProperties": False,
}


def _load_prompt(filename: str) -> str:
    """Load a prompt template file."""
    path = PROMPTS_DIR / filename
    if not path.exists():
        raise FileNotFoundError(f"Prompt file not found: {path}")
    return path.read_text(encoding="utf-8").strip()


def _format_entity_traits(
    visible_entities: List[Dict[str, Any]],
    entity_visual_traits: Dict[str, Any],
) -> str:
    """Format entity visual traits for the prompt."""
    if not visible_entities:
        return "없음"
    lines = []
    for entity in visible_entities:
        eid = entity.get("id", "")
        name = entity.get("name", "unknown")
        etype = entity.get("entity_type", "unknown")
        traits = entity_visual_traits.get(eid, {})
        if isinstance(traits, str):
            try:
                traits = json.loads(traits)
            except json.JSONDecodeError:
                traits = {}
        trait_str = json.dumps(traits, ensure_ascii=False) if traits else "없음"
        lines.append(f"- {name} ({etype}): {trait_str}")
    return "\n".join(lines) if lines else "없음"


def _format_json_block(data: Any) -> str:
    """Format a JSON-serializable object as a readable string."""
    if isinstance(data, str):
        try:
            data = json.loads(data)
        except json.JSONDecodeError:
            return data
    if not data or data == {}:
        return "없음"
    return json.dumps(data, ensure_ascii=False, indent=2)


class T2IPromptComposer:
    """씬 내용 + 엔티티 시각 특성 -> 2가지 T2I 프롬프트 생성.

    A) cinematic: 전체 구도, 카메라 각도/렌즈/조명 포함
    B) closeup: 인물 감정 중심, 클로즈업/미디엄
    """

    def __init__(
        self,
        llm_client: Any,
        prompt_version: str = "v1",
        system_prompt_override: Optional[str] = None,
        user_prompt_override: Optional[str] = None,
    ) -> None:
        self._llm = llm_client
        self._version = prompt_version
        self._system_override = system_prompt_override
        self._user_override = user_prompt_override

    def _get_system_prompt(self) -> str:
        if self._system_override:
            return self._system_override
        return _load_prompt("system.md")

    def _get_user_template(self) -> str:
        if self._user_override:
            return self._user_override
        return _load_prompt("user.md")

    def compose(
        self,
        scene_description: str,
        camera_json: Any,
        lighting_json: Any,
        visible_entities: List[Dict[str, Any]],
        entity_visual_traits: Dict[str, Any],
        world_guide: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """Transform a scene description into two T2I prompts.

        Returns:
            {
                "cinematic": "English T2I prompt for wide shot...",
                "closeup": "English T2I prompt for close-up...",
                "composer_version": "v1",
                "key_visual_elements": [...],
                "removed_narrative_elements": [...],
            }
        """
        system_prompt = self._get_system_prompt()
        user_template = self._get_user_template()

        camera_info = _format_json_block(camera_json)
        lighting_info = _format_json_block(lighting_json)
        entity_traits = _format_entity_traits(visible_entities, entity_visual_traits)
        world_context = ""
        if world_guide:
            world_context = world_guide.get("world_setting_summary", "")
            if not world_context:
                world_context = _format_json_block(world_guide)

        user_prompt = user_template.format(
            scene_description=scene_description,
            camera_info=camera_info,
            lighting_info=lighting_info,
            entity_traits=entity_traits,
            world_context=world_context or "없음",
        )

        result = self._llm.generate_structured(
            system_prompt=system_prompt,
            user_prompt=user_prompt,
            response_schema=_RESPONSE_SCHEMA,
            schema_name="t2i_composer_response",
            max_tokens=4000,
        )

        return {
            "cinematic": result.get("cinematic", ""),
            "closeup": result.get("closeup", ""),
            "composer_version": self._version,
            "key_visual_elements": result.get("key_visual_elements", []),
            "removed_narrative_elements": result.get("removed_narrative_elements", []),
        }
