"""
tests/test_steps_1_6.py — Comprehensive tests for all features added in Steps 1–6.

Step 1: _coerce helper (serialization fix)
Step 2: validate_stage visibility (tested via import/doc)
Step 3: Content-aware validate_stage (FOL receives text)
Step 4: HCP auto-ingest in validate_stage
Step 5: SC-3 tool guide in get_framework_summary
Step 6: _suggest_tools_for_content + sc3_tool_suggestions in hcp_query

Design:
  KV₇: Each test is independent — no shared state.
  AX56: Never returns Hakkalyakîn.
  T6/KV₄: Composite always < 1.0.
"""

from __future__ import annotations

import json
import pytest


# ═══════════════════════════════════════════════════════════════════════
# STEP 1: _coerce helper — type coercion with backward compat
# ═══════════════════════════════════════════════════════════════════════


class TestCoerceNone:
    """_coerce returns default when value is None or empty string."""

    def test_none_returns_default_none(self):
        from fw_server.server import _coerce
        assert _coerce(None, dict) is None

    def test_none_returns_custom_default(self):
        from fw_server.server import _coerce
        assert _coerce(None, dict, {}) == {}

    def test_empty_string_returns_default(self):
        from fw_server.server import _coerce
        assert _coerce("", list, []) == []

    def test_empty_string_returns_none_default(self):
        from fw_server.server import _coerce
        assert _coerce("", dict) is None


class TestCoerceNativePassthrough:
    """_coerce passes through values already of the target type."""

    def test_dict_passthrough(self):
        from fw_server.server import _coerce
        d = {"key": "val"}
        assert _coerce(d, dict) is d

    def test_list_passthrough(self):
        from fw_server.server import _coerce
        lst = [1, 2, 3]
        assert _coerce(lst, list) is lst

    def test_nested_dict_passthrough(self):
        from fw_server.server import _coerce
        d = {"a": {"b": [1, 2]}}
        assert _coerce(d, dict) is d

    def test_empty_dict_passthrough(self):
        from fw_server.server import _coerce
        d = {}
        assert _coerce(d, dict) is d

    def test_empty_list_passthrough(self):
        from fw_server.server import _coerce
        lst = []
        assert _coerce(lst, list) is lst


class TestCoerceJsonString:
    """_coerce parses JSON strings for backward compatibility."""

    def test_json_dict_string(self):
        from fw_server.server import _coerce
        result = _coerce('{"a": 1}', dict)
        assert result == {"a": 1}

    def test_json_list_string(self):
        from fw_server.server import _coerce
        result = _coerce("[1, 0, 1, 0, 1, 0, 1]", list)
        assert result == [1, 0, 1, 0, 1, 0, 1]

    def test_json_empty_dict_string(self):
        from fw_server.server import _coerce
        assert _coerce("{}", dict) == {}

    def test_json_empty_list_string(self):
        from fw_server.server import _coerce
        assert _coerce("[]", list) == []

    def test_json_nested_structure(self):
        from fw_server.server import _coerce
        s = '{"ontoloji": {"text": "hello"}, "fol": {}}'
        result = _coerce(s, dict)
        assert result["ontoloji"]["text"] == "hello"


class TestCoerceErrors:
    """_coerce raises TypeError on incompatible types."""

    def test_json_string_wrong_type(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError, match="Expected dict.*got list"):
            _coerce("[1, 2]", dict)

    def test_json_list_but_want_dict(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError, match="Expected dict"):
            _coerce("[]", dict)

    def test_json_dict_but_want_list(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError, match="Expected list.*got dict"):
            _coerce('{"a": 1}', list)

    def test_non_string_non_target(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError, match="Expected dict.*got int"):
            _coerce(42, dict)

    def test_invalid_json_string(self):
        from fw_server.server import _coerce
        with pytest.raises(json.JSONDecodeError):
            _coerce("not json", dict)

    def test_bool_not_dict(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError):
            _coerce(True, dict)

    def test_float_not_list(self):
        from fw_server.server import _coerce
        with pytest.raises(TypeError):
            _coerce(3.14, list)


class TestCoerceEdgeCases:
    """Edge cases for _coerce."""

    def test_whitespace_only_is_invalid_json(self):
        from fw_server.server import _coerce
        with pytest.raises(json.JSONDecodeError):
            _coerce("   ", dict)

    def test_json_string_with_unicode(self):
        from fw_server.server import _coerce
        result = _coerce('{"name": "İlmelyakîn"}', dict)
        assert result["name"] == "İlmelyakîn"

    def test_large_list(self):
        from fw_server.server import _coerce
        big = list(range(1000))
        assert _coerce(big, list) is big

    def test_json_large_list_string(self):
        from fw_server.server import _coerce
        s = json.dumps(list(range(100)))
        result = _coerce(s, list)
        assert len(result) == 100


