from brox.model import (
    Capability,
    Component,
    Property,
    create_aibom,
    generate_bom_ref,
    hash_content,
)


class TestCapability:
    def test_to_record_string_json_format(self):
        cap = Capability(
            name="filesystem.read",
            scope="/data/**",
            asset="mcp_server:fs",
            evidence=["mcp.json:10", "mcp.json:20"],
        )

        record = cap.to_record_string()
        assert '"cap":"filesystem.read"' in record
        assert '"scope":"/data/**"' in record
        assert '"asset":"mcp_server:fs"' in record
        assert '"evidence"' in record

    def test_evidence_limited_to_five(self):
        cap = Capability(
            name="exec.shell",
            scope="*",
            asset="mcp_server:shell",
            evidence=[f"file.py:{i}" for i in range(10)],
        )

        record = cap.to_record_string()
        import json

        data = json.loads(record)
        assert len(data["evidence"]) == 5

    def test_capability_equality(self):
        cap1 = Capability(
            name="filesystem.read", scope="/data/**", asset="mcp_server:fs", evidence=["a.py:1"]
        )
        cap2 = Capability(
            name="filesystem.read", scope="/data/**", asset="mcp_server:fs", evidence=["b.py:2"]
        )
        cap3 = Capability(
            name="filesystem.write", scope="/data/**", asset="mcp_server:fs", evidence=["a.py:1"]
        )

        assert cap1 == cap2
        assert cap1 != cap3


class TestAIBom:
    def test_create_aibom_has_required_fields(self):
        bom = create_aibom(repo_root="/test/repo")

        assert bom.bom_format == "CycloneDX"
        assert bom.spec_version == "1.5"
        assert bom.version == 1
        assert len(bom.metadata.tools) == 1
        assert bom.metadata.tools[0].name == "brox"

    def test_to_dict_deterministic_ordering(self):
        bom = create_aibom()

        bom.components.append(
            Component(
                type="service",
                name="zebra",
                version="1",
                bom_ref="service:zebra",
                properties=[],
            )
        )
        bom.components.append(
            Component(
                type="service",
                name="alpha",
                version="1",
                bom_ref="service:alpha",
                properties=[],
            )
        )

        data = bom.to_dict()

        component_refs = [c["bom-ref"] for c in data["components"]]
        assert component_refs == sorted(component_refs)

    def test_calculate_hash_deterministic(self):
        import time

        bom1 = create_aibom()
        hash1 = bom1.calculate_hash()

        time.sleep(0.1)

        bom2 = create_aibom()
        hash2 = bom2.calculate_hash()

        assert hash1 == hash2

    def test_canonical_dict_excludes_volatile_fields(self):
        bom = create_aibom(repo_root="/test/repo")
        bom.metadata.properties.append(Property(name="brox.bom.hash", value="test_hash"))

        canonical = bom.to_canonical_dict()

        assert "timestamp" not in canonical.get("metadata", {})

        props = canonical.get("metadata", {}).get("properties", [])
        prop_names = [p["name"] for p in props]
        assert "brox.bom.hash" not in prop_names
        assert "brox.repo.root" not in prop_names

        assert "brox.scan.deterministic" in prop_names


class TestHelperFunctions:
    def test_generate_bom_ref(self):
        ref = generate_bom_ref("mcp_server", "filesystem", "server1")
        assert ref == "mcp_server:filesystem:server1"

    def test_hash_content_consistent(self):
        content = "test content"
        hash1 = hash_content(content)
        hash2 = hash_content(content)

        assert hash1 == hash2
        assert len(hash1) == 64  # SHA256 hex length
