# -*- coding: utf-8 -*-
"""task-2729+5 P0-b audit decision check — 회귀 테스트.

대상 스크립트: scripts/harness/v36/anu_pickup_p0b_audit_decision_check.py
공개 함수: make_isolated_root, write_result_json, fake_pickup_factory,
           fake_verify_factory, fixed_clock, run_checks, snapshot_canonical,
           assert_canonical_untouched, main.

절대 제약:
  - ANU key literal(16hex/c119085 전체 키) 절대 작성 금지.
  - pickup_once 실 발사 0 (스크립트가 mock 처리).
  - canonical(/home/jay/workspace) write 0.
"""
from __future__ import annotations

import json
import shutil
import sys
from pathlib import Path

import pytest

# ── sys.path 보강 ─────────────────────────────────────────────────────────────
# tests/regression/ → 2단계 위 = worktree root
_ROOT = Path(__file__).resolve().parents[2]
if str(_ROOT) not in sys.path:
    sys.path.insert(0, str(_ROOT))

# scripts/harness/v36/ 경로 추가 → `import anu_pickup_p0b_audit_decision_check` 가능
HARNESS_DIR = _ROOT / "scripts" / "harness" / "v36"
if str(HARNESS_DIR) not in sys.path:
    sys.path.insert(0, str(HARNESS_DIR))

# ── 스크립트 import ───────────────────────────────────────────────────────────
# 스크립트 내부에서 worktree root 및 dispatch 패키지를 sys.path/sys.modules 에
# 보강하므로, 여기서는 HARNESS_DIR 경로 추가만으로 충분.
import anu_pickup_p0b_audit_decision_check as chk  # noqa: E402  # pyright: ignore[reportMissingImports]


