"""GPT-5.4 vs Gemini 3 Flash: 씬 10 분석 비교 테스트.

씬 10 (INT. 폐창고-DAY / 서바이벌 게임의 승리)에서 강의원이 visible_entities에
포함되는지 테스트. 강의원은 소울라이드 V.O.로만 등장하므로 물리적 등장 아님.

Usage:
    cd backend
    .venv/bin/python -m tests.test_gpt_scene10_analysis --model gpt
    .venv/bin/python -m tests.test_gpt_scene10_analysis --model gemini
    .venv/bin/python -m tests.test_gpt_scene10_analysis --model both
"""

import json
import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from pathlib import Path

# ── 프롬프트 로드 ──

PROMPT_DIR = Path(__file__).resolve().parent.parent.parent / "prompts" / "_base" / "scene_extractor_v2"


def _get_latest_version_dir() -> Path:
    versions = sorted([d.name for d in PROMPT_DIR.iterdir() if d.is_dir()], reverse=True)
    return PROMPT_DIR / versions[0]


def _load_prompt(name: str) -> str:
    return (_get_latest_version_dir() / name).read_text(encoding="utf-8").strip()


def _load_schema() -> dict:
    path = _get_latest_version_dir() / "scene_detail_schema.json"
    return json.loads(path.read_text(encoding="utf-8"))


def _add_additional_properties_false(schema: dict) -> dict:
    """OpenAI strict mode 요구사항: 모든 object에 additionalProperties: false 추가."""
    if isinstance(schema, dict):
        if schema.get("type") == "object":
            schema["additionalProperties"] = False
        for v in schema.values():
            if isinstance(v, (dict, list)):
                _add_additional_properties_false(v)
    elif isinstance(schema, list):
        for item in schema:
            if isinstance(item, (dict, list)):
                _add_additional_properties_false(item)
    return schema


# ── 데이터 ──

SCENE_10_TEXT = """INT. 폐창고-DAY
막다른폐창고앞으로몰린동녘.
뒤쫓아온지프차.동녘을조준하고방아쇠를당기는데,총알이떨어졌다.
부릉부릉악셀을밟으며동녘을밀어버리려고준비하는지프차.
그대로동녘에게돌진하는지프차.
동녘바로뒤무너진벽틈으로얼른들어가버리고.
벽을들이받는지프차.벽이무너지면서지붕에얹혀있던구조물들이지프로
떨어지고.
큰폭발을일으키는지프차..
창고도터져버리고…
동녘도죽었을까하는순간..
검정을뒤집어쓴채창고뒤로기어나오는동녘..
손목에차고있는단말기에서.
위너!표시가나고..
헬기가다가오며,강의원님우승하셨습니다.축하드립니다.소리가들린다.
어리둥절한동녘과강의원."""

PREV_REF_TEXT = "씬 9: EXT. 다른일각-DAY — 조금전난사당해죽은시체들옆에같이누워있는동녘..옆의시체가진짜죽었는지흘깃보며끔찍하다는표정..동녘 아저씨.이사람들진짜죽은거예요?..."

NEXT_REF_TEXT = "씬 11: INT. 클럽하우스-DAY — 강의원뒷목의기계해제되고,눈을뜨고일어나면,주변의보좌관들과의원들강의원을축하하고..강의원어리둥절한데.다가오는김의원..."

OUTLOOK_MAPPING = "- [[동녘]+[서바이벌군복]]"

ENTITY_NAMES_BLOCK = """인물 (프롬프트에서 반드시 [[이름]+[아웃룩이름]] 형식. 아웃룩 불명이면 [[이름]+[미지정]]):
  - 동녘 (예: [[동녘]+[아웃룩이름]] 또는 [[동녘]+[미지정]])
  - 강의원 (예: [[강의원]+[아웃룩이름]] 또는 [[강의원]+[미지정]])
  - 김의원 (예: [[김의원]+[아웃룩이름]] 또는 [[김의원]+[미지정]])
  - 한치호 (예: [[한치호]+[아웃룩이름]] 또는 [[한치호]+[미지정]])
  - 은성 (예: [[은성]+[아웃룩이름]] 또는 [[은성]+[미지정]])
  - 서현 (예: [[서현]+[아웃룩이름]] 또는 [[서현]+[미지정]])
  - 강사장 (예: [[강사장]+[아웃룩이름]] 또는 [[강사장]+[미지정]])
  - 순례 (예: [[순례]+[아웃룩이름]] 또는 [[순례]+[미지정]])
  - 덕현 (예: [[덕현]+[아웃룩이름]] 또는 [[덕현]+[미지정]])
  - 서창 (예: [[서창]+[아웃룩이름]] 또는 [[서창]+[미지정]])
  - 한재호 (예: [[한재호]+[아웃룩이름]] 또는 [[한재호]+[미지정]])
  - DR.NEX (예: [[DR.NEX]+[아웃룩이름]] 또는 [[DR.NEX]+[미지정]])
  - 코카 (예: [[코카]+[아웃룩이름]] 또는 [[코카]+[미지정]])
배경 (프롬프트에서 [이름: 시각적 설명] 사용 — 상세 설명 필수):
  - [클럽하우스 폐창고: 어둡고 낡은 대형 산업용 창고 내부로, 수십 개의 1인용 수직 캡슐이 줄지어]
  - [야산: 경사진 흙바닥과 잡목, 수풀, 바위가 어우러진 산지 지형]
  - [블랙마켓: 서울의 낡은 상가 건물 내부에 위치한 어둡고 폐쇄적인 실내 공간]
  - [화장터: 대형 금속 화로 설비가 벽면에 매립된 실내 공간]
  - [클론 행사장: 대규모 인원을 수용할 수 있는 넓고 현대적인 이벤트 홀]
  - [오리엔티스 실험실: 금속과 유리 재질로 이루어진 첨단 연구 공간]
물체 (프롬프트에서 [[이름]] 사용):
  - [[1인용 캡슐]]
  - [[의식 동기화 소형기기]]
  - [[라이드건]]
  - [[대형 트레일러]]
  - [[ZRBB51]]
  - [[캣츠아이]]"""


