# -*- coding: utf-8 -*-
"""regression — task-2553+60 LIVE READ-ONLY OPERATIONAL PILOT.

9-stage 실 entrypoint 자동 결선을 *증명* 한다. 본 테스트는 실
entrypoint 를 직접 호출(mock-only/문서-only 금지)하며 read-only —
write 0, 실 dispatch 0, ledger byte-0 (do_write=False).

mock-only FAIL: 음성 케이스로 (i) 미매핑 goal_type fail-closed,
(ii) executor self key callback 구조적 차단, (iii) dead-man/fallback
진행 트리거 하드 FAIL, (iv) self-chain QUARANTINED 를 검증한다.
"""
from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))


@pytest.fixture(scope="module")
def pilot():
    spec = importlib.util.spec_from_file_location(
        "_p60_pilot", ROOT / "scripts/run_operational_pilot_2553plus60.py"
    )
    assert spec and spec.loader
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


@pytest.fixture(scope="module")
def run(pilot):
    # read-only — do_write=False: ledger byte-0, 실 dispatch 0.
    return pilot.run_pilot(do_write=False)


def test_expected_files_allowlist_exact(pilot):
    """§6 expected_files allowlist = 정확히 회장 명세 14종."""
    assert pilot.EXPECTED_FILES == (
        "memory/events/task-2553+60.pilot-run.json",
        "memory/events/task-2553+60.selected-profile.json",
        "memory/events/task-2553+60.gate-hold-decision.json",
        "memory/events/task-2553+60.execution-result.json",
        "memory/events/task-2553+60.independent-collector-result.json",
        "memory/events/task-2553+60.runtime-event-loop-result.json",
        "memory/events/task-2553+60.enactor-result.json",
        "memory/events/task-2553+60.consolidated-result.json",
        "scripts/run_operational_pilot_2553plus60.py",
        "tests/regression/test_operational_pilot_2553plus60.py",
        "memory/events/task-2553+60.decision.json",
        "memory/events/task-2553+60.result.json",
        "memory/reports/task-2553+60.md",
        "memory/events/callback_4tuple_index.jsonl",
    )
    # 정적/ledger 는 runner 직접 write 안 함 (byte-0 / +53 경유).
    for static in (
        "scripts/run_operational_pilot_2553plus60.py",
        "tests/regression/test_operational_pilot_2553plus60.py",
        "memory/reports/task-2553+60.md",
        "memory/events/callback_4tuple_index.jsonl",
    ):
        assert static not in pilot.EMIT_FILES


def test_callback_owner_is_independent_anu_key(pilot):
    """callback owner = 독립 ANU key; executor self key 절대 금지."""
    assert pilot.ANU_CALLBACK_KEY == "c119085addb0f8b7"
    assert pilot.EXECUTOR_SELF_KEY == "a999e2ea4c06d2fb"
    assert pilot.ANU_CALLBACK_KEY != pilot.EXECUTOR_SELF_KEY


def test_nine_stage_auto_chained(run):
    """9-stage 가 한 입력 short read-only goal 으로 자동 결선."""
    ev = run["evaluation"]
    assert ev["auto_chained_9_stage"] is True
    assert ev["all_passed"] is True
    for name, ok in ev["checks"].items():
        assert ok is True, f"{name} failed"


def test_stage1_profile_auto_resolved_without_chair_detail(run):
    """STAGE1 — 회장 미지정 gate/HOLD/allowed/forbidden 자동 산출."""
    s1 = run["stages"]["stage1"]
    assert s1["status"] == "RESOLVED"
    assert s1["profile_bound"] is True
    # fail-closed: 미매핑 goal_type 은 RESOLVED 가 아님.
    assert s1["unknown_goal_type_resolution"].get("status") != "RESOLVED"


def test_stage2_anu_key_contract_enforced(run):
    """STAGE2 — self key callback 구조적 차단(CallbackRegistrationBlocked)."""
    s2 = run["stages"]["stage2"]
    assert s2["anu_key_registration_allowed"] is True
    assert s2["self_key_registration_allowed"] is False
    assert s2["self_key_assert_raised_CallbackRegistrationBlocked"] is True


def test_stage4_executor_self_collector_forbidden(run):
    """STAGE4 — executor self collector = SELF_COLLECTOR_FORBIDDEN."""
    s4 = run["stages"]["stage4"]
    assert s4["executor_self_collector_forbidden"] is True
    assert s4["independent_anu_collector_ok"] is True


def test_stage5_independent_anu_authoritative_self_chain_quarantined(run):
    """STAGE5 — authoritative 는 독립 ANU 만; self-chain QUARANTINED."""
    s5 = run["stages"]["stage5"]
    assert s5["authoritative_pass"] is True
    assert s5["self_chain_quarantined"] is True
    assert s5["executor_self_collector"] is False


