"""FINDING 7 (e2e-bughunt-v1) scene_detail owned redraw repair wrapper.

owned judge 가 redraw_violation 을 검출한 t2i_prompt 를 1-call 로 좁게 rewrite —
owned 환경객체를 redraw 대신 anchor/omit 으로 고친다. caller (detail_steps
`_attempt_owned_redraw_repair`) 가 결과를 기존 validator 로 재검증한 뒤에만 적용한다.

`_owned_judge.py` 와 같은 패턴 — LLM 호출 의존성 (prompt_loader / call_structured)
을 가지므로 pure helper (`_owned_helpers.py`) 와 분리한다.

Contract:
    - redraw_violations 가 비면 AppError (caller 가 redraw 검출 후에만 호출).
    - 1-call only — repair 자체는 재시도하지 않는다 (caller 가 max 1 attempt 관리).
    - 응답에 `t2i_prompt` (non-empty str) / `owned_object_usage` (list) 누락 시
      AppError(step.contract_violation) — silent fallback 금지
      (feedback_no_silent_fallback.md).
"""
from __future__ import annotations

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

from app.core.errors import AppError


def run_owned_repair(
    *,
    t2i_prompt: str,
    owned: List[str],
    redraw_violations: List[Dict[str, str]],
    camera_direction: str,
    call_structured_fn: Callable[..., Dict[str, Any]],
    project_config: Optional[Dict[str, Any]] = None,
    opik_metadata: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """owned redraw violation t2i_prompt 1-call repair.

    Args:
        t2i_prompt: 수정 대상 원본 T2I 프롬프트.
        owned: chain_bg owned 객체 English canonical 목록.
        redraw_violations: owned judge violations 중 verdict == "redraw_violation"
            인 것만 (caller 필터). 각 entry = {owned_object, violating_phrase,
            reason, verdict}.
        camera_direction: shot 자연어 카메라 정보.
        call_structured_fn: `app.modules.llm.llm_client.call_structured` (또는
            test fake).
        project_config / opik_metadata: optional passthrough.

    Returns:
        {"t2i_prompt": str, "owned_object_usage": List[Dict[str, str]]}.

    Raises:
        AppError(step.contract_violation) — redraw_violations 가 비었거나 LLM 응답
        schema contract 위반.
    """
    if not redraw_violations:
        raise AppError(
            code="step.contract_violation",
            message="run_owned_repair called with empty redraw_violations",
        )

    # lazy import — circular import 방지 + helper 모듈과 격리.
    from app.modules.prompt_loader import load_prompt, load_schema
    from app.core.steps._owned_helpers import format_redraw_violations_block

    system = load_prompt("scene_detail_owned_repair", "system")
    schema = load_schema("scene_detail_owned_repair", "schema")
    template = load_prompt("scene_detail_owned_repair", "user_template")

    owned_block = "\n".join(f"- {o}" for o in owned)
    violations_block = format_redraw_violations_block(redraw_violations)
    user_prompt = (
        template
        .replace("{t2i_prompt}", t2i_prompt or "")
        .replace("{owned_list_block}", owned_block)
        .replace("{redraw_violations_block}", violations_block)
        .replace("{camera_direction}", camera_direction or "")
    )

    result = call_structured_fn(
        step="scene_detail_owned_repair",
        system_prompt=system,
        user_prompt=user_prompt,
        response_schema=schema,
        project_config=project_config,
        schema_name="scene_detail_owned_repair",
        opik_metadata=opik_metadata,
    )

    # fail-fast — silent fallback 금지 (feedback_no_silent_fallback.md).
    if not isinstance(result, dict):
        raise AppError(
            code="step.contract_violation",
            message=f"scene_detail_owned_repair response not a dict: {result!r}",
        )
    repaired_prompt = result.get("t2i_prompt")
    if not isinstance(repaired_prompt, str) or not repaired_prompt.strip():
        raise AppError(
            code="step.contract_violation",
            message=(
                "scene_detail_owned_repair response missing/empty 't2i_prompt': "
                f"{result!r}"
            ),
        )
    usage = result.get("owned_object_usage")
    if not isinstance(usage, list):
        raise AppError(
            code="step.contract_violation",
            message=(
                "scene_detail_owned_repair 'owned_object_usage' must be list "
                f"(got {type(usage).__name__}): {result!r}"
            ),
        )
    return {"t2i_prompt": repaired_prompt, "owned_object_usage": list(usage)}