# ═══════════════════════════════════════════════════════════════════════
# STEP 1 (cont): Native type annotations on tools
# ═══════════════════════════════════════════════════════════════════════


class TestToolNativeTypes:
    """Verify that tools accept native Python types (not just JSON strings)."""

    def test_check_kavaid_native_list(self):
        """check_kavaid accepts latife_bits as a native list."""
        from fw_server.server import check_kavaid
        result = json.loads(check_kavaid(0.5, [1, 1, 1, 1, 1, 1, 0]))
        assert "error" not in result

    def test_check_kavaid_native_dict_overrides(self):
        from fw_server.server import check_kavaid
        result = json.loads(check_kavaid(0.3, None, {"KV1": True}))
        assert "error" not in result

    def test_check_kavaid_json_string_backward_compat(self):
        """JSON strings still work via _coerce."""
        from fw_server.server import check_kavaid
        result = json.loads(check_kavaid(0.5, "[1,1,1,1,1,1,0]", None))
        assert "error" not in result

    def test_run_single_lens_native_dict(self):
        from fw_server.server import run_single_lens
        result = json.loads(run_single_lens("FOL", {"text": "AX1 test"}))
        assert "error" not in result

    def test_run_single_lens_json_string_backward_compat(self):
        from fw_server.server import run_single_lens
        result = json.loads(run_single_lens("FOL", '{"text": "AX1 test"}'))
        assert "error" not in result

    def test_run_bileshke_pipeline_native_dict(self):
        from fw_server.server import run_bileshke_pipeline
        result = json.loads(run_bileshke_pipeline({}))
        assert "error" not in result

    def test_run_bileshke_pipeline_json_string_compat(self):
        from fw_server.server import run_bileshke_pipeline
        result = json.loads(run_bileshke_pipeline("{}"))
        assert "error" not in result

    def test_hcp_ingest_native_dict_metadata(self):
        from fw_server.server import hcp_ingest
        meta = {"tool_name": "test", "content_type_hint": "test_data"}
        result = json.loads(hcp_ingest("Test text for ingest", meta))
        assert "error" not in result

    def test_hcp_ingest_json_string_metadata(self):
        from fw_server.server import hcp_ingest
        result = json.loads(hcp_ingest(
            "Test text",
            '{"tool_name": "test", "content_type_hint": "data"}'
        ))
        assert "error" not in result


# ═══════════════════════════════════════════════════════════════════════
# STEP 2: validate_stage visibility and documentation
# ═══════════════════════════════════════════════════════════════════════


class TestValidateStageVisibility:
    """Step 2: validate_stage is properly exposed and documented."""

    def test_validate_stage_importable(self):
        from fw_server.server import validate_stage
        assert callable(validate_stage)

    def test_validate_stage_has_docstring(self):
        from fw_server.server import validate_stage
        assert validate_stage.__doc__ is not None
        assert "AX4" in validate_stage.__doc__

    def test_validate_stage_in_stage_lens_map(self):
        from fw_server.server import _STAGE_LENS_MAP
        assert len(_STAGE_LENS_MAP) >= 12

    def test_validate_stage_accepts_content_param(self):
        """Step 3 added content param — verify signature."""
        import inspect
        from fw_server.server import validate_stage
        sig = inspect.signature(validate_stage)
        assert "content" in sig.parameters
        assert sig.parameters["content"].default is None


# ═══════════════════════════════════════════════════════════════════════
# STEP 3: Content-aware validate_stage
# ═══════════════════════════════════════════════════════════════════════


