"""① LLM step — interview answers → VideoSpec / ScriptResult (JSON).

Uses the OpenAI SDK with Structured Outputs (response_format json_schema, strict)
so the result is guaranteed to match the Remotion data contract. The stable
guardrail system prompt benefits from OpenAI's automatic prompt caching.
"""
from __future__ import annotations

import json
from openai import OpenAI

from app import config as cfg
from app.schemas import GenerateRequest, VideoSpec, ScriptResult

MODEL = cfg.OPENAI_MODEL

# Stable guardrail + role prompt (automatic prompt caching applies to this prefix).
SYSTEM_PROMPT = """\
당신은 한국 로컬 비즈니스(숙소·매장·제품)의 8초 세로형 숏폼 광고 카피 디렉터입니다.
사장/마케터가 직접 답한 정보만으로, 영상 사양(VideoSpec)을 설계합니다. 복잡한 분석은 하지 않습니다.

[절대 가드레일]
1. 식민지 유산·역사정치 민감 용어 금지: "적산가옥", "일제", "식민지", "근대사" 등 → 미학·경험 어휘로 대체.
2. 거짓·과장 금지: 사용자가 주지 않은 거리(예: "서울 40분")·효능·수치를 지어내지 않는다. 주어진 정보만 사용.
3. 자막은 흰색 모노크롬 전제 — 카피에 색 지시어를 넣지 않는다.
4. AI 디스클로저는 end_card.disclosure에 고정 문구로 포함.

[hook 작성 — 레퍼런스 바이럴 공식 "[반전/호기심] + [한방]"]
- eyebrow: 짧은 도발/맥락 한 줄 (예: "아무도 모르는 군산").
- title: 굵은 한방 헤드라인. 사용자의 셀링포인트를 가장 강하게 압축.

[selling_point.items] 정확히 3개. 사용자 한방 셀링포인트 + 유형 기반의 구체 명사구(짧게). 이모지 금지.

[brand_lines] 감성 2줄 (각 줄 짧게). 한 덩어리로 읽히게.

[end_card] brand=업체/상품명, location=주소나 URL(없으면 빈 문자열 ""), disclosure 고정.

[caption] 업로드용. 정보(업체/주소/가격) + 짧은 감성 + 해시태그 5~8개. 유형에 맞는 해시태그.

[profile 선택]
- place + 정적/프라이빗/힐링 → "Still Cinema"
- place + 뷰/활동성/포토스팟, 또는 product 일반 → "Rhythm Reveal"
- 파티/워터파크/대형 액티비티 → "Maximum Viral"

[higgsfield_prompt] 영문. marketing_studio_video 용 고품질 프롬프트. 아래 구조를 반드시 따를 것.

① 포맷 명시: "VERTICAL 9:16 portrait format, mobile short-form."
② 카메라 무브 (place 유형): 드론/핸드헬드 하이브리드로 실내외를 역동적으로 순회.
   예시 패턴: "smooth drone glide through entrance → quick crash-zoom onto hero detail → kinetic handheld reveal of interior space → pull-back drone shot of exterior"
   product 유형: "macro crash-zoom onto product → slow orbit reveal → snappy cut to detail closeup"
③ 분위기: 시간대(twilight/golden hour/daylight)·색감(teal-and-orange grade 또는 유형에 맞는 톤)·질감(subtle film grain, quiet luxury)을 구체적으로 명시.
④ 반드시 포함할 문구: "photorealistic, no AI artifacts, keep architecture and objects structurally accurate, do not warp or distort walls floors or furniture, no morphing, no hallucinated details."
⑤ 마지막 문장: "Hook viewer in first second. Beat-driven energetic edit."
유형(place/product/message)과 업체 분위기에 맞게 세부 내용을 구체화할 것. 절대 짧거나 추상적인 문장으로 끝내지 말 것.
"""