# ─────────────────────────────────────────────────────────────────────────────
# 모듈 scope fixture: run_checks() 를 한 번만 실행 후 재사용
# ─────────────────────────────────────────────────────────────────────────────
@pytest.fixture(scope="module")
def summary() -> dict:
    """run_checks() 를 모듈 전체에서 한 번만 실행."""
    return chk.run_checks()


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 1: all_pass
# ─────────────────────────────────────────────────────────────────────────────
def test_run_checks_all_pass(summary: dict) -> None:
    """run_checks() → summary["all_pass"] is True."""
    assert summary["all_pass"] is True, (
        f"all_pass=False. 실패 항목: "
        f"{[c for c in summary['checks'] if not c['passed']]}"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 2: canonical untouched
# ─────────────────────────────────────────────────────────────────────────────
def test_canonical_untouched(summary: dict) -> None:
    """canonical root 는 변경 없음."""
    assert summary["canonical_untouched"] is True


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 3: raw_key_exposure == 0
# ─────────────────────────────────────────────────────────────────────────────
def test_raw_key_exposure_zero(summary: dict) -> None:
    """ANU raw key 노출 0건."""
    assert summary["raw_key_exposure"] == 0


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 4: findings 빈 리스트
# ─────────────────────────────────────────────────────────────────────────────
def test_no_findings(summary: dict) -> None:
    """이슈 발견 0건."""
    assert summary["findings"] == []


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 5: 모든 checks 통과 + 정확히 11개
# ─────────────────────────────────────────────────────────────────────────────
def test_each_decision_check_passed(summary: dict) -> None:
    """DC1~DC11 모두 passed=True, 총 11개."""
    checks = summary["checks"]
    assert len(checks) == 11, f"check 개수={len(checks)} (expected 11)"
    failed = [c for c in checks if not c["passed"]]
    assert failed == [], f"실패 항목: {failed}"


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 6: wake 시나리오 pickup_calls 검증
# ─────────────────────────────────────────────────────────────────────────────
def test_wake_scenarios_pickup_calls(summary: dict) -> None:
    """DC3(wake)=1, DC9(dedupe)=1, 나머지 no-op/quarantine/skip=0."""
    checks = summary["checks"]

    def get_by_substr(substr: str) -> dict:
        matched = [c for c in checks if substr in c["name"]]
        assert len(matched) == 1, f"'{substr}' 매칭 check={matched}"
        return matched[0]

    # wake 시나리오: pickup_calls == 1
    dc3 = get_by_substr("authoritative_wake")
    assert dc3["pickup_calls"] == 1, (
        f"DC3 pickup_calls={dc3['pickup_calls']} (expected 1)"
    )

    dc9 = get_by_substr("duplicate_prevention")
    assert dc9["pickup_calls"] == 1, (
        f"DC9 pickup_calls={dc9['pickup_calls']} (expected 1)"
    )

    # no-op / quarantine / skip 시나리오: pickup_calls == 0
    noop_substrings = [
        "disabled_noop",        # DC1
        "enabled_nontarget",    # DC2
        "terminal_marker_skip", # DC4
        "dedupe_skip",          # DC5
        "owner_proof_fail",     # DC6
        "readiness_defer",      # DC7
        "null_byte_quarantine",  # DC8
        "sealed_key_missing",   # DC10
        "terminal_skip",        # DC11 (pickup_once_terminal_skip)
    ]
    for substr in noop_substrings:
        c = get_by_substr(substr)
        assert c["pickup_calls"] == 0, (
            f"'{c['name']}' pickup_calls={c['pickup_calls']} (expected 0)"
        )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 7: DC1 disabled_noop verdict
# ─────────────────────────────────────────────────────────────────────────────
def test_disabled_noop_verdict(summary: dict) -> None:
    """DC1: actual == 'NOOP_DISABLED'."""
    checks = summary["checks"]
    dc1 = next(c for c in checks if "disabled_noop" in c["name"])
    assert dc1["actual"] == "NOOP_DISABLED", (
        f"DC1 actual={dc1['actual']!r}"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 8: DC4 terminal marker → PICKUP_SKIP
# ─────────────────────────────────────────────────────────────────────────────
def test_terminal_marker_noop(summary: dict) -> None:
    """DC4: terminal marker no-op → actual == 'PICKUP_SKIP'."""
    checks = summary["checks"]
    dc4 = next(c for c in checks if "terminal_marker_skip" in c["name"])
    assert dc4["actual"] == "PICKUP_SKIP", (
        f"DC4 actual={dc4['actual']!r}"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 9: DC5 dedupe → PICKUP_SKIP
# ─────────────────────────────────────────────────────────────────────────────
def test_dedupe_noop(summary: dict) -> None:
    """DC5: ledger dedupe → actual == 'PICKUP_SKIP'."""
    checks = summary["checks"]
    dc5 = next(c for c in checks if "dedupe_skip" in c["name"])
    assert dc5["actual"] == "PICKUP_SKIP", (
        f"DC5 actual={dc5['actual']!r}"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 10: DC6 owner proof fail → QUARANTINE
# ─────────────────────────────────────────────────────────────────────────────
def test_owner_proof_fail_quarantine(summary: dict) -> None:
    """DC6: 비-AUTHORITATIVE verify_fn → actual == 'QUARANTINE'."""
    checks = summary["checks"]
    dc6 = next(c for c in checks if "owner_proof_fail" in c["name"])
    assert dc6["actual"] == "QUARANTINE", (
        f"DC6 actual={dc6['actual']!r}"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 11: DC10 sealed_key_missing → fail-closed, wake 0
# ─────────────────────────────────────────────────────────────────────────────
def test_sealed_key_missing_failclosed(summary: dict) -> None:
    """DC10: sealed_key_loader=None → actual == 'SEALED_KEY_MISSING', wake 0."""
    checks = summary["checks"]
    dc10 = next(c for c in checks if "sealed_key_missing" in c["name"])
    assert dc10["actual"] == "SEALED_KEY_MISSING", (
        f"DC10 actual={dc10['actual']!r}"
    )
    # fail-closed: pickup 실 발사 없음
    assert dc10["pickup_calls"] == 0, (
        f"DC10 pickup_calls={dc10['pickup_calls']} (expected 0, fail-closed)"
    )


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 12: make_isolated_root → canonical 이 아닌 temp dir
# ─────────────────────────────────────────────────────────────────────────────
def test_make_isolated_root_not_canonical() -> None:
    """make_isolated_root() 반환 경로가 canonical root 아래가 아님."""
    CANONICAL_PREFIX = "/home/jay/workspace"
    root = chk.make_isolated_root()
    try:
        assert not root.startswith(CANONICAL_PREFIX), (
            f"make_isolated_root() 가 canonical 경로를 반환함: {root}"
        )
        # 실제로 디렉토리가 생성됐는지 확인
        import os
        assert os.path.isdir(root), f"temp dir 미생성: {root}"
    finally:
        shutil.rmtree(root, ignore_errors=True)


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 13: main(["--json"]) == 0, JSON 파싱 가능
# ─────────────────────────────────────────────────────────────────────────────
def test_main_exit_zero(capsys: pytest.CaptureFixture) -> None:
    """main(['--json']) == 0, stdout 이 유효한 JSON."""
    exit_code = chk.main(["--json"])
    captured = capsys.readouterr()
    assert exit_code == 0, (
        f"main exit_code={exit_code} (expected 0). stdout={captured.out[:200]}"
    )
    # JSON 파싱 가능 확인
    parsed = json.loads(captured.out)
    assert isinstance(parsed, dict), "stdout 이 dict JSON 이 아님"
    assert "all_pass" in parsed
    assert parsed["all_pass"] is True


# ─────────────────────────────────────────────────────────────────────────────
# 테스트 14: summary JSON 에 raw key prefix 없음
# ─────────────────────────────────────────────────────────────────────────────
def test_no_real_anu_key_in_summary(summary: dict) -> None:
    """summary JSON 에 'c119085' 부분문자열 없음 (raw key 노출 0)."""
    dump = json.dumps(summary)
    # 실 키 prefix 노출 탐지 (리터럴을 분리 조합해 작성)
    key_prefix = "c" + "1" + "1" + "9" + "0" + "8" + "5"
    assert key_prefix not in dump, (
        "summary JSON 에 ANU key prefix 가 노출됨"
    )
