"""
tests/test_holografik.py — Comprehensive tests for the Topoloji + Holografik Lens (Lens #7 of 7).

Tests cover:
  holografik.types      — BesmeleBoyut, DimensionType, CompositionMode, SeedVector,
                           HolographicUnit, FidelityPair, IsomorphismResult,
                           HolographicModel, clamp_score
  holografik.verification — 9 checks, verify_all, yakinlasma, framework_summary
  holografik.constraints  — 11 constraint factories (10 individual + 1 composite)

Governing axioms tested:
  AX37:     Holographic Structure — parts mirror the whole
  AX38:     Tereşşuhat — faithful seepage
  T12:      Besmele Seed — triple isomorphism
  T14:      Ne Ayn Ne Gayr chain — 0 < fidelity < 1
  KV₆:     Holographic Seed Omnipresence
  AX52:     Multiplicative gate
  N-2/AX59: Celâl Inclusion (B21–B22)
  AX63:     Tesanüd/İnfirâd asymmetry
  KV₄/T6:  Convergence bound
  KV₇:     Independence
  AX57:     Transparency
"""

from __future__ import annotations

import json
import math
import unittest

from holografik.types import (
    ALL_DIMENSIONS,
    BesmeleBoyut,
    CELALI_DIMENSIONS,
    CEMALI_DIMENSIONS,
    CompositionMode,
    DimensionType,
    DIMENSION_TYPE,
    FidelityPair,
    HolographicModel,
    HolographicUnit,
    IsomorphismResult,
    NUM_CELALI,
    NUM_CEMALI,
    NUM_DIMENSIONS,
    SeedVector,
    clamp_score,
    _cosine_similarity,
)
from holografik.verification import (
    check_celal_inclusion,
    check_convergence_bound,
    check_fidelity_bounds,
    check_holographic_isomorphism,
    check_independence,
    check_seed_omnipresence,
    check_seed_structure,
    check_tesanud_infirad,
    check_transparency,
    framework_summary,
    verify_all,
    yakinlasma,
)
from holografik.constraints import (
    _parse_holographic,
    celal_inclusion as celal_inclusion_constraint,
    fidelity_bounds as fidelity_bounds_constraint,
    holographic_convergence_bound,
    holographic_convergence_score,
    holographic_independence,
    holographic_isomorphism as holographic_isomorphism_constraint,
    holographic_transparency,
    seed_omnipresence as seed_omnipresence_constraint,
    seed_structure as seed_structure_constraint,
    tesanud_infirad_asymmetry,
    valid_holographic_entry,
)


# ========================================================================
# Test helpers
# ========================================================================

def _make_seed(*values: float) -> SeedVector:
    """Create a SeedVector from 22 values."""
    if len(values) != 22:
        raise ValueError(f"Expected 22 values, got {len(values)}")
    return SeedVector(values=tuple(values))


def _make_uniform_seed(v: float = 0.5) -> SeedVector:
    """Create a uniform seed vector."""
    return SeedVector.uniform(v)


def _make_full_model() -> HolographicModel:
    """Create a fully populated holographic model for testing."""
    model = HolographicModel(name="test_model")

    seed_a = _make_seed(
        0.8, 0.7, 0.6, 0.5, 0.4, 0.5, 0.6, 0.7, 0.8, 0.5,
        0.6, 0.7, 0.4, 0.3, 0.5, 0.6, 0.4, 0.5, 0.3, 0.4,
        0.3, 0.2,  # celâlî
    )
    seed_b = _make_seed(
        0.7, 0.6, 0.5, 0.4, 0.3, 0.6, 0.5, 0.6, 0.7, 0.4,
        0.5, 0.6, 0.3, 0.4, 0.4, 0.5, 0.3, 0.4, 0.2, 0.3,
        0.2, 0.1,  # celâlî
    )

    model.add_unit(HolographicUnit(name="besmele", seed=seed_a, level="besmele"))
    model.add_unit(HolographicUnit(name="fatiha", seed=seed_b, level="fatiha"))

    model.add_fidelity_pair(FidelityPair(
        source_name="besmele", derivative_name="fatiha", fidelity=0.85,
    ))

    model.add_isomorphism(IsomorphismResult(
        unit_a_name="besmele", unit_b_name="fatiha",
        similarity=0.92, shared_dimensions=22, is_isomorphic=True,
    ))

    model.composition_mode = CompositionMode.TESANUD
    return model


def _model_to_json(model: HolographicModel) -> str:
    """Serialize model to JSON string for constraint testing."""
    d = model.to_dict()
    # Reformat for _parse_holographic
    units = []
    for u in d["units"]:
        seed_dict = u["seed"]
        seed_list = [seed_dict[dim.name] for dim in ALL_DIMENSIONS]
        units.append({
            "name": u["name"],
            "level": u.get("level", ""),
            "description": u.get("description", ""),
            "seed": seed_list,
        })
    output = {
        "name": d["name"],
        "units": units,
        "fidelity_pairs": [
            {"source": p["source"], "derivative": p["derivative"], "fidelity": p["fidelity"]}
            for p in d["fidelity_pairs"]
        ],
        "isomorphism_results": [
            {
                "unit_a": r["unit_a"], "unit_b": r["unit_b"],
                "similarity": r["similarity"],
                "shared_dimensions": r["shared_dimensions"],
                "is_isomorphic": r["is_isomorphic"],
            }
            for r in d["isomorphism_results"]
        ],
        "composition_mode": d["composition_mode"],
    }
    return json.dumps(output)


