"""Closeout grade auto-classifier regression — task-2643 Track D.

4 enum 분류 정합 + fixture parametrized + 우선순위 doctrine 검증.
"""

import json
import sys
from pathlib import Path

import pytest

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

from utils.closeout_grade_auto_classifier import (
    CloseoutGrade,
    GradeDecision,
    classify_closeout_grade,
)

FIXTURE_ROOT = _WT / "tests" / "fixtures" / "closeout_grade"


def _load(name: str):
    base = FIXTURE_ROOT / name
    evidence = json.loads((base / "evidence.json").read_text(encoding="utf-8"))
    expected = json.loads((base / "expected.json").read_text(encoding="utf-8"))
    return evidence, expected


@pytest.mark.parametrize(
    "fixture_name",
    [
        "documented_only",
        "harness_enforced",
    ],
)
def test_fixture_grade_matches(fixture_name):
    ev, exp = _load(fixture_name)
    decision: GradeDecision = classify_closeout_grade(
        changed_files=ev["changed_files"],
        evidence_files=ev.get("evidence_files"),
        dry_run_report=ev.get("dry_run_report"),
        rollback_plan_present=ev.get("rollback_plan_present", False),
        live_settings_applied=ev.get("live_settings_applied", False),
    )
    assert decision.grade.value == exp["grade"], (
        f"[{fixture_name}] grade mismatch: got={decision.grade}, want={exp['grade']}"
    )
    if "rationale_substring" in exp:
        joined = " ".join(decision.rationale)
        assert exp["rationale_substring"] in joined, (
            f"[{fixture_name}] rationale missing {exp['rationale_substring']!r}: {decision.rationale}"
        )
    for key, want in exp["signals_assertions"].items():
        assert decision.signals.get(key) == want, (
            f"[{fixture_name}] signal {key!r} mismatch: got={decision.signals.get(key)}, want={want}"
        )


def test_4_enum_values_complete():
    """4 enum 누락 없음."""
    assert {g.value for g in CloseoutGrade} == {
        "DOCUMENTED_ONLY",
        "REGRESSION_GUARDED",
        "RUNTIME_GUARDED",
        "HARNESS_ENFORCED",
    }


def test_runtime_guarded_priority_below_harness_enforced():
    """RUNTIME signal 모두 있어도 live_settings_applied=True 면 HARNESS_ENFORCED."""
    d = classify_closeout_grade(
        changed_files=[
            "hooks/h.py",
            "memory/specs/staged_settings_template_x.json",
            "memory/events/x_dry_run_report.json",
        ],
        dry_run_report={"case_summary": {"deny_count": 1, "allow_count": 1}},
        rollback_plan_present=True,
        live_settings_applied=True,
    )
    assert d.grade == CloseoutGrade.HARNESS_ENFORCED


def test_runtime_guarded_requires_all_three_signals():
    """staged hook 있지만 rollback plan 없으면 RUNTIME_GUARDED 미달 → REGRESSION (if regression) or DOCUMENTED."""
    # rollback plan 없음 + dry-run report 없음
    d = classify_closeout_grade(
        changed_files=["hooks/h.py", "memory/specs/staged_settings_template_x.json"],
        rollback_plan_present=False,
    )
    assert d.grade != CloseoutGrade.RUNTIME_GUARDED


def test_regression_guarded_when_only_regression_tests_added():
    d = classify_closeout_grade(
        changed_files=["tests/regression/test_x.py"],
        rollback_plan_present=False,
    )
    assert d.grade == CloseoutGrade.REGRESSION_GUARDED


def test_documented_only_when_only_docs_changed():
    d = classify_closeout_grade(
        changed_files=[
            "memory/feedback_x.md",
            "memory/specs/spec_y.md",
            "memory/events/event_z.json",
        ],
    )
    assert d.grade == CloseoutGrade.DOCUMENTED_ONLY
    assert d.signals["doc_only_paths"] is True


def test_classifier_doctrine_manual_declaration_not_authoritative():
    """사람이 'HARNESS_ENFORCED' 라고 수동 선언해도 입력 signal 만으로 산정한다.

    e.g. live_settings_applied=False 인 한 절대 HARNESS_ENFORCED 안 됨.
    """
    d = classify_closeout_grade(
        changed_files=["hooks/h.py"],
        live_settings_applied=False,
    )
    assert d.grade != CloseoutGrade.HARNESS_ENFORCED


def test_dry_run_report_must_have_both_deny_and_allow_counts():
    """dry-run report 가 deny_count or allow_count 0 이면 RUNTIME_GUARDED 미달."""
    d = classify_closeout_grade(
        changed_files=[
            "hooks/h.py",
            "memory/specs/staged_settings_template_x.json",
            "memory/events/x_dry_run_report.json",
        ],
        dry_run_report={"case_summary": {"deny_count": 0, "allow_count": 5}},
        rollback_plan_present=True,
    )
    # has_dry_run_report=False → 한 단계 아래로 떨어짐
    assert d.grade != CloseoutGrade.RUNTIME_GUARDED