def test_stage5_plus58_fallback_acceptance_operational_pass(run):
    """STAGE5 — +58 기준 (b) NON_BLOCKING schema-valid·binding 일치 →
    OPERATIONAL_PASS (DUPLICATE-only 아님)."""
    fb = run["stages"]["stage5"]["fallback_acceptance"]
    assert fb["non_blocking_mark_schema_valid"] is True
    assert fb["non_blocking_mark_errors"] == []
    assert fb["semantic_binding_ok"] is True
    assert fb["verdict"]["verdict"] == "OPERATIONAL_PASS"
    assert "b" in fb["verdict"]["satisfied_criterion"]


def test_stage6_durable_success_writeback_53(run):
    """STAGE6 — +53 durable-success additive write-back (read-only eval)."""
    s6 = run["stages"]["stage6"]
    assert s6["durable_success"] is True
    assert s6["writeback_classification"] in (
        "DURABLE_SUCCESS_WRITTEN",
        "WRITEBACK_IDEMPOTENT_SKIP",
    )
    assert s6["write_performed"] is False  # 본 테스트 read-only


def test_stage7_loop_detects_event_deadman_forbidden(run):
    """STAGE7 — registry append 즉시 감지; dead-man 진행 트리거 하드 FAIL."""
    s7 = run["stages"]["stage7"]
    assert s7["detected_durable_success"] is True
    neg = s7["forbidden_trigger_negative"]
    assert neg["hard_fail"] is True
    assert neg["dead_man_signal_observed_not_promoted"] is True


def test_stage8_enactor_proposal_only_fallback_forbidden(run):
    """STAGE8 — proposal-only·additive; fallback 진행 트리거 하드 FAIL."""
    s8 = run["stages"]["stage8"]
    assert s8["actual_write_performed"] is False
    assert s8["auto_executed"] is False
    assert s8["forbidden_trigger_negative"]["hard_fail"] is True


def test_self_chain_not_authoritative(run):
    """self-chain 산출은 영구 비권위 — authoritative 는 독립 ANU collector."""
    art = _build(run)
    cr = art["memory/events/task-2553+60.consolidated-result.json"]
    assert cr["self_chain_authoritative"] is False
    assert "DEFERRED_TO_INDEPENDENT_ANU_COLLECTOR" in (
        cr["authoritative_selection"]
    )


def test_subagent_usage_ledger_present(run):
    """회장 §4(2) — team result contract + subagent usage ledger 강제."""
    art = _build(run)
    res = art["memory/events/task-2553+60.result.json"]
    led = res["subagent_usage_ledger"]
    assert led["subagents_used"] == []
    assert "subagent 미사용" in led["declaration"]
    assert led["external_boundary_contract"]["team_result_contract"] is True
    assert (
        led["external_boundary_contract"][
            "executor_self_dispatch_delegation_count"
        ]
        == 0
    )


def test_acceptance_required_inclusions(run):
    """회장 §4(1~3) 필수 포함 — fallback 기준·subagent ledger·+58 backlog."""
    art = _build(run)
    cr = art["memory/events/task-2553+60.consolidated-result.json"]
    inc = cr["acceptance_required_inclusions"]
    a1 = inc["1_fallback_after_durable_success"]
    assert a1["fallback_acceptance_verdict"] == "OPERATIONAL_PASS"
    assert a1["duplicate_only_is_not_pass"] is True
    assert inc["2_team_result_contract_subagent_ledger"]["subagents_used"] == []
    bl = inc["3_plus58_codex_microfix_backlog"]
    assert bl["blocking"] is False
    sev = sorted(i["severity"] for i in bl["items"])
    assert sev == ["HIGH", "MEDIUM", "MEDIUM"]


def test_mock_only_is_fail_real_entrypoint_required(pilot, run):
    """mock-only FAIL — 실 entrypoint 가 호출됨을 증명 (문서-only 금지)."""
    art = _build(run)
    pr = art["memory/events/task-2553+60.pilot-run.json"]
    assert pr["mock_only"] is False
    assert pr["documentation_only"] is False
    # 각 stage real_entrypoint 가 정본 모듈 경로를 명시.
    for i in range(1, 9):
        ep = run["stages"][f"stage{i}"]["real_entrypoint"]
        assert "anu_v3" in ep or "dispatch." in ep or "scripts." in ep
    # MockOnlyError 가 정의되어 있어 mock-only 거부 가능.
    assert issubclass(pilot.MockOnlyError, RuntimeError)


def _build(run):
    spec = importlib.util.spec_from_file_location(
        "_p60_pilot_b", ROOT / "scripts/run_operational_pilot_2553plus60.py"
    )
    assert spec and spec.loader
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod._build_artifacts(run)