# ========================================================================
# SECTION 1: types.py — Enums
# ========================================================================

class TestDimensionType(unittest.TestCase):
    def test_cemali_value(self):
        self.assertEqual(DimensionType.CEMALI.value, "cemali")

    def test_celali_value(self):
        self.assertEqual(DimensionType.CELALI.value, "celali")

    def test_two_members(self):
        self.assertEqual(len(DimensionType), 2)


class TestCompositionMode(unittest.TestCase):
    def test_tesanud(self):
        self.assertEqual(CompositionMode.TESANUD.value, "tesanud")

    def test_infirad(self):
        self.assertEqual(CompositionMode.INFIRAD.value, "infirad")

    def test_independent(self):
        self.assertEqual(CompositionMode.INDEPENDENT.value, "independent")

    def test_three_members(self):
        self.assertEqual(len(CompositionMode), 3)


class TestBesmeleBoyut(unittest.TestCase):
    """Validate the 22 Besmele dimensions (B01–B22)."""

    def test_total_count(self):
        self.assertEqual(len(BesmeleBoyut), 22)

    def test_num_constants(self):
        self.assertEqual(NUM_DIMENSIONS, 22)
        self.assertEqual(NUM_CEMALI, 20)
        self.assertEqual(NUM_CELALI, 2)

    def test_cemali_dimensions(self):
        self.assertEqual(len(CEMALI_DIMENSIONS), 20)
        for dim in CEMALI_DIMENSIONS:
            self.assertEqual(DIMENSION_TYPE[dim], DimensionType.CEMALI)

    def test_celali_dimensions(self):
        self.assertEqual(len(CELALI_DIMENSIONS), 2)
        self.assertEqual(CELALI_DIMENSIONS[0], BesmeleBoyut.B21)
        self.assertEqual(CELALI_DIMENSIONS[1], BesmeleBoyut.B22)
        for dim in CELALI_DIMENSIONS:
            self.assertEqual(DIMENSION_TYPE[dim], DimensionType.CELALI)

    def test_b21_celal_adalet(self):
        self.assertEqual(BesmeleBoyut.B21.value, "Celal_Adalet")

    def test_b22_kahhar_tathir(self):
        self.assertEqual(BesmeleBoyut.B22.value, "Kahhar_Tathir")

    def test_b01_rahman(self):
        self.assertEqual(BesmeleBoyut.B01.value, "Rahman")

    def test_all_dimensions_order(self):
        self.assertEqual(len(ALL_DIMENSIONS), 22)
        self.assertEqual(ALL_DIMENSIONS[0], BesmeleBoyut.B01)
        self.assertEqual(ALL_DIMENSIONS[21], BesmeleBoyut.B22)

    def test_dimension_type_map_complete(self):
        for dim in BesmeleBoyut:
            self.assertIn(dim, DIMENSION_TYPE)


# ========================================================================
# SECTION 2: types.py — SeedVector
# ========================================================================

class TestSeedVector(unittest.TestCase):

    def test_22_components_required(self):
        with self.assertRaises(ValueError):
            SeedVector(values=(1.0,) * 21)
        with self.assertRaises(ValueError):
            SeedVector(values=(1.0,) * 23)

    def test_valid_construction(self):
        sv = _make_uniform_seed(0.5)
        self.assertEqual(len(sv.values), 22)

    def test_cemali_property(self):
        sv = _make_uniform_seed(0.3)
        self.assertEqual(len(sv.cemali), 20)
        self.assertTrue(all(v == 0.3 for v in sv.cemali))

    def test_celali_property(self):
        sv = _make_uniform_seed(0.4)
        self.assertEqual(len(sv.celali), 2)
        self.assertTrue(all(v == 0.4 for v in sv.celali))

    def test_has_celal_true(self):
        sv = _make_uniform_seed(0.1)
        self.assertTrue(sv.has_celal)

    def test_has_celal_false(self):
        vals = [0.5] * 20 + [0.0, 0.0]
        sv = SeedVector(values=tuple(vals))
        self.assertFalse(sv.has_celal)

    def test_is_omnipresent_true(self):
        sv = _make_uniform_seed(0.1)
        self.assertTrue(sv.is_omnipresent)

    def test_is_omnipresent_false(self):
        sv = SeedVector.zero()
        self.assertFalse(sv.is_omnipresent)

    def test_nonzero_count(self):
        vals = [0.5] * 10 + [0.0] * 12
        sv = SeedVector(values=tuple(vals))
        self.assertEqual(sv.nonzero_count, 10)
        self.assertEqual(sv.zero_count, 12)

    def test_magnitude(self):
        sv = SeedVector(values=tuple([1.0] * 22))
        expected = math.sqrt(22)
        self.assertAlmostEqual(sv.magnitude, expected, places=6)

    def test_zero_magnitude(self):
        sv = SeedVector.zero()
        self.assertEqual(sv.magnitude, 0.0)

    def test_dimension_value(self):
        vals = list(range(22))
        sv = SeedVector(values=tuple(float(v) for v in vals))
        self.assertEqual(sv.dimension_value(BesmeleBoyut.B01), 0.0)
        self.assertEqual(sv.dimension_value(BesmeleBoyut.B22), 21.0)

    def test_gate_score_all_positive(self):
        sv = _make_uniform_seed(0.1)
        self.assertEqual(sv.gate_score(), 1.0)

    def test_gate_score_one_zero(self):
        vals = [0.5] * 21 + [0.0]
        sv = SeedVector(values=tuple(vals))
        self.assertEqual(sv.gate_score(), 0.0)

    def test_gate_score_all_zero(self):
        sv = SeedVector.zero()
        self.assertEqual(sv.gate_score(), 0.0)

    def test_to_dict(self):
        sv = _make_uniform_seed(0.5)
        d = sv.to_dict()
        self.assertEqual(len(d), 22)
        self.assertIn("B01", d)
        self.assertIn("B22", d)

    def test_zero_static(self):
        sv = SeedVector.zero()
        self.assertEqual(len(sv.values), 22)
        self.assertTrue(all(v == 0.0 for v in sv.values))

    def test_uniform_static(self):
        sv = SeedVector.uniform(0.7)
        self.assertTrue(all(v == 0.7 for v in sv.values))

    def test_frozen(self):
        sv = _make_uniform_seed(0.5)
        with self.assertRaises(AttributeError):
            sv.values = (0.0,) * 22

    def test_non_numeric_component(self):
        with self.assertRaises(TypeError):
            SeedVector(values=tuple(["a"] * 22))


