"""tests/regression/test_dependency_wait_classification.py

task-2614 Track E — 실제 batch 사례 2608(선언된 dependency edge 부분 충족)
을 fixture 로 박제하고 Track D ``anu_v3.batch_dependency_classifier`` 를
read-only import 하여 WAITING_FOR_DEPENDENCY 가 봇 미수신 사고
(DISPATCH_NOT_RECEIVED)와 ENUM 차원 분리·상호배타로 보존되는지 검증한다
(재발 방지).

ANTI-CONFLATION INVARIANT(I1/I2/I3) 재발 방지: gate 미충족 정상 보류는
절대 incident/blocking 으로 격상되지 않는다. 상수 분류기는 정상보류 ↔
사고 동시 만족 불가(mock-only FAIL).
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

WORKSPACE = Path(__file__).resolve().parent.parent.parent
if str(WORKSPACE) not in sys.path:
    sys.path.insert(0, str(WORKSPACE))

import anu_v3.batch_dependency_classifier as BDC  # noqa: E402
from anu_v3.batch_dependency_classifier import (  # noqa: E402
    DISPATCH_NOT_RECEIVED,
    WAITING_FOR_DEPENDENCY,
    ConflationError,
    TrackContext,
    classify_batch,
    classify_track,
)

FIXTURES = WORKSPACE / "memory" / "fixtures"


def _load(case: str) -> dict:
    return json.loads(
        (FIXTURES / f"task-2614.case-{case}.json").read_text(encoding="utf-8")
    )


def _ctx(d: dict) -> TrackContext:
    allowed = TrackContext.__dataclass_fields__.keys()
    return TrackContext(**{k: v for k, v in d.items() if k in allowed})


def test_case_2608_is_waiting_for_dependency_not_incident() -> None:
    fx = _load("2608")
    exp = fx["expected"]
    tc = classify_track(_ctx(fx["track_d_context"]))
    assert tc.verdict == exp["track_d_verdict"] == WAITING_FOR_DEPENDENCY
    assert tc.is_incident is exp["track_d_is_incident"] is False
    assert (
        tc.is_blocking_for_adjudicator
        is exp["track_d_is_blocking_for_adjudicator"]
        is False
    )
    assert tc.gate_satisfied is exp["track_d_gate_satisfied"] is False
    assert tc.upstream_unmet == exp["track_d_upstream_unmet"]


def test_case_2608_distinct_from_dispatch_not_received() -> None:
    """2608 정상 보류는 봇 미수신 사고와 ENUM 차원 분리·상호배타.

    by-design 레코드는 생성 가능(WAITING_FOR_DEPENDENCY), 그러나 동일
    상태를 DISPATCH_NOT_RECEIVED 로 재표현하려는 시도는 ConflationError."""
    fx = _load("2608")
    tc = classify_track(_ctx(fx["track_d_context"]))
    rec = tc.as_not_started_record("declared dependency edge partially met")
    assert rec["verdict"] == WAITING_FOR_DEPENDENCY
    assert rec["is_incident"] is False
    assert rec["distinct_from_dispatch_not_received"]["asserted"] is True
    assert tc.verdict != DISPATCH_NOT_RECEIVED


def test_dispatch_not_received_positive_control_is_incident() -> None:
    """gate 충족 ∧ dispatch fired ∧ 봇 미수신 → DISPATCH_NOT_RECEIVED 사고."""
    tc = classify_track(
        TrackContext(
            track_id="t-incident",
            task_id="task-ctrl",
            gate_kind="NONE",
            declared_upstream=[],
            dispatch_fired=True,
            dispatch_receipt=False,
        )
    )
    assert tc.verdict == DISPATCH_NOT_RECEIVED
    assert tc.is_incident is True
    assert tc.is_blocking_for_adjudicator is True


def test_anti_conflation_invariant_i2_holds_on_batch() -> None:
    """batch 분류에서 gate 미충족(2608)은 blocking_verdicts 에 포함되지
    않는다 — adjudicator 가 정상 보류를 HOLD 로 격상하지 않음."""
    fx = _load("2608")
    out = classify_batch([_ctx(fx["track_d_context"])], batch_id="task-2614-E")
    assert out["anti_conflation_invariant_held"] is True
    assert out["adjudicator_input_contract"]["blocking_verdicts"] == [
        DISPATCH_NOT_RECEIVED
    ]
    assert out["summary"]["incident_count"] == 0
    assert out["summary"]["blocking_for_adjudicator_count"] == 0


def test_mock_only_would_fail_meta_guard() -> None:
    """상수 분류기는 2608 정상보류(WAITING_FOR_DEPENDENCY) 와 사고
    대조군(DISPATCH_NOT_RECEIVED)을 동시에 만족할 수 없다."""
    fx = _load("2608")
    waiting = classify_track(_ctx(fx["track_d_context"])).verdict
    incident = classify_track(
        TrackContext(
            track_id="t",
            task_id="task-ctrl",
            gate_kind="NONE",
            dispatch_fired=True,
            dispatch_receipt=False,
        )
    ).verdict
    assert waiting != incident
    assert {waiting, incident} == {
        WAITING_FOR_DEPENDENCY,
        DISPATCH_NOT_RECEIVED,
    }
    assert callable(BDC.classify_track)
    assert ConflationError is not None
