"""CP-9 Phase 7 — strategy_registry.from_score_card bridge tests.

Verifies the score-card → FailureMode factory function appended to
`recoil/pipeline/orchestrator/strategy_registry.py` per Phase 1 audit
§ 12f items 4-7.

Mapping under test (LOCKED — copy of Phase 1 audit § 12f item 5):

  panel_score is None  →  (FailureMode.UNKNOWN, 0.0)
  panel_score >= 0.7   →  (FailureMode.NONE,    panel_score)
  panel_score >= 0.4   →  (FailureMode.UNKNOWN, panel_score)  # mid-band
  panel_score <  0.4   →  (FailureMode.IDENTITY_DRIFT, 1.0 - panel_score)

Boundary cases at 0.4 and 0.7 are explicitly tested. Uses ONLY existing
FailureMode enum members — no invented modes (CRITIC_FAIL_HARD etc.
do not exist).
"""

import sys
import pathlib
import inspect

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent.parent.parent))
from recoil.core.paths import ensure_pipeline_importable  # noqa: E402

ensure_pipeline_importable()

from recoil.core.critic import FailureMode  # noqa: E402
from orchestrator.strategy_registry import (  # noqa: E402
    from_score_card,
    detect_failure_mode,
)


# ── Required mapping cases (per spec) ────────────────────────────────


def test_panel_score_none_returns_unknown_zero_confidence() -> None:
    mode, conf = from_score_card({"panel_score": None})
    assert mode == FailureMode.UNKNOWN
    assert conf == 0.0


def test_panel_score_high_returns_none_with_passthrough_confidence() -> None:
    mode, conf = from_score_card({"panel_score": 0.9})
    assert mode == FailureMode.NONE
    assert conf == 0.9


def test_panel_score_mid_returns_unknown_with_passthrough_confidence() -> None:
    mode, conf = from_score_card({"panel_score": 0.5})
    assert mode == FailureMode.UNKNOWN
    assert conf == 0.5


def test_panel_score_low_returns_identity_drift_with_inverted_confidence() -> None:
    mode, conf = from_score_card({"panel_score": 0.2})
    assert mode == FailureMode.IDENTITY_DRIFT
    # confidence = 1.0 - panel_score
    assert conf == 0.8


# ── Boundary cases at 0.4 and 0.7 ────────────────────────────────────


def test_boundary_at_0_7_inclusive_returns_none() -> None:
    """panel_score == 0.7 is the lower bound of the NONE band (>= 0.7)."""
    mode, conf = from_score_card({"panel_score": 0.7})
    assert mode == FailureMode.NONE
    assert conf == 0.7


def test_just_below_0_7_returns_unknown_mid_band() -> None:
    mode, conf = from_score_card({"panel_score": 0.6999})
    assert mode == FailureMode.UNKNOWN
    assert conf == 0.6999


def test_boundary_at_0_4_inclusive_returns_unknown_mid_band() -> None:
    """panel_score == 0.4 is the lower bound of the UNKNOWN mid-band (>= 0.4)."""
    mode, conf = from_score_card({"panel_score": 0.4})
    assert mode == FailureMode.UNKNOWN
    assert conf == 0.4


def test_just_below_0_4_returns_identity_drift() -> None:
    mode, conf = from_score_card({"panel_score": 0.3999})
    assert mode == FailureMode.IDENTITY_DRIFT
    # confidence = 1.0 - 0.3999 = 0.6001 (within float rounding)
    assert abs(conf - (1.0 - 0.3999)) < 1e-9


# ── Edge values ──────────────────────────────────────────────────────


def test_score_zero_returns_identity_drift_with_full_confidence() -> None:
    mode, conf = from_score_card({"panel_score": 0.0})
    assert mode == FailureMode.IDENTITY_DRIFT
    assert conf == 1.0


def test_score_one_returns_none_with_full_confidence() -> None:
    mode, conf = from_score_card({"panel_score": 1.0})
    assert mode == FailureMode.NONE
    assert conf == 1.0


# ── Return shape mirrors detect_failure_mode ─────────────────────────


def test_return_shape_is_tuple_failure_mode_float() -> None:
    """Mirrors detect_failure_mode(...) return shape so callers can swap inputs."""
    result = from_score_card({"panel_score": 0.5})
    assert isinstance(result, tuple)
    assert len(result) == 2
    mode, conf = result
    assert isinstance(mode, FailureMode)
    assert isinstance(conf, float)


# ── Tolerates extra keys (forward compat with full ScoreCard shape) ──


def test_full_scorecard_shape_tolerated() -> None:
    """Function reads only panel_score; extra ScoreCard keys are ignored."""
    full_card = {
        "panel_id": "image_panel_v1",
        "panel_score": 0.85,
        "panel_warnings": [],
        "judges": [{"judge_id": "j1", "score": 0.85}],
        "aggregation": "median",
        "panel_cost_usd": 0.0023,
    }
    mode, conf = from_score_card(full_card)
    assert mode == FailureMode.NONE
    assert conf == 0.85


# ── Does not invent enum members ─────────────────────────────────────


def test_only_existing_failure_mode_members_returned() -> None:
    """from_score_card must use only FailureMode members that already exist."""
    valid_members = {m for m in FailureMode}
    for score in [None, 0.0, 0.2, 0.4, 0.5, 0.6999, 0.7, 0.85, 1.0]:
        mode, _ = from_score_card({"panel_score": score})
        assert mode in valid_members, (
            f"from_score_card returned non-existent FailureMode for "
            f"panel_score={score}: {mode!r}"
        )


def test_critic_fail_hard_does_not_exist() -> None:
    """Phase 1 audit § 12f item 4: CRITIC_FAIL_HARD was a phantom from
    the BUILD_SPEC body and is NOT a valid FailureMode member."""
    member_names = {m.name for m in FailureMode}
    assert "CRITIC_FAIL_HARD" not in member_names
    assert "CRITIC_FAIL_SOFT" not in member_names


# ── detect_failure_mode signature unchanged (Phase 7 hard gate) ──────


def test_detect_failure_mode_signature_byte_stable() -> None:
    """Phase 1 audit § 12f items 6-7: detect_failure_mode body + signature
    are NOT modified in CP-9 Phase 7. Substrate-only addition."""
    sig = inspect.signature(detect_failure_mode)
    params = list(sig.parameters.keys())
    assert params == ["pass_result", "coverage_pass"]
    # Return annotation preserved.
    assert "FailureMode" in str(sig.return_annotation) or "tuple" in str(
        sig.return_annotation
    )