# ========================================================================
# SECTION 3: types.py — HolographicUnit
# ========================================================================

class TestHolographicUnit(unittest.TestCase):

    def test_valid_construction(self):
        sv = _make_uniform_seed(0.5)
        u = HolographicUnit(name="besmele", seed=sv)
        self.assertEqual(u.name, "besmele")

    def test_empty_name_raises(self):
        sv = _make_uniform_seed(0.5)
        with self.assertRaises(ValueError):
            HolographicUnit(name="", seed=sv)

    def test_whitespace_name_raises(self):
        sv = _make_uniform_seed(0.5)
        with self.assertRaises(ValueError):
            HolographicUnit(name="   ", seed=sv)

    def test_optional_fields(self):
        sv = _make_uniform_seed(0.3)
        u = HolographicUnit(name="test", seed=sv, level="fatiha", description="desc")
        self.assertEqual(u.level, "fatiha")
        self.assertEqual(u.description, "desc")

    def test_frozen(self):
        sv = _make_uniform_seed(0.5)
        u = HolographicUnit(name="test", seed=sv)
        with self.assertRaises(AttributeError):
            u.name = "other"


# ========================================================================
# SECTION 4: types.py — FidelityPair
# ========================================================================

class TestFidelityPair(unittest.TestCase):

    def test_valid_pair(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.5)
        self.assertTrue(fp.is_valid)

    def test_boundary_zero_invalid(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.0)
        self.assertFalse(fp.is_valid)

    def test_boundary_one_invalid(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=1.0)
        self.assertFalse(fp.is_valid)

    def test_negative_invalid(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=-0.1)
        self.assertFalse(fp.is_valid)

    def test_near_zero_valid(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.0001)
        self.assertTrue(fp.is_valid)

    def test_near_one_valid(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.9999)
        self.assertTrue(fp.is_valid)

    def test_empty_source_raises(self):
        with self.assertRaises(ValueError):
            FidelityPair(source_name="", derivative_name="b", fidelity=0.5)

    def test_empty_derivative_raises(self):
        with self.assertRaises(ValueError):
            FidelityPair(source_name="a", derivative_name="", fidelity=0.5)

    def test_non_numeric_fidelity(self):
        with self.assertRaises(TypeError):
            FidelityPair(source_name="a", derivative_name="b", fidelity="high")

    def test_frozen(self):
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.7)
        with self.assertRaises(AttributeError):
            fp.fidelity = 0.8


# ========================================================================
# SECTION 5: types.py — IsomorphismResult
# ========================================================================

class TestIsomorphismResult(unittest.TestCase):

    def test_valid_result(self):
        r = IsomorphismResult(
            unit_a_name="a", unit_b_name="b",
            similarity=0.9, shared_dimensions=20, is_isomorphic=True,
        )
        self.assertTrue(r.is_isomorphic)
        self.assertEqual(r.shared_dimensions, 20)

    def test_non_numeric_similarity(self):
        with self.assertRaises(TypeError):
            IsomorphismResult(
                unit_a_name="a", unit_b_name="b",
                similarity="high", shared_dimensions=20, is_isomorphic=True,
            )

    def test_non_int_shared(self):
        with self.assertRaises(TypeError):
            IsomorphismResult(
                unit_a_name="a", unit_b_name="b",
                similarity=0.9, shared_dimensions=20.5, is_isomorphic=True,
            )


# ========================================================================
# SECTION 6: types.py — clamp_score
# ========================================================================

class TestClampScore(unittest.TestCase):

    def test_normal_value(self):
        self.assertEqual(clamp_score(0.5), 0.5)

    def test_above_max(self):
        self.assertEqual(clamp_score(1.5), 0.9999)

    def test_exactly_one(self):
        self.assertEqual(clamp_score(1.0), 0.9999)

    def test_zero(self):
        self.assertEqual(clamp_score(0.0), 0.0)

    def test_negative(self):
        self.assertEqual(clamp_score(-1.0), 0.0)

    def test_max_value(self):
        self.assertEqual(clamp_score(0.9999), 0.9999)


# ========================================================================
# SECTION 7: types.py — cosine similarity
# ========================================================================