class TestValidateStageContent:
    """Step 3: validate_stage with content parameter."""

    def test_without_content_still_works(self):
        """Backward compat: no content = baseline behavior."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage("2", "Test summary"))
        assert "gate" in result
        assert result["composite_score"] < 1.0

    def test_with_content_returns_valid(self):
        from fw_server.server import validate_stage
        result = json.loads(validate_stage(
            "2",
            "Testing AX1 formalization",
            content="AX1 states that all being is formally ordered."
        ))
        assert "gate" in result
        assert "error" not in result
        assert result["composite_score"] < 1.0

    def test_fol_gets_content_when_stage_2(self):
        """Stage 2 uses FOL → with content, FOL should get text."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage(
            "2",
            "Layer 1 analysis",
            content="AX1 K1 Ontological Axiom: all being is ordered by Sudur."
        ))
        assert "error" not in result
        # FOL is relevant for stage 2 — score should be in results
        assert "FOL" in result.get("relevant_lens_scores", {})

    def test_content_does_not_affect_non_fol_stages(self):
        """Content only affects FOL lens — other lens scores unchanged."""
        from fw_server.server import validate_stage
        r1 = json.loads(validate_stage("4", "test"))  # Bayes stage
        r2 = json.loads(validate_stage("4", "test", content="AX1 text"))
        # Bayes score should be the same either way
        bayes1 = r1["all_lens_scores"].get("Bayes", -1)
        bayes2 = r2["all_lens_scores"].get("Bayes", -1)
        assert bayes1 == bayes2

    def test_content_with_empty_string_treated_as_none(self):
        """Empty content string = no content."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage("2", "test", content=""))
        assert "error" not in result

    def test_content_with_axiom_refs_influences_fol(self):
        """Content with many AX refs should be processed by FOL."""
        from fw_server.server import validate_stage
        content = (
            "AX1 is the ontological axiom. AX2 defines Ilim. "
            "AX3 defines Irade. AX4 defines Kudret. "
            "T3 propagation. KV1 harfi classification."
        )
        result = json.loads(validate_stage("2", "multi-axiom", content=content))
        assert "error" not in result
        # Just verify valid JSON response
        assert "gate" in result

    @pytest.mark.parametrize("stage", ["preflight", "1", "3", "5", "9a"])
    def test_content_accepted_by_all_stages(self, stage):
        """Content param accepted regardless of stage."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage(
            stage, "content test", content="Some text."
        ))
        assert "error" not in result


# ═══════════════════════════════════════════════════════════════════════
# STEP 4: HCP auto-ingest in validate_stage
# ═══════════════════════════════════════════════════════════════════════