# Structured-output JSON schema (strict: additionalProperties:false + all props required).
_SPEC_SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "properties": {
        "profile": {"type": "string", "enum": ["Still Cinema", "Rhythm Reveal", "Maximum Viral"]},
        "higgsfield_prompt": {"type": "string"},
        "hook": {
            "type": "object",
            "additionalProperties": False,
            "properties": {"eyebrow": {"type": "string"}, "title": {"type": "string"}},
            "required": ["eyebrow", "title"],
        },
        "selling_point": {
            "type": "object",
            "additionalProperties": False,
            "properties": {"items": {"type": "array", "items": {"type": "string"}}},
            "required": ["items"],
        },
        "brand_lines": {"type": "array", "items": {"type": "string"}},
        "end_card": {
            "type": "object",
            "additionalProperties": False,
            "properties": {
                "brand": {"type": "string"},
                "location": {"type": "string"},
                "disclosure": {"type": "string"},
            },
            "required": ["brand", "location", "disclosure"],
        },
        "caption": {"type": "string"},
    },
    "required": [
        "profile", "higgsfield_prompt", "hook", "selling_point",
        "brand_lines", "end_card", "caption",
    ],
}


def _kind_label(kind: str) -> str:
    return {
        "place": "장소",
        "message": "메시지(축하·안부·홍보)",
        "product": "물건(제품·음식 등 정적 아이템)",
    }.get(kind, kind)


def _user_msg(req: GenerateRequest, tail: str) -> str:
    return (
        f"유형: {_kind_label(req.kind)}\n"
        f"업체/상품명: {req.biz_name}\n"
        f"주소/판매URL: {req.addr or '(없음)'}\n"
        f"가격: {req.price or '(없음)'}\n"
        f"강력한 한방 셀링포인트: {req.selling}\n\n"
        f"{tail}"
    )


def _complete(client: OpenAI, system: str, user: str, schema_name: str,
              schema: dict, max_tokens: int) -> dict:
    """OpenAI Structured Outputs 호출 → 검증된 JSON(dict) 반환."""
    resp = client.chat.completions.create(
        model=MODEL,
        max_completion_tokens=max_tokens,
        response_format={
            "type": "json_schema",
            "json_schema": {"name": schema_name, "strict": True, "schema": schema},
        },
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": user},
        ],
    )
    return json.loads(resp.choices[0].message.content)


def build_spec(req: GenerateRequest, client: OpenAI | None = None) -> VideoSpec:
    client = client or OpenAI()
    data = _complete(
        client, SYSTEM_PROMPT, _user_msg(req, "위 정보로 VideoSpec을 설계해줘."),
        "video_spec", _SPEC_SCHEMA, max_tokens=2048,
    )
    return VideoSpec.model_validate(data)


# ---------- 자막 스크립트 4블록 ----------
SCRIPT_SYSTEM = """\
당신은 한국 로컬 비즈니스 숏폼 광고의 자막 스크립트 작가입니다.
사장/마케터가 답한 정보로, 영상에 얹을 4개 블록을 만듭니다.

[4블록]
- intro: 첫 1~2초 후킹. 호기심·반전을 거는 짧은 한 줄.
- selling: 가장 강력한 셀링포인트를 한 문장으로 압축.
- story: 감성 스토리. 고객이 그 공간/제품에서 느낄 감정을 1~2문장으로.
- cta: 행동 유도. 예약·구매·방문으로 자연스럽게.

[가드레일]
- 거짓·과장 금지(주어지지 않은 거리·효능·수치 X). 주어진 정보만.
- 식민지 유산 어휘(적산가옥/일제/식민지 등) 금지 → 미학·경험 어휘로.
- 자막은 흰색 모노크롬 전제 — 색 지시어 넣지 않음. 이모지 남발 금지.
- 각 블록은 짧고 입에 붙게. 한국어.
"""

_SCRIPT_SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "properties": {
        "intro": {"type": "string"},
        "selling": {"type": "string"},
        "story": {"type": "string"},
        "cta": {"type": "string"},
    },
    "required": ["intro", "selling", "story", "cta"],
}


def build_script(req: GenerateRequest, client: OpenAI | None = None) -> ScriptResult:
    client = client or OpenAI()
    data = _complete(
        client, SCRIPT_SYSTEM, _user_msg(req, "위 정보로 4블록 자막 스크립트를 만들어줘."),
        "script", _SCRIPT_SCHEMA, max_tokens=1024,
    )
    return ScriptResult.model_validate(data)