class TestCosineSimilarity(unittest.TestCase):

    def test_identical_vectors(self):
        sv = _make_uniform_seed(0.5)
        self.assertAlmostEqual(_cosine_similarity(sv, sv), 1.0, places=6)

    def test_orthogonal_vectors(self):
        vals_a = [1.0] + [0.0] * 21
        vals_b = [0.0] + [1.0] + [0.0] * 20
        sv_a = SeedVector(values=tuple(vals_a))
        sv_b = SeedVector(values=tuple(vals_b))
        self.assertAlmostEqual(_cosine_similarity(sv_a, sv_b), 0.0, places=6)

    def test_zero_vector(self):
        sv = _make_uniform_seed(0.5)
        zero = SeedVector.zero()
        self.assertEqual(_cosine_similarity(sv, zero), 0.0)

    def test_similar_vectors(self):
        sv_a = _make_uniform_seed(0.5)
        vals_b = [0.5] * 10 + [0.6] * 12
        sv_b = SeedVector(values=tuple(vals_b))
        sim = _cosine_similarity(sv_a, sv_b)
        self.assertGreater(sim, 0.9)
        self.assertLess(sim, 1.0)


# ========================================================================
# SECTION 8: types.py — HolographicModel
# ========================================================================

class TestHolographicModel(unittest.TestCase):

    def test_empty_model(self):
        m = HolographicModel()
        self.assertEqual(m.units, [])
        self.assertEqual(m.fidelity_pairs, [])
        self.assertEqual(m.isomorphism_results, [])

    def test_add_unit(self):
        m = HolographicModel()
        u = HolographicUnit(name="a", seed=_make_uniform_seed(0.5))
        m.add_unit(u)
        self.assertEqual(len(m.units), 1)

    def test_get_unit(self):
        m = HolographicModel()
        u = HolographicUnit(name="a", seed=_make_uniform_seed(0.5))
        m.add_unit(u)
        self.assertEqual(m.get_unit("a"), u)
        self.assertIsNone(m.get_unit("nonexistent"))

    def test_add_unit_type_check(self):
        m = HolographicModel()
        with self.assertRaises(TypeError):
            m.add_unit("not a unit")

    def test_add_fidelity_pair(self):
        m = HolographicModel()
        fp = FidelityPair(source_name="a", derivative_name="b", fidelity=0.8)
        m.add_fidelity_pair(fp)
        self.assertEqual(len(m.fidelity_pairs), 1)

    def test_add_fidelity_pair_type_check(self):
        m = HolographicModel()
        with self.assertRaises(TypeError):
            m.add_fidelity_pair("not a pair")

    def test_add_isomorphism(self):
        m = HolographicModel()
        r = IsomorphismResult(
            unit_a_name="a", unit_b_name="b",
            similarity=0.9, shared_dimensions=22, is_isomorphic=True,
        )
        m.add_isomorphism(r)
        self.assertEqual(len(m.isomorphism_results), 1)

    def test_add_isomorphism_type_check(self):
        m = HolographicModel()
        with self.assertRaises(TypeError):
            m.add_isomorphism("not a result")

    def test_all_seeds_omnipresent_true(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.1)))
        m.add_unit(HolographicUnit(name="b", seed=_make_uniform_seed(0.2)))
        self.assertTrue(m.all_seeds_omnipresent)

    def test_all_seeds_omnipresent_false(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.1)))
        m.add_unit(HolographicUnit(name="b", seed=SeedVector.zero()))
        self.assertFalse(m.all_seeds_omnipresent)

    def test_all_fidelities_valid_true(self):
        m = HolographicModel()
        m.add_fidelity_pair(FidelityPair("a", "b", 0.5))
        m.add_fidelity_pair(FidelityPair("b", "c", 0.7))
        self.assertTrue(m.all_fidelities_valid)

    def test_all_fidelities_valid_false(self):
        m = HolographicModel()
        m.add_fidelity_pair(FidelityPair("a", "b", 0.0))
        self.assertFalse(m.all_fidelities_valid)

    def test_celal_included_true(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.3)))
        self.assertTrue(m.celal_included)

    def test_celal_included_false(self):
        m = HolographicModel()
        vals = [0.5] * 20 + [0.0, 0.0]
        m.add_unit(HolographicUnit(name="a", seed=SeedVector(values=tuple(vals))))
        self.assertFalse(m.celal_included)

    def test_mean_similarity(self):
        m = HolographicModel()
        m.add_isomorphism(IsomorphismResult("a", "b", 0.8, 20, True))
        m.add_isomorphism(IsomorphismResult("a", "c", 0.6, 15, True))
        self.assertAlmostEqual(m.mean_similarity, 0.7, places=6)

    def test_mean_similarity_empty(self):
        m = HolographicModel()
        self.assertEqual(m.mean_similarity, 0.0)

    def test_tesanud_strength(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        m.add_unit(HolographicUnit(name="b", seed=_make_uniform_seed(0.6)))
        strength = m.tesanud_strength
        self.assertGreater(strength, 0.0)
        self.assertLess(strength, 1.0)

    def test_tesanud_strength_single_unit(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        self.assertEqual(m.tesanud_strength, 0.0)

    def test_compute_isomorphism(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        m.add_unit(HolographicUnit(name="b", seed=_make_uniform_seed(0.6)))
        result = m.compute_isomorphism("a", "b")
        self.assertIsNotNone(result)
        self.assertGreater(result.similarity, 0.5)
        self.assertEqual(len(m.isomorphism_results), 1)

    def test_compute_isomorphism_missing_unit(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        result = m.compute_isomorphism("a", "nonexistent")
        self.assertIsNone(result)

    def test_compute_fidelity(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="src", seed=_make_uniform_seed(0.5)))
        m.add_unit(HolographicUnit(name="der", seed=_make_uniform_seed(0.6)))
        pair = m.compute_fidelity("src", "der")
        self.assertIsNotNone(pair)
        self.assertGreater(pair.fidelity, 0)
        self.assertLess(pair.fidelity, 1)

    def test_compute_fidelity_missing_unit(self):
        m = HolographicModel()
        result = m.compute_fidelity("a", "b")
        self.assertIsNone(result)

    def test_to_dict(self):
        m = _make_full_model()
        d = m.to_dict()
        self.assertIn("name", d)
        self.assertIn("units", d)
        self.assertIn("fidelity_pairs", d)
        self.assertIn("isomorphism_results", d)
        self.assertIn("composition_mode", d)
        self.assertIn("all_seeds_omnipresent", d)
        self.assertIn("all_fidelities_valid", d)
        self.assertIn("celal_included", d)
        self.assertIn("tesanud_strength", d)

    def test_composition_mode_default(self):
        m = HolographicModel()
        self.assertEqual(m.composition_mode, CompositionMode.INDEPENDENT)


# ========================================================================
# SECTION 9: verification.py — Individual checks
# ========================================================================

class TestCheckSeedStructure(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_seed_structure(None)
        self.assertTrue(passed)

    def test_empty_model(self):
        passed, _ = check_seed_structure(HolographicModel())
        self.assertTrue(passed)

    def test_valid_model(self):
        m = _make_full_model()
        passed, _ = check_seed_structure(m)
        self.assertTrue(passed)


class TestCheckHolographicIsomorphism(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_holographic_isomorphism(None)
        self.assertTrue(passed)

    def test_no_results(self):
        m = HolographicModel()
        passed, _ = check_holographic_isomorphism(m)
        self.assertTrue(passed)

    def test_valid_results(self):
        m = _make_full_model()
        passed, _ = check_holographic_isomorphism(m)
        self.assertTrue(passed)

    def test_negative_similarity(self):
        m = HolographicModel()
        m.add_isomorphism(IsomorphismResult("a", "b", -0.1, 10, False))
        passed, detail = check_holographic_isomorphism(m)
        self.assertFalse(passed)
        self.assertIn("Negative", detail)


class TestCheckSeedOmnipresence(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_seed_omnipresence(None)
        self.assertTrue(passed)

    def test_all_omnipresent(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.1)))
        passed, _ = check_seed_omnipresence(m)
        self.assertTrue(passed)

    def test_zero_seed(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=SeedVector.zero()))
        passed, detail = check_seed_omnipresence(m)
        self.assertFalse(passed)
        self.assertIn("KV₆", detail)


class TestCheckFidelityBounds(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_fidelity_bounds(None)
        self.assertTrue(passed)

    def test_valid_fidelities(self):
        m = HolographicModel()
        m.add_fidelity_pair(FidelityPair("a", "b", 0.5))
        passed, _ = check_fidelity_bounds(m)
        self.assertTrue(passed)

    def test_zero_fidelity(self):
        m = HolographicModel()
        m.add_fidelity_pair(FidelityPair("a", "b", 0.0))
        passed, detail = check_fidelity_bounds(m)
        self.assertFalse(passed)
        self.assertIn("T14", detail)

    def test_one_fidelity(self):
        m = HolographicModel()
        m.add_fidelity_pair(FidelityPair("a", "b", 1.0))
        passed, detail = check_fidelity_bounds(m)
        self.assertFalse(passed)


class TestCheckCelalInclusion(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_celal_inclusion(None)
        self.assertTrue(passed)

    def test_celal_present(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.3)))
        passed, _ = check_celal_inclusion(m)
        self.assertTrue(passed)

    def test_celal_absent(self):
        m = HolographicModel()
        vals = [0.5] * 20 + [0.0, 0.0]
        m.add_unit(HolographicUnit(name="a", seed=SeedVector(values=tuple(vals))))
        passed, detail = check_celal_inclusion(m)
        self.assertFalse(passed)
        self.assertIn("N-2/AX59", detail)


class TestCheckConvergenceBound(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_convergence_bound(None)
        self.assertTrue(passed)

    def test_valid_model(self):
        m = _make_full_model()
        passed, _ = check_convergence_bound(m)
        self.assertTrue(passed)

    def test_seed_value_one(self):
        m = HolographicModel()
        vals = [1.0] + [0.5] * 21
        m.add_unit(HolographicUnit(name="a", seed=SeedVector(values=tuple(vals))))
        passed, detail = check_convergence_bound(m)
        self.assertFalse(passed)
        self.assertIn("KV₄/T6", detail)


class TestCheckTesanudInfirad(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_tesanud_infirad(None)
        self.assertTrue(passed)

    def test_tesanud_valid(self):
        m = _make_full_model()
        passed, _ = check_tesanud_infirad(m)
        self.assertTrue(passed)

    def test_tesanud_single_unit(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        m.composition_mode = CompositionMode.TESANUD
        passed, detail = check_tesanud_infirad(m)
        self.assertFalse(passed)
        self.assertIn("AX63", detail)

    def test_infirad_mode(self):
        m = HolographicModel()
        m.composition_mode = CompositionMode.INFIRAD
        passed, _ = check_tesanud_infirad(m)
        self.assertTrue(passed)

    def test_independent_mode(self):
        m = HolographicModel()
        m.composition_mode = CompositionMode.INDEPENDENT
        passed, _ = check_tesanud_infirad(m)
        self.assertTrue(passed)


class TestCheckIndependence(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_independence(None)
        self.assertTrue(passed)

    def test_independent_units(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        m.add_unit(HolographicUnit(name="b", seed=_make_uniform_seed(0.6)))
        passed, _ = check_independence(m)
        self.assertTrue(passed)

    def test_identical_seeds(self):
        m = HolographicModel()
        seed = _make_uniform_seed(0.5)
        m.add_unit(HolographicUnit(name="a", seed=seed))
        m.add_unit(HolographicUnit(name="b", seed=seed))
        passed, detail = check_independence(m)
        self.assertFalse(passed)
        self.assertIn("KV₇", detail)


class TestCheckTransparency(unittest.TestCase):

    def test_none_model(self):
        passed, _ = check_transparency(None)
        self.assertTrue(passed)

    def test_valid_model(self):
        m = _make_full_model()
        passed, _ = check_transparency(m)
        self.assertTrue(passed)


# ========================================================================
# SECTION 10: verification.py — verify_all, yakinlasma, framework_summary
# ========================================================================

class TestVerifyAll(unittest.TestCase):

    def test_returns_all_checks(self):
        results = verify_all(None)
        self.assertEqual(len(results), 9)
        expected_keys = {
            "seed_structure", "holographic_isomorphism", "seed_omnipresence",
            "fidelity_bounds", "celal_inclusion", "convergence_bound",
            "tesanud_infirad", "independence", "transparency",
        }
        self.assertEqual(set(results.keys()), expected_keys)

    def test_all_pass_none(self):
        results = verify_all(None)
        for name, (passed, _) in results.items():
            self.assertTrue(passed, f"{name} should pass for None model")

    def test_full_model_all_pass(self):
        m = _make_full_model()
        results = verify_all(m)
        for name, (passed, detail) in results.items():
            self.assertTrue(passed, f"{name} failed: {detail}")


class TestYakinlasma(unittest.TestCase):

    def test_none_model(self):
        score = yakinlasma(None)
        self.assertEqual(score, 0.0)  # No data → 0.0 (consistent with all lenses)

    def test_full_model(self):
        m = _make_full_model()
        score = yakinlasma(m)
        self.assertGreater(score, 0.0)
        self.assertLess(score, 1.0)

    def test_score_bounded(self):
        score = yakinlasma()
        self.assertLessEqual(score, 0.9999)
        self.assertGreaterEqual(score, 0.0)

    def test_valid_and_empty_both_bounded(self):
        """Both valid and empty models produce scores < 1.0."""
        m = _make_full_model()
        full_score = yakinlasma(m)
        empty_score = yakinlasma(HolographicModel())
        self.assertLess(full_score, 1.0)
        self.assertLess(empty_score, 1.0)


class TestFrameworkSummary(unittest.TestCase):

    def test_structure(self):
        s = framework_summary()
        self.assertEqual(s["lens"], "Topoloji + Holografik")
        self.assertEqual(s["lens_number"], 7)
        self.assertEqual(s["faculty"], "Ruh + Hafî")
        self.assertIn("B01–B22", s["domain"])
        self.assertIn("AX37", s["axioms_checked"])
        self.assertIn("KV6", s["kavaid_checked"])
        self.assertEqual(s["max_score"], 0.9999)

    def test_capabilities_list(self):
        s = framework_summary()
        self.assertIsInstance(s["capabilities"], list)
        self.assertGreater(len(s["capabilities"]), 0)


# ========================================================================
# SECTION 11: constraints.py — _parse_holographic
# ========================================================================

class TestParseHolographic(unittest.TestCase):

    def test_invalid_json(self):
        ok, model, msg = _parse_holographic("not json")
        self.assertFalse(ok)
        self.assertIsNone(model)
        self.assertIn("JSON", msg)

    def test_not_object(self):
        ok, model, msg = _parse_holographic("[1, 2, 3]")
        self.assertFalse(ok)
        self.assertIn("object", msg)

    def test_empty_object(self):
        ok, model, msg = _parse_holographic("{}")
        self.assertTrue(ok)
        self.assertIsNotNone(model)

    def test_full_model(self):
        m = _make_full_model()
        json_str = _model_to_json(m)
        ok, parsed, msg = _parse_holographic(json_str)
        self.assertTrue(ok, msg)
        self.assertIsNotNone(parsed)
        self.assertEqual(len(parsed.units), 2)
        self.assertEqual(len(parsed.fidelity_pairs), 1)
        self.assertEqual(len(parsed.isomorphism_results), 1)

    def test_dict_seed_format(self):
        data = {
            "name": "test",
            "units": [{
                "name": "u1",
                "seed": {dim.name: 0.5 for dim in ALL_DIMENSIONS},
            }],
        }
        ok, model, msg = _parse_holographic(json.dumps(data))
        self.assertTrue(ok, msg)
        self.assertEqual(len(model.units), 1)

    def test_wrong_seed_length(self):
        data = {
            "name": "test",
            "units": [{"name": "u1", "seed": [0.5] * 10}],
        }
        ok, model, msg = _parse_holographic(json.dumps(data))
        self.assertFalse(ok)
        self.assertIn("components", msg)


# ========================================================================
# SECTION 12: constraints.py — Individual constraint factories
# ========================================================================

class TestSeedStructureConstraint(unittest.TestCase):

    def test_returns_constraint(self):
        c = seed_structure_constraint()
        self.assertEqual(c.name, "seed_structure")

    def test_valid_json(self):
        m = _make_full_model()
        c = seed_structure_constraint()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)
        self.assertGreater(result.score, 0)
        self.assertLess(result.score, 1.0)

    def test_invalid_json(self):
        c = seed_structure_constraint()
        result = c.check("invalid")
        self.assertFalse(result.passed)
        self.assertEqual(result.score, 0.0)


class TestHolographicIsomorphismConstraint(unittest.TestCase):

    def test_returns_constraint(self):
        c = holographic_isomorphism_constraint()
        self.assertEqual(c.name, "holographic_isomorphism")

    def test_valid_json(self):
        m = _make_full_model()
        c = holographic_isomorphism_constraint()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestSeedOmnipresenceConstraint(unittest.TestCase):

    def test_returns_constraint(self):
        c = seed_omnipresence_constraint()
        self.assertEqual(c.name, "seed_omnipresence")

    def test_valid_json(self):
        m = _make_full_model()
        c = seed_omnipresence_constraint()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestFidelityBoundsConstraint(unittest.TestCase):

    def test_returns_constraint(self):
        c = fidelity_bounds_constraint()
        self.assertEqual(c.name, "fidelity_bounds")

    def test_valid_json(self):
        m = _make_full_model()
        c = fidelity_bounds_constraint()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestCelalInclusionConstraint(unittest.TestCase):

    def test_returns_constraint(self):
        c = celal_inclusion_constraint()
        self.assertEqual(c.name, "celal_inclusion")

    def test_valid_json(self):
        m = _make_full_model()
        c = celal_inclusion_constraint()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestHolographicConvergenceBound(unittest.TestCase):

    def test_returns_constraint(self):
        c = holographic_convergence_bound()
        self.assertEqual(c.name, "holographic_convergence_bound")

    def test_valid_json(self):
        m = _make_full_model()
        c = holographic_convergence_bound()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestTesanudInfiradAsymmetry(unittest.TestCase):

    def test_returns_constraint(self):
        c = tesanud_infirad_asymmetry()
        self.assertEqual(c.name, "tesanud_infirad_asymmetry")

    def test_valid_json(self):
        m = _make_full_model()
        c = tesanud_infirad_asymmetry()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestHolographicIndependence(unittest.TestCase):

    def test_returns_constraint(self):
        c = holographic_independence()
        self.assertEqual(c.name, "holographic_independence")

    def test_valid_json(self):
        m = _make_full_model()
        c = holographic_independence()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestHolographicTransparency(unittest.TestCase):

    def test_returns_constraint(self):
        c = holographic_transparency()
        self.assertEqual(c.name, "holographic_transparency")

    def test_valid_json(self):
        m = _make_full_model()
        c = holographic_transparency()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)


class TestHolographicConvergenceScore(unittest.TestCase):

    def test_returns_constraint(self):
        c = holographic_convergence_score()
        self.assertEqual(c.name, "holographic_convergence_score")

    def test_valid_json(self):
        m = _make_full_model()
        c = holographic_convergence_score()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed)
        self.assertGreater(result.score, 0)
        self.assertLess(result.score, 1.0)

    def test_invalid_json(self):
        c = holographic_convergence_score()
        result = c.check("bad")
        self.assertFalse(result.passed)


class TestValidHolographicEntry(unittest.TestCase):

    def test_returns_constraint(self):
        c = valid_holographic_entry()
        self.assertEqual(c.name, "valid_holographic_entry")

    def test_valid_json(self):
        m = _make_full_model()
        c = valid_holographic_entry()
        result = c.check(_model_to_json(m))
        self.assertTrue(result.passed, result.message)

    def test_invalid_json(self):
        c = valid_holographic_entry()
        result = c.check("bad")
        self.assertFalse(result.passed)


# ========================================================================
# SECTION 13: KV₇ — Independence (no cross-lens imports)
# ========================================================================

class TestKV7Independence(unittest.TestCase):
    """KV₇: holografik must not import from other lens packages."""

    def test_types_no_cross_lens_import(self):
        import holografik.types as mod
        with open(mod.__file__, encoding="utf-8") as f:
            source = f.read()
        FORBIDDEN = [
            "kavram_sozlugu", "mereoloji", "fol_formalizasyon",
            "kategori_teorisi", "bayes_analiz", "oyun_teorisi",
        ]
        for pkg in FORBIDDEN:
            self.assertNotIn(
                f"import {pkg}", source,
                f"KV₇ violation: types.py imports {pkg}",
            )

    def test_verification_no_cross_lens_import(self):
        import holografik.verification as mod
        with open(mod.__file__, encoding="utf-8") as f:
            source = f.read()
        FORBIDDEN = [
            "kavram_sozlugu", "mereoloji", "fol_formalizasyon",
            "kategori_teorisi", "bayes_analiz", "oyun_teorisi",
        ]
        for pkg in FORBIDDEN:
            self.assertNotIn(
                f"import {pkg}", source,
                f"KV₇ violation: verification.py imports {pkg}",
            )

    def test_constraints_no_cross_lens_import(self):
        import holografik.constraints as mod
        with open(mod.__file__, encoding="utf-8") as f:
            source = f.read()
        FORBIDDEN = [
            "kavram_sozlugu", "mereoloji", "fol_formalizasyon",
            "kategori_teorisi", "bayes_analiz", "oyun_teorisi",
        ]
        for pkg in FORBIDDEN:
            self.assertNotIn(
                f"import {pkg}", source,
                f"KV₇ violation: constraints.py imports {pkg}",
            )


# ========================================================================
# SECTION 14: Framework axiom integration tests
# ========================================================================

class TestAX37HolographicStructure(unittest.TestCase):
    """AX37: ∀P ⊂ Kur'an: Structure(P) ≅ Structure(Kur'an)"""

    def test_similar_units_are_isomorphic(self):
        m = HolographicModel()
        seed_a = _make_uniform_seed(0.5)
        seed_b = _make_uniform_seed(0.6)
        m.add_unit(HolographicUnit(name="part", seed=seed_a))
        m.add_unit(HolographicUnit(name="whole", seed=seed_b))
        result = m.compute_isomorphism("part", "whole", threshold=0.5)
        self.assertTrue(result.is_isomorphic)

    def test_dissimilar_units_not_isomorphic(self):
        m = HolographicModel()
        vals_a = [0.9] + [0.0] * 21
        vals_b = [0.0] * 21 + [0.9]
        seed_a = SeedVector(values=tuple(vals_a))
        seed_b = SeedVector(values=tuple(vals_b))
        m.add_unit(HolographicUnit(name="a", seed=seed_a))
        m.add_unit(HolographicUnit(name="b", seed=seed_b))
        result = m.compute_isomorphism("a", "b", threshold=0.5)
        self.assertFalse(result.is_isomorphic)


class TestT14NeAynNeGayr(unittest.TestCase):
    """T14: 0 < Fidelity < 1 at every level of the mirror chain."""

    def test_fidelity_strictly_bounded(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="source", seed=_make_uniform_seed(0.7)))
        m.add_unit(HolographicUnit(name="derivative", seed=_make_uniform_seed(0.5)))
        pair = m.compute_fidelity("source", "derivative")
        self.assertGreater(pair.fidelity, 0)
        self.assertLess(pair.fidelity, 1)

    def test_identical_seeds_still_bounded(self):
        """Even identical seeds produce fidelity < 1 due to clamping."""
        m = HolographicModel()
        seed = _make_uniform_seed(0.5)
        m.add_unit(HolographicUnit(name="a", seed=seed))
        m.add_unit(HolographicUnit(name="b", seed=seed))
        pair = m.compute_fidelity("a", "b")
        self.assertLess(pair.fidelity, 1.0)
        self.assertGreater(pair.fidelity, 0.0)


class TestKV6SeedOmnipresence(unittest.TestCase):
    """KV₆: ∀module: HolographicConstant(module) > 0"""

    def test_omnipresent_seed_detected(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.1)))
        passed, _ = check_seed_omnipresence(m)
        self.assertTrue(passed)

    def test_zero_seed_detected_as_violation(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=SeedVector.zero()))
        passed, _ = check_seed_omnipresence(m)
        self.assertFalse(passed)


class TestAX52MultiplicativeGate(unittest.TestCase):
    """AX52: Gate(A) = ∏ᵢ 𝟙(bᵢ > 0) — zero in any dimension = collapse."""

    def test_all_positive(self):
        sv = _make_uniform_seed(0.1)
        self.assertEqual(sv.gate_score(), 1.0)

    def test_one_zero_collapses(self):
        vals = [0.5] * 21 + [0.0]
        sv = SeedVector(values=tuple(vals))
        self.assertEqual(sv.gate_score(), 0.0)


class TestN2CelalInclusion(unittest.TestCase):
    """N-2/AX59: Analysis must not exclude celâlî dimensions."""

    def test_celal_present(self):
        sv = _make_uniform_seed(0.3)
        self.assertTrue(sv.has_celal)

    def test_celal_absent(self):
        vals = [0.5] * 20 + [0.0, 0.0]
        sv = SeedVector(values=tuple(vals))
        self.assertFalse(sv.has_celal)

    def test_only_b21_present(self):
        vals = [0.5] * 20 + [0.3, 0.0]
        sv = SeedVector(values=tuple(vals))
        self.assertTrue(sv.has_celal)


class TestAX63TesanudInfirad(unittest.TestCase):
    """AX63: Affirmations compose super-additively; denials stay isolated."""

    def test_tesanud_strength_increases_with_similar_units(self):
        m = HolographicModel()
        m.add_unit(HolographicUnit(name="a", seed=_make_uniform_seed(0.5)))
        m.add_unit(HolographicUnit(name="b", seed=_make_uniform_seed(0.5001)))
        strength = m.tesanud_strength
        self.assertGreater(strength, 0.9)

    def test_tesanud_strength_low_for_dissimilar(self):
        m = HolographicModel()
        vals_a = [0.9] + [0.0] * 21
        vals_b = [0.0] * 21 + [0.9]
        m.add_unit(HolographicUnit(name="a", seed=SeedVector(values=tuple(vals_a))))
        m.add_unit(HolographicUnit(name="b", seed=SeedVector(values=tuple(vals_b))))
        strength = m.tesanud_strength
        self.assertLess(strength, 0.1)


# ========================================================================
# SECTION 15: T6/KV₄ — Score bounding across all interfaces
# ========================================================================

class TestScoreBounds(unittest.TestCase):

    def test_yakinlasma_always_below_one(self):
        for _ in range(5):
            m = _make_full_model()
            score = yakinlasma(m)
            self.assertLess(score, 1.0)

    def test_clamp_never_reaches_one(self):
        self.assertLess(clamp_score(999), 1.0)

    def test_convergence_score_constraint_below_one(self):
        c = holographic_convergence_score()
        m = _make_full_model()
        result = c.check(_model_to_json(m))
        self.assertLess(result.score, 1.0)


if __name__ == "__main__":
    unittest.main()