class TestValidateStageHCPAutoIngest:
    """Step 4: validate_stage auto-ingests gate result into HCP."""

    def test_hcp_ingested_key_present(self):
        from fw_server.server import validate_stage
        result = json.loads(validate_stage("2", "HCP ingest test"))
        assert "hcp_ingested" in result

    def test_hcp_ingested_is_boolean(self):
        from fw_server.server import validate_stage
        result = json.loads(validate_stage("3", "type check"))
        assert isinstance(result["hcp_ingested"], bool)

    def test_hcp_ingested_is_true_normally(self):
        """Under normal conditions, ingest should succeed."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage("1", "should ingest"))
        assert result["hcp_ingested"] is True

    @pytest.mark.parametrize("stage", [
        "preflight", "1", "2", "3", "4", "5", "6", "7", "8", "9a"
    ])
    def test_all_stages_report_hcp_ingest(self, stage):
        from fw_server.server import validate_stage
        result = json.loads(validate_stage(stage, f"stage {stage}"))
        assert "hcp_ingested" in result

    def test_ingest_text_includes_gate_verdict(self):
        """Verify HCP has gate results by querying back."""
        from fw_server.server import validate_stage, hcp_query
        # First, do a gate
        validate_stage("2", "HCP query test stage")
        # Query HCP for gate results
        qr = json.loads(hcp_query("GATE stage=2"))
        # If HCP had data, we should get results
        if qr.get("n_results", 0) > 0:
            contents = [r["content"] for r in qr["results"]]
            found = any("GATE" in c for c in contents)
            assert found, "GATE result not found in HCP query"

    def test_multiple_stages_create_multiple_ingest_entries(self):
        """Each stage gate creates a separate HCP entry."""
        from fw_server.server import validate_stage
        # Run two different stages
        r1 = json.loads(validate_stage("preflight", "first"))
        r2 = json.loads(validate_stage("9a", "second"))
        assert r1["hcp_ingested"] is True
        assert r2["hcp_ingested"] is True


# ═══════════════════════════════════════════════════════════════════════
# STEP 5a: SC-3 tool guide in get_framework_summary
# ═══════════════════════════════════════════════════════════════════════


class TestSC3ToolGuide:
    """Step 5: get_framework_summary includes sc3_tool_guide."""

    def _get_summary(self):
        from fw_server.server import get_framework_summary
        return json.loads(get_framework_summary())

    def test_sc3_tool_guide_present(self):
        s = self._get_summary()
        assert "sc3_tool_guide" in s

    def test_sc3_tool_guide_has_purpose(self):
        guide = self._get_summary()["sc3_tool_guide"]
        assert "purpose" in guide
        assert isinstance(guide["purpose"], str)
        assert "SC-3" in guide["purpose"]

    def test_sc3_tool_guide_has_rule(self):
        guide = self._get_summary()["sc3_tool_guide"]
        assert "rule" in guide
        assert "SC-3 FAILS" in guide["rule"]

    def test_sc3_tool_guide_has_claim_to_tool(self):
        guide = self._get_summary()["sc3_tool_guide"]
        assert "claim_to_tool" in guide
        assert isinstance(guide["claim_to_tool"], list)

    def test_sc3_tool_guide_has_8_claim_types(self):
        guide = self._get_summary()["sc3_tool_guide"]
        assert len(guide["claim_to_tool"]) == 8

    def test_each_claim_entry_has_required_fields(self):
        guide = self._get_summary()["sc3_tool_guide"]
        required = {"claim_type", "description", "tool", "call_example", "grounds"}
        for entry in guide["claim_to_tool"]:
            assert required <= set(entry.keys()), f"Missing fields in {entry}"

    @pytest.mark.parametrize("claim_type", [
        "structural_pattern",
        "convergence_or_quality",
        "constraint_satisfaction",
        "axiom_or_formal_reference",
        "inference_chain",
        "part_whole_or_holographic",
        "epistemic_grade",
        "system_health",
    ])
    def test_expected_claim_type_present(self, claim_type):
        guide = self._get_summary()["sc3_tool_guide"]
        types = [e["claim_type"] for e in guide["claim_to_tool"]]
        assert claim_type in types, f"{claim_type} missing from guide"

    def test_structural_pattern_uses_validate_stage(self):
        guide = self._get_summary()["sc3_tool_guide"]
        entry = next(e for e in guide["claim_to_tool"]
                     if e["claim_type"] == "structural_pattern")
        assert entry["tool"] == "validate_stage"

    def test_axiom_reference_uses_run_single_lens(self):
        guide = self._get_summary()["sc3_tool_guide"]
        entry = next(e for e in guide["claim_to_tool"]
                     if e["claim_type"] == "axiom_or_formal_reference")
        assert entry["tool"] == "run_single_lens"
        assert "fol" in entry["call_example"]

    def test_system_health_uses_get_framework_summary(self):
        guide = self._get_summary()["sc3_tool_guide"]
        entry = next(e for e in guide["claim_to_tool"]
                     if e["claim_type"] == "system_health")
        assert entry["tool"] == "get_framework_summary"

    def test_check_kavaid_in_guide(self):
        guide = self._get_summary()["sc3_tool_guide"]
        entry = next(e for e in guide["claim_to_tool"]
                     if e["claim_type"] == "constraint_satisfaction")
        assert entry["tool"] == "check_kavaid"

    def test_inference_chain_uses_verify_chains(self):
        guide = self._get_summary()["sc3_tool_guide"]
        entry = next(e for e in guide["claim_to_tool"]
                     if e["claim_type"] == "inference_chain")
        assert entry["tool"] == "verify_chains"

    def test_call_examples_are_nonempty_strings(self):
        guide = self._get_summary()["sc3_tool_guide"]
        for entry in guide["claim_to_tool"]:
            assert isinstance(entry["call_example"], str)
            assert len(entry["call_example"]) > 10

    def test_grounds_are_nonempty_strings(self):
        guide = self._get_summary()["sc3_tool_guide"]
        for entry in guide["claim_to_tool"]:
            assert isinstance(entry["grounds"], str)
            assert len(entry["grounds"]) > 5


class TestFrameworkSummaryStructure:
    """Verify overall get_framework_summary structure with sc3 addition."""

    def test_has_system_integration(self):
        from fw_server.server import get_framework_summary
        s = json.loads(get_framework_summary())
        assert "system_integration" in s

    def test_has_hayat_layer(self):
        from fw_server.server import get_framework_summary
        s = json.loads(get_framework_summary())
        assert "hayat_layer" in s

    def test_system_integration_before_sc3(self):
        """Both system_integration and sc3_tool_guide coexist."""
        from fw_server.server import get_framework_summary
        s = json.loads(get_framework_summary())
        assert "system_integration" in s
        assert "sc3_tool_guide" in s

    def test_module_summaries_present(self):
        from fw_server.server import get_framework_summary
        s = json.loads(get_framework_summary())
        expected = [
            "kavram_sozlugu", "mereoloji", "bileshke",
            "fol_formalizasyon", "bayes_analiz", "oyun_teorisi",
            "kategori_teorisi", "holografik",
        ]
        for mod in expected:
            assert mod in s, f"Missing module summary: {mod}"


# ═══════════════════════════════════════════════════════════════════════
# STEP 6a: _suggest_tools_for_content pattern engine
# ═══════════════════════════════════════════════════════════════════════


class TestSuggestToolsAxiomRef:
    """Pattern: axiom_reference — matches AX\\d{1,2}."""

    def test_ax1_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["According to AX1"])
        types = [s["claim_type"] for s in r]
        assert "axiom_reference" in types

    def test_ax66_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX66 ada-first strategy"])
        types = [s["claim_type"] for s in r]
        assert "axiom_reference" in types

    def test_ax_case_insensitive(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["per ax17, names are realia"])
        types = [s["claim_type"] for s in r]
        assert "axiom_reference" in types

    def test_suggestion_has_correct_tool(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX37 holographic"])
        axref = next(s for s in r if s["claim_type"] == "axiom_reference")
        assert axref["tool"] == "run_single_lens"

    def test_no_false_positive_for_AXE(self):
        """'AXE' should not match AX\\d."""
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["I used an AXE to chop wood"])
        types = [s["claim_type"] for s in r]
        assert "axiom_reference" not in types


class TestSuggestToolsTheoremRef:
    """Pattern: theorem_reference — matches T\\d{1,2}."""

    def test_t6_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["T6 convergence bound"])
        types = [s["claim_type"] for s in r]
        assert "theorem_reference" in types

    def test_t17_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Per T17, coverage is 6/7"])
        types = [s["claim_type"] for s in r]
        assert "theorem_reference" in types

    def test_t_case_sensitive(self):
        """Theorem regex is case-sensitive (T, not t)."""
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["t6 lowercase"])
        types = [s["claim_type"] for s in r]
        # Should NOT match since T\d uses default (case-sensitive)
        assert "theorem_reference" not in types


class TestSuggestToolsInferenceChain:
    """Pattern: inference_chain — matches C[1-7]."""

    def test_c1_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Chain C1 traces ontological poverty"])
        types = [s["claim_type"] for s in r]
        assert "inference_chain" in types

    def test_c7_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["C7 score bound chain"])
        types = [s["claim_type"] for s in r]
        assert "inference_chain" in types

    def test_c8_not_detected(self):
        """Only C1-C7 are valid chains."""
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["C8 does not exist"])
        types = [s["claim_type"] for s in r]
        assert "inference_chain" not in types

    def test_suggests_verify_chains(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["C3 holographic chain"])
        chain = next(s for s in r if s["claim_type"] == "inference_chain")
        assert chain["tool"] == "verify_chains"


class TestSuggestToolsKavaidRef:
    """Pattern: kavaid_reference — matches KV with subscript or digit."""

    def test_kv1_subscript_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["KV₁ harfi classification"])
        types = [s["claim_type"] for s in r]
        assert "kavaid_reference" in types

    def test_kv4_digit_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["KV4 convergence bound"])
        types = [s["claim_type"] for s in r]
        assert "kavaid_reference" in types

    def test_kv7_subscript_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["KV₇ independence"])
        types = [s["claim_type"] for s in r]
        assert "kavaid_reference" in types

    def test_suggests_check_kavaid(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Check KV₄ compliance"])
        kv = next(s for s in r if s["claim_type"] == "kavaid_reference")
        assert kv["tool"] == "check_kavaid"


class TestSuggestToolsConvergence:
    """Pattern: convergence_claim — matches convergence/bileshke/composite."""

    def test_convergence_word(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["The convergence score is 0.8"])
        types = [s["claim_type"] for s in r]
        assert "convergence_claim" in types

    def test_bileshke_word(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["bileshke composite result"])
        types = [s["claim_type"] for s in r]
        assert "convergence_claim" in types

    def test_composite_score_word(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["composite score = 0.72"])
        types = [s["claim_type"] for s in r]
        assert "convergence_claim" in types

    def test_case_insensitive_convergence(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["CONVERGENCE CLAIM"])
        types = [s["claim_type"] for s in r]
        assert "convergence_claim" in types

    def test_suggests_bileshke_pipeline(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["convergence"])
        c = next(s for s in r if s["claim_type"] == "convergence_claim")
        assert c["tool"] == "run_bileshke_pipeline"


class TestSuggestToolsHolographic:
    """Pattern: holographic_claim — matches holographic/B01-B22/KV6/seed."""

    def test_holographic_word(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["holographic structure detected"])
        types = [s["claim_type"] for s in r]
        assert "holographic_claim" in types

    def test_b01_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["B01 dimension score"])
        types = [s["claim_type"] for s in r]
        assert "holographic_claim" in types

    def test_b22_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["B22 celali dimension"])
        types = [s["claim_type"] for s in r]
        assert "holographic_claim" in types

    def test_kv6_subscript_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["KV₆ seed omnipresence"])
        types = [s["claim_type"] for s in r]
        assert "holographic_claim" in types

    def test_part_whole_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["part-whole relationship"])
        types = [s["claim_type"] for s in r]
        assert "holographic_claim" in types

    def test_suggests_holografik_lens(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["holographic"])
        h = next(s for s in r if s["claim_type"] == "holographic_claim")
        assert h["tool"] == "run_single_lens"
        assert "holografik" in h["call_hint"]


class TestSuggestToolsStructuralPattern:
    """Pattern: structural_pattern — matches three-phase/gate/vessel/etc."""

    def test_three_phase_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["three-phase actualization"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_integration_prerequisite(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["integration prerequisite AX5"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_multiplicative_gate(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["multiplicative gate AX52"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_transparent_vessel(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["transparent vessel AX27"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_unreachable_supremum(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["unreachable supremum T6"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_structural_incompleteness(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["structural incompleteness T17"])
        types = [s["claim_type"] for s in r]
        assert "structural_pattern" in types

    def test_suggests_validate_stage(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["three-phase actualization"])
        sp = next(s for s in r if s["claim_type"] == "structural_pattern")
        assert sp["tool"] == "validate_stage"


class TestSuggestToolsGradeClaim:
    """Pattern: grade_claim — matches epistemic grade terms."""

    def test_tasavvur_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Tasavvur level grasp"])
        types = [s["claim_type"] for s in r]
        assert "grade_claim" in types

    def test_tasdik_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Tasdik affirmation"])
        types = [s["claim_type"] for s in r]
        assert "grade_claim" in types

    def test_ilmelyakin_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["İlmelyakîn certainty"])
        types = [s["claim_type"] for s in r]
        assert "grade_claim" in types

    def test_ilmelyakin_ascii_detected(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Ilmelyakin grade"])
        types = [s["claim_type"] for s in r]
        assert "grade_claim" in types

    def test_epistemic_grade_phrase(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["the epistemic grade is high"])
        types = [s["claim_type"] for s in r]
        assert "grade_claim" in types

    def test_suggests_bileshke_pipeline(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Tasdik"])
        g = next(s for s in r if s["claim_type"] == "grade_claim")
        assert g["tool"] == "run_bileshke_pipeline"


class TestSuggestToolsDedup:
    """Deduplication: same claim_type only appears once."""

    def test_multiple_axiom_refs_single_suggestion(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX1, AX2, AX3, AX4, AX17"])
        axrefs = [s for s in r if s["claim_type"] == "axiom_reference"]
        assert len(axrefs) == 1

    def test_multiple_kv_refs_single_suggestion(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["KV₁ KV₂ KV₃ KV₄ KV₇"])
        kvrefs = [s for s in r if s["claim_type"] == "kavaid_reference"]
        assert len(kvrefs) == 1


class TestSuggestToolsCombined:
    """Multiple claim types in single text → multiple suggestions."""

    def test_axiom_and_kavaid(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX17 and KV₆ omnipresence"])
        types = {s["claim_type"] for s in r}
        assert "axiom_reference" in types
        assert "kavaid_reference" in types

    def test_three_types_in_one(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content([
            "AX17 holographic structure with convergence score 0.8"
        ])
        types = {s["claim_type"] for s in r}
        assert len(types) >= 3

    def test_all_eight_detectable(self):
        """Text with all 8 patterns → 8 suggestions."""
        from fw_server.server import _suggest_tools_for_content
        text = (
            "AX1 axiom ref. T6 theorem. C1 chain. KV₄ kavaid. "
            "convergence score. holographic structure. "
            "three-phase actualization. Tasavvur grade."
        )
        r = _suggest_tools_for_content([text])
        types = {s["claim_type"] for s in r}
        assert len(types) == 8


class TestSuggestToolsNoMatch:
    """No patterns found → empty list."""

    def test_empty_text(self):
        from fw_server.server import _suggest_tools_for_content
        assert _suggest_tools_for_content([""]) == []

    def test_empty_list(self):
        from fw_server.server import _suggest_tools_for_content
        assert _suggest_tools_for_content([]) == []

    def test_plain_text_no_patterns(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["Just a normal sentence."])
        assert r == []

    def test_unrelated_code(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["def hello(): return 42"])
        assert r == []


class TestSuggestToolsFieldStructure:
    """Each suggestion has exactly 4 fields."""

    def test_suggestion_fields(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX1 test"])
        assert len(r) > 0
        for s in r:
            assert set(s.keys()) == {"claim_type", "tool", "call_hint", "reason"}

    def test_all_fields_are_strings(self):
        from fw_server.server import _suggest_tools_for_content
        r = _suggest_tools_for_content(["AX1 KV₄ convergence"])
        for s in r:
            for k, v in s.items():
                assert isinstance(v, str), f"{k} is {type(v)}, expected str"


# ═══════════════════════════════════════════════════════════════════════
# STEP 6b: hcp_query sc3_tool_suggestions
# ═══════════════════════════════════════════════════════════════════════


class TestHCPQuerySC3Suggestions:
    """Step 6: hcp_query includes sc3_tool_suggestions when patterns found."""

    def _ingest_and_query(self, ingest_text, query_text):
        from fw_server.server import hcp_ingest, hcp_query
        hcp_ingest(ingest_text)
        return json.loads(hcp_query(query_text))

    def test_no_suggestions_for_plain_query(self):
        """Plain text query with plain content → no suggestions.

        NOTE: HCP is a shared global — earlier tests may leave chunks that
        trigger SC-3 patterns.  We reset HCP state to guarantee isolation.
        """
        import fw_server.server as srv
        old = srv._hcp_protocol
        srv._hcp_protocol = None          # force fresh HCP
        try:
            r = self._ingest_and_query(
                "This is normal technical documentation.",
                "What is the documentation about?"
            )
            # sc3_tool_suggestions should be absent (or empty)
            if "sc3_tool_suggestions" in r:
                assert len(r["sc3_tool_suggestions"]) == 0
        finally:
            srv._hcp_protocol = old       # restore for later tests

    def test_suggestions_from_query_text(self):
        """Claim patterns in the QUERY text trigger suggestions."""
        r = self._ingest_and_query(
            "Some plain context data.",
            "What does AX17 say about Esma ontology?"
        )
        assert "sc3_tool_suggestions" in r
        types = [s["claim_type"] for s in r["sc3_tool_suggestions"]]
        assert "axiom_reference" in types

    def test_suggestions_from_chunk_content(self):
        """Claim patterns in RETURNED CHUNKS trigger suggestions."""
        r = self._ingest_and_query(
            "AX37 holographic structure means every part mirrors the whole.",
            "Tell me about part-whole structure"
        )
        # Either query or chunk should trigger holographic detection
        if "sc3_tool_suggestions" in r:
            types = [s["claim_type"] for s in r["sc3_tool_suggestions"]]
            # At least holographic should be detected from content
            assert len(types) >= 1

    def test_sc3_note_present_when_suggestions(self):
        """When suggestions exist, sc3_note is included."""
        r = self._ingest_and_query(
            "Some data.", "What about AX1?"
        )
        if r.get("sc3_tool_suggestions"):
            assert "sc3_note" in r
            assert "SC-3" in r["sc3_note"]

    def test_response_still_has_standard_fields(self):
        """SC-3 additions don't break existing response structure."""
        r = self._ingest_and_query(
            "Some content.", "AX1 query"
        )
        assert "action" in r
        assert r["action"] == "query"
        assert "question" in r
        assert "n_results" in r
        assert "results" in r

    def test_hcp_not_ready_returns_error(self):
        """HCP not ready → error, no crash."""
        from fw_server.server import hcp_query
        import fw_server.server as srv
        # Temporarily clear HCP
        old = srv._hcp_protocol
        srv._hcp_protocol = None
        try:
            r = json.loads(hcp_query("test query"))
            # Either returns error (HCP not ready) or creates new HCP
            # Both are acceptable — just no crash
            assert isinstance(r, dict)
        finally:
            srv._hcp_protocol = old


