"""Tests for InformationFlowTracker (Priority #4, OWASP MCP06/MCP10)."""

import pytest
from insa_its.detectors.information_flow import InformationFlowTracker


class TestInit:
    def test_creates_tracker(self):
        t = InformationFlowTracker()
        assert t.name == "information_flow"
        assert t.enabled is True

    def test_initial_state_empty(self):
        t = InformationFlowTracker()
        assert len(t._provenance) == 0
        assert len(t._channels) == 0
        assert len(t._agent_scopes) == 0


class TestChannelRegistration:
    def test_register_channel(self):
        t = InformationFlowTracker()
        t.register_channel("agent_a", "agent_b")
        assert frozenset({"agent_a", "agent_b"}) in t._channels

    def test_register_multiple_channels(self):
        t = InformationFlowTracker()
        t.register_channel("a", "b")
        t.register_channel("b", "c")
        assert len(t._channels) == 2


class TestAgentScopes:
    def test_set_scope(self):
        t = InformationFlowTracker()
        t.set_agent_scope("order_bot", ["order_id", "sku", "dollar_amount"])
        assert "order_bot" in t._agent_scopes
        assert "order_id" in t._agent_scopes["order_bot"]

    def test_scope_violation_detected(self):
        t = InformationFlowTracker()
        t.set_agent_scope("shipping_bot", ["order_id", "sku"])
        result = t.check(
            "Customer email: john@example.com, SSN: 123-45-6789",
            1, sender_id="shipping_bot",
        )
        assert result["detected"] is True
        assert "scope_violation" in result["flags"]

    def test_within_scope_no_violation(self):
        t = InformationFlowTracker()
        t.set_agent_scope("order_bot", ["order_id", "dollar_amount"])
        result = t.check(
            "Processing order #ABC123 for $150.00",
            1, sender_id="order_bot",
        )
        assert "scope_violation" not in result.get("flags", [])


class TestProvenanceTracking:
    def test_tracks_first_introducer(self):
        t = InformationFlowTracker()
        t.check("Order #XYZ789 confirmed", 1, sender_id="order_bot")
        prov = t.get_provenance("order_id", "XYZ789")
        assert prov is not None
        assert prov["introduced_by"] == "order_bot"

    def test_tracks_seen_by(self):
        t = InformationFlowTracker()
        t.check("Order #ABC123 placed", 1, sender_id="agent_a")
        t.check("Shipping order #ABC123", 2, sender_id="agent_b")
        prov = t.get_provenance("order_id", "ABC123")
        assert "agent_a" in prov["seen_by"]
        assert "agent_b" in prov["seen_by"]


class TestUnauthorizedAccess:
    def test_detects_unauthorized_data_access(self):
        t = InformationFlowTracker()
        t.register_channel("order_bot", "inventory_bot")
        # order_bot introduces data
        t.check("Order #XYZ789 placed", 1, sender_id="order_bot")
        # shipping_bot (no channel to order_bot) references the same data
        result = t.check(
            "Shipping order #XYZ789",
            2, sender_id="shipping_bot",
        )
        assert result["detected"] is True
        assert "unauthorized_data_access" in result["flags"]

    def test_authorized_access_no_flag(self):
        t = InformationFlowTracker()
        t.register_channel("order_bot", "inventory_bot")
        t.check("Order #ABC123", 1, sender_id="order_bot")
        result = t.check("Order #ABC123 in stock", 2, sender_id="inventory_bot")
        assert "unauthorized_data_access" not in result.get("flags", [])

    def test_multi_hop_authorized(self):
        t = InformationFlowTracker()
        t.register_channel("a", "b")
        t.register_channel("b", "c")
        t.check("Order #MULTI1", 1, sender_id="a")
        result = t.check("Order #MULTI1 forwarded", 2, sender_id="c")
        # c has a path through b to a, so should be authorized
        assert "unauthorized_data_access" not in result.get("flags", [])