def _build_user_prompt(variation_count: int) -> str:
    """동일한 사용자 프롬프트 생성."""
    return _load_prompt("turn_scene_detail.md").format(
        scene_index=10,
        heading="INT. 폐창고-DAY",
        scene_text=SCENE_10_TEXT,
        entity_names_block=ENTITY_NAMES_BLOCK,
        scene_outlook_mapping=OUTLOOK_MAPPING,
        prev_ref_text=PREV_REF_TEXT,
        next_ref_text=NEXT_REF_TEXT,
        variation_count=variation_count,
    )


def _print_result(label: str, result: dict, out_filename: str):
    """결과 출력 + 저장."""
    print(f"\n{'=' * 60}")
    print(f"{label} 분석 결과")
    print("=" * 60)
    print(f"\n  heading: {result.get('heading')}")
    print(f"  beat_title: {result.get('beat_title')}")
    print(f"  representative_moment: {result.get('representative_moment')}")

    print(f"\n  visible_entities:")
    for ve in result.get("visible_entities", []):
        print(f"    - {ve['entity_name']} ({ve['entity_type']})")

    char_names = [ve["entity_name"] for ve in result.get("visible_entities", [])
                  if ve["entity_type"] == "character"]
    print(f"\n  등장 인물: {char_names}")
    if "강의원" in char_names:
        print("  ❌ 강의원이 visible_entities에 포함됨 (잘못됨 — V.O.만 등장)")
    else:
        print("  ✅ 강의원이 visible_entities에 미포함 (정확함 — V.O.만 등장)")

    print(f"\n  dependent_scene_index: {result.get('dependent_scene_index')}")
    print(f"  dependency_reason: {result.get('dependency_reason')}")

    print(f"\n  T2I variations ({len(result.get('t2i_variations', []))}):")
    for v in result.get("t2i_variations", []):
        label_v = v.get("variant_label", "")
        effect = v.get("camera_effect", "")
        prompt = v.get("t2i_prompt", "")
        print(f"    [{label_v}] {effect}")
        print(f"      {prompt[:200]}...")

    out_path = Path(__file__).parent / out_filename
    out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"\n  전체 결과 저장: {out_path}")


def run_gpt_analysis():
    """GPT-5.4로 씬 10 분석 (프로젝트의 OpenAIClient 사용)."""
    from app.modules.llm.openai_client import OpenAIClient
    from app.core.config import settings

    system_prompt = _load_prompt("system.md")
    schema = _load_schema()
    # OpenAI strict mode: additionalProperties: false 필수
    schema = _add_additional_properties_false(schema)
    variation_count = settings.scene_variation_count
    user_prompt = _build_user_prompt(variation_count)

    print("=" * 60)
    print(f"GPT-5.4 씬 10 분석 테스트 (model: {settings.openai_model})")
    print("=" * 60)
    print(f"[User Prompt length]: {len(user_prompt)} chars")

    client = OpenAIClient()
    result = client.generate_structured(
        system_prompt=system_prompt,
        user_prompt=user_prompt,
        response_schema=schema,
        schema_name="scene_detail",
    )

    _print_result("GPT-5.4", result, "gpt_scene10_result.json")
    return result


def run_gemini_analysis():
    """Gemini 3 Flash로 같은 씬 10 분석 (비교용)."""
    from app.modules.llm.gemini_text_client import GeminiTextClient
    from app.core.config import settings

    system_prompt = _load_prompt("system.md")
    schema = _load_schema()
    variation_count = settings.scene_variation_count
    user_prompt = _build_user_prompt(variation_count)

    print("\n" + "=" * 60)
    print(f"Gemini 3 Flash 씬 10 분석 테스트 (model: {settings.gemini_text_model})")
    print("=" * 60)
    print(f"[User Prompt length]: {len(user_prompt)} chars")

    client = GeminiTextClient()
    result = client.send_structured(
        user_message=user_prompt,
        response_schema=schema,
        system_instruction=system_prompt,
    )

    _print_result("Gemini 3 Flash", result, "gemini_scene10_result.json")
    return result


def run_gemini_pro_analysis():
    """Gemini 3.1 Pro로 같은 씬 10 분석."""
    from app.modules.llm.gemini_text_client import GeminiTextClient
    from app.core.config import settings

    model = "gemini-3.1-pro-preview"
    system_prompt = _load_prompt("system.md")
    schema = _load_schema()
    variation_count = settings.scene_variation_count
    user_prompt = _build_user_prompt(variation_count)

    print("\n" + "=" * 60)
    print(f"Gemini 3.1 Pro 씬 10 분석 테스트 (model: {model})")
    print("=" * 60)
    print(f"[User Prompt length]: {len(user_prompt)} chars")

    client = GeminiTextClient(model=model)
    result = client.send_structured(
        user_message=user_prompt,
        response_schema=schema,
        system_instruction=system_prompt,
    )

    _print_result("Gemini 3.1 Pro", result, "gemini_pro_scene10_result.json")
    return result


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="GPT vs Gemini 씬 10 분석 비교")
    parser.add_argument("--model", choices=["gpt", "gemini", "gemini-pro", "both"], default="both")
    args = parser.parse_args()

    if args.model in ("gpt", "both"):
        run_gpt_analysis()
    if args.model in ("gemini", "both"):
        run_gemini_analysis()
    if args.model == "gemini-pro":
        run_gemini_pro_analysis()