# ═══════════════════════════════════════════════════════════════════════
# STEP 5b: SC-3 Decision Matrix in instructions file
# ═══════════════════════════════════════════════════════════════════════


class TestFieldInstructionsFile:
    """Verify field instructions file documents the shaping-directives architecture."""

    def _read_instructions(self):
        import os
        path = os.path.join(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
            ".github", "instructions", "dusun-field.instructions.md"
        )
        with open(path, "r", encoding="utf-8") as f:
            return f.read()

    def test_instructions_contain_shaping_directives(self):
        content = self._read_instructions()
        assert "shaping_directives" in content

    def test_instructions_mention_grade_ceiling(self):
        content = self._read_instructions()
        assert "grade_ceiling" in content

    def test_instructions_mention_core_rule(self):
        content = self._read_instructions()
        assert "Core Rule" in content

    def test_instructions_have_anti_patterns(self):
        content = self._read_instructions()
        assert "What NOT to Do" in content
        assert "Do not" in content

    def test_instructions_reference_paths(self):
        content = self._read_instructions()
        assert "P0" in content and "P4" in content


# ═══════════════════════════════════════════════════════════════════════
# CROSS-STEP INTEGRATION TESTS
# ═══════════════════════════════════════════════════════════════════════


class TestCrossStepIntegration:
    """Tests that verify multiple steps work together correctly."""

    def test_validate_stage_with_content_and_hcp_ingest(self):
        """Steps 3+4: content-aware gate + auto-ingest."""
        from fw_server.server import validate_stage
        result = json.loads(validate_stage(
            "2", "Integration test",
            content="AX1 K₁ ontological axiom. AX2 İlim."
        ))
        assert result["hcp_ingested"] is True
        assert "gate" in result
        assert result["composite_score"] < 1.0

    def test_framework_summary_sc3_matches_suggest_patterns(self):
        """Steps 5+6: sc3_tool_guide and _suggest_tools patterns cover same claims."""
        from fw_server.server import get_framework_summary
        from fw_server.server import _SC3_PATTERNS
        summary = json.loads(get_framework_summary())
        guide_tools = {
            e["tool"] for e in summary["sc3_tool_guide"]["claim_to_tool"]
        }
        pattern_tools = {p["tool"] for p in _SC3_PATTERNS}
        # Both should reference the same set of tools
        assert guide_tools & pattern_tools, "No overlap between guide and patterns"

    def test_coerce_used_by_check_kavaid_native(self):
        """Step 1: _coerce enables native list input to check_kavaid."""
        from fw_server.server import check_kavaid
        r = json.loads(check_kavaid(0.4, [1, 1, 1, 1, 1, 1, 0], {"KV1": True}))
        assert "error" not in r

    def test_full_pipeline_flow(self):
        """End-to-end: ingest → validate_stage → query with SC-3."""
        from fw_server.server import hcp_ingest, validate_stage, hcp_query

        # 1. Ingest some framework text
        hcp_ingest("AX37 holographic structure means parts mirror wholes. "
                    "T12 besmele seed. KV₆ omnipresence.")

        # 2. Validate a stage with content
        gate = json.loads(validate_stage(
            "3", "Ontological framework",
            content="AX37 holographic. T12 besmele."
        ))
        assert gate["hcp_ingested"] is True

        # 3. Query HCP — should return results with SC-3 suggestions
        qr = json.loads(hcp_query("holographic structure AX37"))
        assert "results" in qr
        if qr.get("sc3_tool_suggestions"):
            types = {s["claim_type"] for s in qr["sc3_tool_suggestions"]}
            # Should suggest something for holographic or axiom
            assert len(types) >= 1

    def test_ax56_ceiling_maintained(self):
        """All tools maintain AX56 — no Hakkalyakîn."""
        from fw_server.server import validate_stage, get_framework_summary
        # validate_stage
        r = json.loads(validate_stage("2", "AX56 test"))
        assert "Hakkalyakîn" not in r.get("ax57_compact", "")
        # framework_summary
        s = json.loads(get_framework_summary())
        assert s["system_integration"]["max_grade"] == "İlmelyakîn (AX56)"

    def test_kv4_composite_bound(self):
        """T6/KV₄: composite always < 1.0."""
        from fw_server.server import validate_stage
        for stage in ["1", "2", "3", "4", "5", "9a"]:
            r = json.loads(validate_stage(stage, "KV4 test"))
            assert r["composite_score"] < 1.0, (
                f"Stage {stage}: composite={r['composite_score']} >= 1.0"
            )