class TestUnregisteredChannel:
    def test_detects_unregistered_channel(self):
        t = InformationFlowTracker()
        t.register_channel("a", "b")
        t.register_channel("a", "c")
        # d and a are known agents but have no channel
        t.check("msg", 1, sender_id="a")
        result = t.check("msg", 2, sender_id="d", receiver_id="a")
        # d is not a known agent yet, so this won't trigger
        # But if d is known:
        t._known_agents.add("d")
        result = t.check("msg", 3, sender_id="d", receiver_id="a")
        assert "unregistered_channel" in result["flags"]


class TestReceiverScopeViolation:
    def test_detects_receiver_scope_violation(self):
        t = InformationFlowTracker()
        t.set_agent_scope("shipping_bot", ["order_id", "sku"])
        result = t.check(
            "Customer email: admin@corp.com",
            1, sender_id="order_bot", receiver_id="shipping_bot",
        )
        assert result["detected"] is True
        assert "receiver_scope_violation" in result["flags"]


class TestEntityExtraction:
    def test_extracts_order_id(self):
        t = InformationFlowTracker()
        result = t.check("Order #ABC123", 1, sender_id="bot")
        assert result["evidence"]["entities_found"] > 0

    def test_extracts_email(self):
        t = InformationFlowTracker()
        result = t.check("Contact user@domain.com", 1, sender_id="bot")
        assert result["evidence"]["entities_found"] > 0

    def test_extracts_dollar_amount(self):
        t = InformationFlowTracker()
        result = t.check("Total: $1,250.00", 1, sender_id="bot")
        assert result["evidence"]["entities_found"] > 0

    def test_extracts_uuid(self):
        t = InformationFlowTracker()
        result = t.check(
            "Session: 550e8400-e29b-41d4-a716-446655440000",
            1, sender_id="bot",
        )
        assert result["evidence"]["entities_found"] > 0


class TestSeverity:
    def test_unauthorized_access_is_critical(self):
        t = InformationFlowTracker()
        t.register_channel("a", "b")
        t.check("Order #DATA1", 1, sender_id="a")
        result = t.check("Order #DATA1", 2, sender_id="c")
        if result["detected"]:
            assert result["severity"] in ("critical", "high")

    def test_scope_violation_is_high(self):
        t = InformationFlowTracker()
        t.set_agent_scope("bot", ["order_id"])
        result = t.check("SSN: 123-45-6789", 1, sender_id="bot")
        if result["detected"]:
            assert result["severity"] in ("high", "critical")


class TestCleanMessages:
    def test_no_entities_no_detection(self):
        t = InformationFlowTracker()
        result = t.check("Hello, how are you today?", 1, sender_id="bot")
        assert result["detected"] is False


class TestReset:
    def test_reset_clears_all(self):
        t = InformationFlowTracker()
        t.register_channel("a", "b")
        t.set_agent_scope("a", ["order_id"])
        t.check("Order #ABC123", 1, sender_id="a")
        t.reset()
        assert len(t._provenance) == 0
        assert len(t._channels) == 0
        assert len(t._agent_scopes) == 0


class TestResultFormat:
    def test_result_has_required_fields(self):
        t = InformationFlowTracker()
        result = t.check("msg", 1, sender_id="bot")
        assert "detected" in result
        assert "anomaly_type" in result
        assert "severity" in result
        assert "flags" in result
        assert "evidence" in result
        assert "timestamp_us" in result
        assert result["anomaly_type"] == "INFORMATION_FLOW_VIOLATION"

    def test_evidence_has_violation_details(self):
        t = InformationFlowTracker()
        result = t.check("msg", 1, sender_id="bot")
        assert "violations" in result["evidence"]
        assert "violation_count" in result["evidence"]

    def test_entity_hash_not_raw(self):
        t = InformationFlowTracker()
        t.set_agent_scope("bot", ["order_id"])
        result = t.check("Email: secret@corp.com", 1, sender_id="bot")
        if result["detected"]:
            for v in result["evidence"]["violations"]:
                assert "entity_hash" in v
                assert "secret@corp.com" not in v.get("entity_hash", "")
