"""trip-wire 5종 증거 수치의 생성·검증·대조 헬퍼 (task-2950).

배경:
    dispatch 프롬프트에는 trip-wire 5종(Critical7 / PII net-new / 회귀실패 /
    forbidden_paths 침범 / nonce)이 인라인으로 상주한다. 그 **실측 수치**는
    ``result.json`` 의 ``trip_wire`` 필드에 백업된다.

★ 핵심 정책 (QC-RULES.md §trip-wire 8-B):
    result.json 의 수치는 실행 주체의 **자기증명**이므로 그 자체로 통과 근거가
    아니다. 판정은 ANU 가 clean worktree 에서 게이트를 **재실행**해 얻은 수치로
    하고, result.json 값은 대조용 백업으로만 쓴다. 불일치 시 재실행 수치를 채택한다.

설계 원칙:
    - 무손상: 어떤 입력에도 raise 하지 않는다(잘못된 타입은 위반으로 분류).
    - 미측정(None) 을 0 으로 승격하지 않는다. 미측정은 통과가 아니다.
"""

from __future__ import annotations

from typing import Any, Dict, Iterable, Optional

# result.json 안의 백업 필드 이름
TRIP_WIRE_RESULT_KEY = "trip_wire"

# 0 이어야 통과인 카운터 4종
TRIP_WIRE_COUNTER_FIELDS = (
    "critical7",
    "pii_net_new",
    "regression_failures",
    "forbidden_paths_violations",
)
# nonce 는 카운터가 아니라 task_id 일치 검사
TRIP_WIRE_NONCE_FIELD = "nonce"
TRIP_WIRE_FIELDS = TRIP_WIRE_COUNTER_FIELDS + (TRIP_WIRE_NONCE_FIELD,)


def build_trip_wire_evidence(
    task_id: str = "",
    critical7: Optional[int] = None,
    pii_net_new: Optional[int] = None,
    regression_failures: Optional[int] = None,
    forbidden_paths_violations: Optional[int] = None,
    nonce: Optional[str] = None,
) -> Dict[str, Any]:
    """trip-wire 5종 증거 dict 를 생성한다.

    Args:
        task_id: 발사된 작업 ID. ``nonce`` 미지정 시 nonce 로 사용된다.
        critical7: Critical7 지적 건수 (통과=0). 미측정이면 None.
        pii_net_new: net-new PII 노출 건수 (통과=0). 미측정이면 None.
        regression_failures: 회귀 테스트 실패 건수 (통과=0). 미측정이면 None.
        forbidden_paths_violations: forbidden_paths 침범 건수 (통과=0). 미측정이면 None.
        nonce: 증거의 소유 task_id. 미지정 시 ``task_id``.

    Returns:
        5종 필드를 가진 dict. 미측정 항목은 None 으로 남는다(0 으로 채우지 않는다).
    """
    return {
        "critical7": critical7,
        "pii_net_new": pii_net_new,
        "regression_failures": regression_failures,
        "forbidden_paths_violations": forbidden_paths_violations,
        TRIP_WIRE_NONCE_FIELD: nonce if nonce is not None else (task_id or None),
    }


def attach_trip_wire_evidence(result: Any, evidence: Dict[str, Any]) -> Dict[str, Any]:
    """result.json dict 에 ``trip_wire`` 증거를 병합한 **새 dict** 를 반환한다.

    원본을 변형하지 않는다(호출자가 이미 만든 payload 를 조용히 바꾸지 않기 위해).
    ``result`` 가 dict 가 아니면 새 dict 를 만들어 증거만 담는다(무손상).
    """
    merged: Dict[str, Any] = dict(result) if isinstance(result, dict) else {}
    merged[TRIP_WIRE_RESULT_KEY] = dict(evidence) if isinstance(evidence, dict) else {}
    return merged