class TestSC3PatternsConstant:
    """Verify _SC3_PATTERNS is properly structured."""

    def test_sc3_patterns_importable(self):
        from fw_server.server import _SC3_PATTERNS
        assert isinstance(_SC3_PATTERNS, list)

    def test_sc3_patterns_has_8_entries(self):
        from fw_server.server import _SC3_PATTERNS
        assert len(_SC3_PATTERNS) == 8

    def test_each_pattern_has_required_keys(self):
        from fw_server.server import _SC3_PATTERNS
        required = {"claim_type", "regex", "tool", "call_hint", "reason"}
        for pat in _SC3_PATTERNS:
            assert required <= set(pat.keys()), f"Missing keys: {required - set(pat.keys())}"

    def test_each_regex_is_compiled(self):
        import re
        from fw_server.server import _SC3_PATTERNS
        for pat in _SC3_PATTERNS:
            assert isinstance(pat["regex"], re.Pattern), (
                f"{pat['claim_type']}: regex not compiled"
            )

    def test_claim_types_are_unique(self):
        from fw_server.server import _SC3_PATTERNS
        types = [p["claim_type"] for p in _SC3_PATTERNS]
        assert len(types) == len(set(types)), "Duplicate claim types"

    def test_all_tools_are_valid(self):
        from fw_server.server import _SC3_PATTERNS
        valid_tools = {
            "run_single_lens", "run_bileshke_pipeline", "check_kavaid",
            "verify_chains", "validate_stage", "get_framework_summary",
        }
        for pat in _SC3_PATTERNS:
            assert pat["tool"] in valid_tools, (
                f"{pat['claim_type']}: unknown tool {pat['tool']}"
            )
