"""아웃룩 병합 — GPT 5.4 기반 중복 아웃룩 병합.

병렬 씬 분석에서 동일 아웃룩이 다른 이름으로 생성될 수 있음.
원본 시나리오 + 아웃룩 목록을 GPT에 보내 병합 그룹을 판단하고 통합.
"""

import json
import logging
from typing import Any, Dict, List

from app.modules.llm.llm_client import call_structured
from app.modules.prompt_loader import load_prompt, load_schema as _loader_schema

logger = logging.getLogger(__name__)

_MODULE = "outlook_merger"


def _load_prompt(name: str, **kwargs) -> str:
    stem = name.replace(".md", "") if name.endswith(".md") else name
    return load_prompt(_MODULE, stem, **kwargs)


def _load_schema(name: str) -> Dict[str, Any]:
    stem = name.replace(".json", "") if name.endswith(".json") else name
    return _loader_schema(_MODULE, stem)


def merge_outlooks(
    outlooks: List[Dict[str, Any]],
    fulltext: str = "",
    project_llm_config: Dict = None,
) -> Dict[str, Any]:
    """GPT 5.4로 중복 아웃룩을 병합.

    Args:
        outlooks: [{"name": str, "description": str, "characters": [str]}]
        fulltext: 원본 시나리오 전문 (맥락 판단용)

    Returns:
        {
            "merge_groups": [{"keep_name": str, "merge_names": [str], "reason": str}],
            "name_map": {old_name: new_name}  # 병합 치환 맵
        }
    """
    if len(outlooks) < 2:
        logger.info("Only %d outlooks — no merge needed", len(outlooks))
        return {"merge_groups": [], "name_map": {}}

    # 아웃룩 목록 텍스트 구성
    outlook_lines = []
    for o in outlooks:
        chars = ", ".join(o.get("characters", []))
        outlook_lines.append(f"- {o['name']}: {o.get('description', '')} (인물: {chars})")
    outlook_list = "\n".join(outlook_lines)

    prompt = _load_prompt("merge_prompt.md", outlook_list=outlook_list, fulltext=fulltext)
    schema = _load_schema("merge_schema.json")

    try:
        result = call_structured(
            step="outlook_merge",
            system_prompt="아웃룩 병합 전문가. 원본 시나리오를 참고하여 시각적으로 동일한 아웃룩만 병합한다.",
            user_prompt=prompt,
            response_schema=schema,
            project_config=project_llm_config,
            schema_name="outlook_merge",
        )
    except Exception as exc:
        logger.warning("Outlook merge GPT call failed: %s", exc)
        return {"merge_groups": [], "name_map": {}}

    merge_groups = result.get("merge_groups", [])

    # 치환 맵 구성
    name_map: Dict[str, str] = {}
    for group in merge_groups:
        keep = group.get("keep_name", "")
        for old_name in group.get("merge_names", []):
            if old_name != keep:
                name_map[old_name] = keep
                logger.info("Outlook merge: '%s' → '%s' (%s)", old_name, keep, group.get("reason", ""))

    logger.info("Outlook merge complete: %d groups, %d renames", len(merge_groups), len(name_map))
    return {"merge_groups": merge_groups, "name_map": name_map}


def apply_merge_to_t2i(scenes: List[Dict[str, Any]], name_map: Dict[str, str]) -> None:
    """씬 T2I 프롬프트 내 아웃룩 이름을 병합 결과로 치환 (in-place)."""
    if not name_map:
        return

    for scene in scenes:
        # t2i_prompt 치환
        t2i = scene.get("t2i_prompt", "")
        for old_name, new_name in name_map.items():
            t2i = t2i.replace(f"[{old_name}]", f"[{new_name}]")
        scene["t2i_prompt"] = t2i

        # t2i_variations 치환
        for var in scene.get("t2i_variations", []):
            vt2i = var.get("t2i_prompt", "")
            for old_name, new_name in name_map.items():
                vt2i = vt2i.replace(f"[{old_name}]", f"[{new_name}]")
            var["t2i_prompt"] = vt2i

        # outlooks 치환
        for outlook in scene.get("outlooks", []):
            oname = outlook.get("outlook_name", "")
            if oname in name_map:
                outlook["outlook_name"] = name_map[oname]
