"""signage_author — 장면이 부르는 실물 표기의 원어 문안 저작 (2026-08-14 #119②).

카나리아 S3 실측: 표기 정책 v20 은 "장면이 부르는 원어 표기 허용 + 발명
금지"를 실었는데 팻말이 **뭐라고 쓰여 있는지**는 아무도 공급하지 않아
모델이 규정 준수로 백지 팻말을 그렸다. 허용은 값이 아니다 — 값(문안)을
저작해 공급해야 그릴 재료가 생긴다.

계약 요점:
- 판별+저작 = 1콜(gemini-flash). "대부분의 샷은 빈 목록"이 팩 본문에
  명문 — 당연한 것은 저작하지 않는다.
- 문안은 무조건 원어(세계관 사실이 주는 장소·시대의 언어) — 팩이 소유.
  코드에는 표기 대상 어휘가 없다(통칭 하드코딩 금지).
- 산출은 호출측이 records 사이드카에 캐시·영속한다(지문 = 샷 텍스트+
  장소+세계관+정책+팩) — 기록 없는 유료 저작은 없다.
- 플래그 `signage_author_enabled` 기본 OFF = byte-identical.
"""
from __future__ import annotations

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

from app.modules.prompt_loader import load_prompt, pack_dir_content_hash

logger = logging.getLogger(__name__)

_MODULE = "signage_author"

PACK_VERSION_MAP = {
    "1": "1.202608141700",
}
SIGNAGE_PACK_VERSION = "1"
# 정책 문자열 — ON 시 지문 기여. 저작 계약이 바뀌면 버전을 올린다.
SIGNAGE_POLICY_VERSION = "signage_author_v1_flash"
# 한 샷의 문안 상한 — 프레임을 지배하는 것만 (넘치면 앞에서 자른다).
MAX_INSCRIPTIONS = 3

AUTHOR_MODEL = "gemini-flash"  # 보조 분업 — 판별·짧은 저작


def resolve_signage_pack(selector: str = SIGNAGE_PACK_VERSION) -> str:
    try:
        return PACK_VERSION_MAP[selector]
    except KeyError:
        raise ValueError(
            f"unknown signage_author pack selector {selector!r} "
            f"(known: {sorted(PACK_VERSION_MAP)})")


def signage_pack_content_hash(
        selector: str = SIGNAGE_PACK_VERSION) -> str:
    return pack_dir_content_hash(_MODULE, resolve_signage_pack(selector))


def build_author_schema() -> Dict[str, Any]:
    return {
        "type": "object",
        "properties": {
            "inscriptions": {
                "type": "array", "maxItems": 4,
                "items": {
                    "type": "object",
                    "properties": {
                        "surface_native": {
                            "type": "string", "minLength": 2},
                        "text_native": {"type": "string", "minLength": 1},
                        "reason_ko": {"type": "string", "minLength": 2},
                    },
                    "required": [
                        "surface_native", "text_native", "reason_ko"],
                    "additionalProperties": False,
                },
            },
        },
        "required": ["inscriptions"],
        "additionalProperties": False,
    }


def author_inscriptions(
    *,
    step_tag: str,
    shot_text: str,
    place_text: str,
    world_facts_block: str,
    project_config: Optional[Dict[str, Any]] = None,
    opik_metadata: Optional[Dict[str, Any]] = None,
) -> List[Dict[str, Any]]:
    """샷 하나의 표기 필요 판별 + 원어 문안 저작 — 1콜, 빈 목록이 기본."""
    from app.modules.llm.llm_client import call_structured

    author_sys = load_prompt(
        _MODULE, "author_sys", version=resolve_signage_pack()).strip()
    parts = [{
        "type": "text",
        "text": ("SHOT (authoritative):\n" + shot_text.strip()
                 + "\n\nLOCATION:\n" + (place_text or "").strip()
                 + "\n\nWORLD FACTS (place, era and language —"
                 " authoritative):\n" + (world_facts_block or "").strip()),
    }]
    pc = {**(project_config or {}), step_tag: {"model": AUTHOR_MODEL}}
    data = call_structured(
        step_tag, author_sys, parts, build_author_schema(),
        project_config=pc, schema_name=step_tag,
        opik_metadata=opik_metadata,
    )
    items = [x for x in (data.get("inscriptions") or [])
             if isinstance(x, dict)]
    return items[:MAX_INSCRIPTIONS]
