"""Tests for fw_server.context — Channel 2: Enriched Tool Returns."""

from __future__ import annotations

import pytest

from fw_server.context import build_framework_context, _TOOL_DIRECTIVES


# ─────────────────────────────────────────────────────────────────────
# Directive table structure
# ─────────────────────────────────────────────────────────────────────

class TestToolDirectives:
    """Validate the static directive table."""

    def test_all_core_tools_have_directives(self):
        expected = {"dusun", "validate_stage", "run_bileshke_pipeline", "check_kavaid"}
        assert expected.issubset(set(_TOOL_DIRECTIVES.keys()))

    def test_each_tool_has_nonempty_directives(self):
        for tool, dirs in _TOOL_DIRECTIVES.items():
            assert len(dirs) > 0, f"{tool} has empty directives"

    def test_directives_are_strings(self):
        for tool, dirs in _TOOL_DIRECTIVES.items():
            for d in dirs:
                assert isinstance(d, str), f"{tool} has non-string directive"

    def test_directives_contain_enforcement_language(self):
        """Every directive should use MUST/NEVER/MUST NOT."""
        enforcement = {"MUST", "NEVER", "MUST NOT"}
        for tool, dirs in _TOOL_DIRECTIVES.items():
            for d in dirs:
                upper = d.upper()
                assert any(e in upper for e in enforcement), (
                    f"{tool} directive missing enforcement language: {d[:60]}…"
                )


# ─────────────────────────────────────────────────────────────────────
# Basic return structure
# ─────────────────────────────────────────────────────────────────────

class TestBasicStructure:
    """build_framework_context returns a well-formed dict."""

    def test_returns_dict(self):
        result = build_framework_context("dusun")
        assert isinstance(result, dict)

    def test_minimal_call_has_directives(self):
        result = build_framework_context("dusun")
        assert "directives" in result
        assert len(result["directives"]) > 0

    def test_unknown_tool_returns_dict_without_directives(self):
        result = build_framework_context("unknown_tool")
        assert isinstance(result, dict)
        assert "directives" not in result

    def test_no_extra_keys_on_minimal_call(self):
        result = build_framework_context("unknown_tool")
        # With no kwargs and unknown tool, should be empty dict
        assert len(result) == 0


# ─────────────────────────────────────────────────────────────────────
# Grade ceiling enforcement
# ─────────────────────────────────────────────────────────────────────

class TestGradeCeiling:

    def test_grade_sets_ceiling(self):
        result = build_framework_context("dusun", grade="Tasdik")
        assert result["grade_ceiling"] == "Tasdik"
        assert result["grade_ceiling_source"] == "dusun"

    def test_grade_inflation_warning_present(self):
        result = build_framework_context("dusun", grade="Tasavvur")
        assert "grade_inflation_warning" in result
        assert "Tasavvur" in result["grade_inflation_warning"]

    def test_no_grade_no_ceiling_keys(self):
        result = build_framework_context("dusun")
        assert "grade_ceiling" not in result
        assert "grade_inflation_warning" not in result


# ─────────────────────────────────────────────────────────────────────
# Gate enforcement
# ─────────────────────────────────────────────────────────────────────

class TestGateEnforcement:

    def test_fail_gate_sets_enforcement(self):
        result = build_framework_context("validate_stage", gate="FAIL")
        assert result["gate_verdict"] == "FAIL"
        assert "MUST revise" in result["gate_enforcement"]

    def test_warn_gate_sets_enforcement(self):
        result = build_framework_context("validate_stage", gate="WARN")
        assert result["gate_verdict"] == "WARN"
        assert "MUST" in result["gate_enforcement"]
        assert "acknowledge" in result["gate_enforcement"]

    def test_pass_gate_no_enforcement(self):
        result = build_framework_context("validate_stage", gate="PASS")
        assert result["gate_verdict"] == "PASS"
        assert "gate_enforcement" not in result

    def test_no_gate_no_verdict(self):
        result = build_framework_context("validate_stage")
        assert "gate_verdict" not in result


# ─────────────────────────────────────────────────────────────────────
# Composite score / KV₄ bound
# ─────────────────────────────────────────────────────────────────────

class TestCompositeScore:

    def test_normal_score_no_violation(self):
        result = build_framework_context("run_bileshke_pipeline", composite_score=0.72)
        assert result["composite_score"] == 0.72
        assert "kv4_violation" not in result

    def test_boundary_score_triggers_kv4(self):
        result = build_framework_context("run_bileshke_pipeline", composite_score=0.95)
        assert "kv4_violation" in result
        assert "CRITICAL" in result["kv4_violation"]

    def test_above_boundary_triggers_kv4(self):
        result = build_framework_context("run_bileshke_pipeline", composite_score=0.99)
        assert "kv4_violation" in result

    def test_no_score_no_composite_key(self):
        result = build_framework_context("run_bileshke_pipeline")
        assert "composite_score" not in result