def _counter_violation(field: str, value: Any) -> Optional[str]:
    """카운터 필드 1개를 판정. 위반이면 사유 문자열, 통과면 None."""
    if value is None:
        return f"{field}=미측정(None) — 미측정은 통과가 아니다"
    if isinstance(value, bool) or not isinstance(value, int):
        return f"{field}={value!r} — 정수 카운터가 아니다"
    if value != 0:
        return f"{field}={value} — 0 이어야 한다"
    return None


def verify_trip_wire_evidence(evidence: Any, task_id: str = "") -> Dict[str, Any]:
    """증거 dict 가 trip-wire 5종을 모두 통과하는지 판정한다.

    ★ 이 함수의 PASS 는 **자기증명 통과**일 뿐이다. 완료 판정은 ANU 가 clean
      worktree 에서 재실행한 수치로 한다(QC-RULES.md §trip-wire 8-B).

    Returns:
        ``{"passed": bool, "violations": [사유, ...], "checked": [필드, ...]}``
    """
    violations: list[str] = []
    if not isinstance(evidence, dict):
        return {
            "passed": False,
            "violations": [f"trip_wire 증거가 dict 가 아니다: {type(evidence).__name__}"],
            "checked": [],
        }

    for field in TRIP_WIRE_COUNTER_FIELDS:
        reason = _counter_violation(field, evidence.get(field))
        if reason:
            violations.append(reason)

    nonce = evidence.get(TRIP_WIRE_NONCE_FIELD)
    if not nonce:
        violations.append(f"{TRIP_WIRE_NONCE_FIELD}=미측정(None) — 미측정은 통과가 아니다")
    elif task_id and nonce != task_id:
        violations.append(f"{TRIP_WIRE_NONCE_FIELD}={nonce!r} — 발사 task_id({task_id!r})와 불일치")

    return {
        "passed": not violations,
        "violations": violations,
        "checked": list(TRIP_WIRE_FIELDS),
    }


def compare_trip_wire_evidence(claimed: Any, observed: Any) -> Dict[str, Any]:
    """봇 자기증명(claimed) 과 ANU 재실행 관측치(observed) 를 항목별로 대조한다.

    QC-RULES.md §trip-wire 8-B 의 3단계(불일치 처리)를 코드로 지원한다.
    불일치 시 **관측치를 채택**한다 — 반환 dict 의 ``adopted`` 가 그 결과다.

    Returns:
        ``{"match": bool, "mismatches": {field: {"claimed": x, "observed": y}},
           "adopted": {field: observed_value}}``
    """
    claimed_map = claimed if isinstance(claimed, dict) else {}
    observed_map = observed if isinstance(observed, dict) else {}

    mismatches: Dict[str, Dict[str, Any]] = {}
    adopted: Dict[str, Any] = {}
    for field in TRIP_WIRE_FIELDS:
        c_val = claimed_map.get(field)
        o_val = observed_map.get(field)
        adopted[field] = o_val  # 항상 관측치 채택
        if c_val != o_val:
            mismatches[field] = {"claimed": c_val, "observed": o_val}

    return {"match": not mismatches, "mismatches": mismatches, "adopted": adopted}


def extract_trip_wire_evidence(result: Any) -> Dict[str, Any]:
    """result.json dict 에서 ``trip_wire`` 증거를 꺼낸다(없으면 빈 dict)."""
    if not isinstance(result, dict):
        return {}
    evidence = result.get(TRIP_WIRE_RESULT_KEY)
    return evidence if isinstance(evidence, dict) else {}


def iter_trip_wire_fields() -> Iterable[str]:
    """trip-wire 필드 이름을 순회한다(프롬프트/문서 대조용)."""
    return iter(TRIP_WIRE_FIELDS)


__all__ = [
    "TRIP_WIRE_RESULT_KEY",
    "TRIP_WIRE_COUNTER_FIELDS",
    "TRIP_WIRE_NONCE_FIELD",
    "TRIP_WIRE_FIELDS",
    "build_trip_wire_evidence",
    "attach_trip_wire_evidence",
    "verify_trip_wire_evidence",
    "compare_trip_wire_evidence",
    "extract_trip_wire_evidence",
    "iter_trip_wire_fields",
]
