"""G3.1 evidence/inference 4-field helpers — single source.

LLM strict schema 가 강제 못하는 contract consistency 와 옛 checkpoint
backfill 을 처리. 두 step (scene_consistency, scene_detail) 이 공유.
"""
from __future__ import annotations

import logging
from typing import Any, Dict, Union

from app.core.errors import AppError

logger = logging.getLogger(__name__)

LEGACY_CONFIDENCE = "legacy"  # adapter-only marker. LLM strict schema 가 거부.
VALID_LLM_CONFIDENCE = ("high", "medium", "low")
VALID_STORED_CONFIDENCE = VALID_LLM_CONFIDENCE + (LEGACY_CONFIDENCE,)
EVIDENCE_FIELDS = ("source_facts", "visual_inferences", "creative_decisions")


def _normalize_evidence_fields(
    item: Union[Dict[str, Any], Any], where: str = "",
) -> Union[Dict[str, Any], Any]:
    """G3.1 lazy backfill — 4 evidence 필드 누락 시 default 주입.

    **옛 checkpoint 전용** (CRITICAL #2). 새 LLM 응답에는 호출 금지 — strict schema
    가 4 필드 강제. 이 함수가 빈자리 채우면 strict 우회 silent backfill 회귀 발생.

    Mutation in-place. file 안 건드림.

    Type guard (Cl-M1 fix): item 이 dict 아니면 그대로 반환 — caller 가 list-of-X
    iter 중 비-dict element 를 만나도 AttributeError 안 남.
    """
    if not isinstance(item, dict):
        return item

    backfilled = False
    for field in EVIDENCE_FIELDS:
        if field not in item or not isinstance(item[field], list):
            item[field] = []
            backfilled = True

    if "confidence" not in item or item["confidence"] not in VALID_STORED_CONFIDENCE:
        item["confidence"] = LEGACY_CONFIDENCE
        backfilled = True
    elif backfilled:
        item["confidence"] = LEGACY_CONFIDENCE

    if backfilled and where:
        logger.debug("evidence backfill marker=legacy where=%s", where)

    return item


def _normalize_scene_consistency_result(
    scene_result: Dict[str, Any], where: str = ""
) -> Dict[str, Any]:
    """fixed_elements 각 element normalize. PROBLEM #3: malformed container type guard.

    Codex BLOCKING 2 fix: helper 가 'list 아니면 silent return' 으로 끝내면 downstream
    consumer (shot_dependency_t2i / t2i_review / scene_checkpoint_loaders) 가 list
    가정하고 ``v.get()`` 호출 → AttributeError. fixed_elements/t2i_variations 가
    malformed 일 때 helper 가 in-place 로 빈 list 강제 → consumer 정상 (빈 iter, noop).
    옛 데이터 손실은 어차피 malformed 라 의미 없음.
    """
    if not isinstance(scene_result, dict):
        return scene_result
    fixed = scene_result.get("fixed_elements")
    if not isinstance(fixed, list):
        # malformed — in-place coerce. caller status 로 partial 격상은 별도 path.
        scene_result["fixed_elements"] = []
        return scene_result
    for fe in fixed:
        _normalize_evidence_fields(fe, where=where)
    return scene_result


def _normalize_scene_detail_result(
    scene_result: Dict[str, Any], where: str = ""
) -> Dict[str, Any]:
    """t2i_variations 각 variation normalize. type guard.

    Codex BLOCKING 2 fix: malformed t2i_variations 가 list 아니면 in-place 빈 list
    강제 (consumer AttributeError 차단).
    """
    if not isinstance(scene_result, dict):
        return scene_result
    vars_ = scene_result.get("t2i_variations")
    if not isinstance(vars_, list):
        scene_result["t2i_variations"] = []
        return scene_result
    for var in vars_:
        _normalize_evidence_fields(var, where=where)
    return scene_result


def assert_fresh_llm_evidence(item: Dict[str, Any], step_name: str) -> None:
    """post-parse contract validator (IMPROVEMENT #2).

    LLM strict schema = 4 필드 존재만 강제. contract consistency 추가 검증:
      - confidence == "legacy" → reject (LLM 출력 금지, adapter-only)
      - source_facts == [] AND confidence != "low" → violation
      - 4 lists 모두 [] AND confidence != "low" → violation

    AppError(code="step.contract_violation") raise → step_runner retry path.
    **새 LLM 응답에만 호출**. 옛 cp 는 normalize 가 처리.
    """
    confidence = item.get("confidence")
    if confidence == LEGACY_CONFIDENCE:
        raise AppError(
            code="step.contract_violation",
            message=f"{step_name}: LLM 출력에 confidence='legacy' 금지 (adapter-only)",
            status_code=502,
        )
    sf = item.get("source_facts") or []
    vi = item.get("visual_inferences") or []
    cd = item.get("creative_decisions") or []
    if not sf and confidence != "low":
        raise AppError(
            code="step.contract_violation",
            message=f"{step_name}: source_facts=[] 인데 confidence={confidence} (low 여야 함)",
            status_code=502,
        )
    if not (sf or vi or cd) and confidence != "low":
        raise AppError(
            code="step.contract_violation",
            message=f"{step_name}: 4 lists 모두 비어있음 — confidence={confidence}",
            status_code=502,
        )