# ─────────────────────────────────────────────────────────────────────
# Kavaid summary
# ─────────────────────────────────────────────────────────────────────

class TestKavaidSummary:

    def test_all_pass_no_warning(self):
        result = build_framework_context("check_kavaid", kavaid_pass_count=8)
        assert result["kavaid_pass_count"] == 8
        assert "kavaid_warning" not in result

    def test_partial_pass_has_warning(self):
        result = build_framework_context("check_kavaid", kavaid_pass_count=6)
        assert "kavaid_warning" in result
        assert "6/8" in result["kavaid_warning"]

    def test_zero_pass_has_warning(self):
        result = build_framework_context("check_kavaid", kavaid_pass_count=0)
        assert "kavaid_warning" in result
        assert "0/8" in result["kavaid_warning"]


# ─────────────────────────────────────────────────────────────────────
# Anomalies
# ─────────────────────────────────────────────────────────────────────

class TestAnomalies:

    def test_anomalies_set_count_and_directive(self):
        anomalies = ["missing coverage", "grade mismatch"]
        result = build_framework_context("validate_stage", anomalies=anomalies)
        assert result["anomaly_count"] == 2
        assert "MUST NOT smooth" in result["anomaly_directive"]

    def test_empty_anomalies_no_keys(self):
        result = build_framework_context("validate_stage", anomalies=[])
        assert "anomaly_count" not in result
        assert "anomaly_directive" not in result

    def test_none_anomalies_no_keys(self):
        result = build_framework_context("validate_stage", anomalies=None)
        assert "anomaly_count" not in result


# ─────────────────────────────────────────────────────────────────────
# Session awareness (Channel 3 integration)
# ─────────────────────────────────────────────────────────────────────

class TestSessionAwareness:

    def test_session_context_injected(self):
        session = {"call_count": 3, "grade_ceiling": "Tasdik"}
        result = build_framework_context("dusun", session_context=session)
        assert result["session"] == session

    def test_no_session_no_key(self):
        result = build_framework_context("dusun")
        assert "session" not in result


# ─────────────────────────────────────────────────────────────────────
# Cascade status
# ─────────────────────────────────────────────────────────────────────

class TestCascadeStatus:

    def test_nominal_cascade_no_directive(self):
        cascade = {"degradation_level": "NOMINAL", "has_failure": False}
        result = build_framework_context("dusun", cascade_status=cascade)
        assert result["cascade"] == cascade
        assert "cascade_directive" not in result

    def test_degraded_cascade_has_directive(self):
        cascade = {"degradation_level": "DEGRADED", "has_warning": True}
        result = build_framework_context("validate_stage", cascade_status=cascade)
        assert "cascade_directive" in result
        assert "DEGRADED" in result["cascade_directive"]

    def test_critical_cascade_has_directive(self):
        cascade = {"degradation_level": "CRITICAL", "has_failure": True}
        result = build_framework_context("validate_stage", cascade_status=cascade)
        assert "CRITICAL" in result["cascade_directive"]


# ─────────────────────────────────────────────────────────────────────
# KV₃ compliance — never raises
# ─────────────────────────────────────────────────────────────────────

class TestKV3Compliance:
    """The context builder MUST never raise — returns stub on error."""

    def test_bad_session_context_type(self):
        """Even if session_context is a weird type, should not crash."""
        result = build_framework_context(
            "dusun",
            session_context="not_a_dict",  # type: ignore
        )
        # Should succeed — strings are valid values for the dict
        assert isinstance(result, dict)

    def test_none_tool_name(self):
        """None tool_name might cause issues in dict lookup."""
        result = build_framework_context(None)  # type: ignore
        assert isinstance(result, dict)

    def test_negative_kavaid_count(self):
        result = build_framework_context("check_kavaid", kavaid_pass_count=-1)
        assert isinstance(result, dict)
        assert result["kavaid_pass_count"] == -1


# ─────────────────────────────────────────────────────────────────────
# Full payload assembly
# ─────────────────────────────────────────────────────────────────────

class TestFullPayload:
    """Test with all parameters populated."""

    def test_all_fields_present(self):
        result = build_framework_context(
            "validate_stage",
            grade="Tasdik",
            gate="WARN",
            composite_score=0.78,
            kavaid_pass_count=7,
            anomalies=["low coverage on lens 5"],
            session_context={"call_count": 2, "grade_ceiling": "Tasdik"},
            cascade_status={"degradation_level": "DEGRADED", "has_warning": True},
        )
        assert result["grade_ceiling"] == "Tasdik"
        assert result["gate_verdict"] == "WARN"
        assert result["composite_score"] == 0.78
        assert result["kavaid_pass_count"] == 7
        assert "kavaid_warning" in result  # 7 < 8
        assert result["anomaly_count"] == 1
        assert "session" in result
        assert "cascade" in result
        assert "cascade_directive" in result
        assert "directives" in result

    def test_error_key_absent_on_success(self):
        result = build_framework_context("dusun", grade="Tasdik")
        assert "error" not in result
