"""Tests for Security Gate (Quality Gate #19)."""

import pytest

from cyberbrain26.security_plane import (
    AIFirewall,
    ArchitectureGate,
    AuditLog,
    GateStatus,
    PermissionGateway,
    PolicyEngine,
    PolicyMode,
    PrivacyGate,
    QualityGateRunner,
    SecurityGate,
    TrustLevel,
)
from cyberbrain26.security_plane.identity import AgentIdentity, PermissionScope


@pytest.fixture
def trusted_agent_data() -> dict:
    return {
        "agent_id": "agent-1",
        "identity": AgentIdentity(
            agent_id="agent-1",
            role="coder",
            trust_level=TrustLevel.TRUSTED,
            scopes={PermissionScope.READ_FILE, PermissionScope.SHELL},
        ),
        "trust_level": "trusted",
    }


@pytest.fixture
def security_gate() -> SecurityGate:
    return SecurityGate()


@pytest.fixture
def full_security_stack():
    gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
    engine = PolicyEngine(mode=PolicyMode.SAFE)
    audit = AuditLog()
    firewall = AIFirewall()
    return gateway, engine, audit, firewall


class TestSecurityGateInit:
    def test_gate_id(self, security_gate: SecurityGate) -> None:
        assert security_gate.GATE_ID == "gate-19"

    def test_gate_name(self, security_gate: SecurityGate) -> None:
        assert security_gate.GATE_NAME == "Security Gate"

    def test_gate_level(self, security_gate: SecurityGate) -> None:
        assert security_gate.LEVEL.value == "critical"


class TestSecurityGatePasses:
    def test_security_gate_passes_with_all_components(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        assert result.status == GateStatus.PASS
        assert result.failed == 0
        assert result.overall_passed is True

    def test_security_gate_fails_without_permission_gateway(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=None,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        assert result.status == GateStatus.FAIL
        perm_check = next(c for c in result.checks if c.name == "permission_gateway_active")
        assert perm_check.status == GateStatus.FAIL

    def test_security_gate_fails_without_policy_engine(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=None,
            audit_log=audit,
            firewall=firewall,
        )

        assert result.status == GateStatus.FAIL
        policy_check = next(c for c in result.checks if c.name == "policy_engine_configured")
        assert policy_check.status == GateStatus.FAIL


class TestSecurityGateChecks:
    def test_all_agents_have_identity(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        identity_check = next(c for c in result.checks if c.name == "all_agents_have_identity")
        assert identity_check.status == GateStatus.PASS

    def test_no_untrusted_agents(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        untrusted_check = next(c for c in result.checks if c.name == "no_untrusted_agents")
        assert untrusted_check.status == GateStatus.PASS

    def test_untrusted_agent_fails_identity_check(
        self, security_gate: SecurityGate
    ) -> None:
        untrusted_data = {
            "agent_id": "agent-bad",
            "identity": AgentIdentity(
                agent_id="agent-bad",
                role="coder",
                trust_level=TrustLevel.UNTRUSTED,
            ),
            "trust_level": "untrusted",
        }
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[untrusted_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        untrusted_check = next(c for c in result.checks if c.name == "no_untrusted_agents")
        assert untrusted_check.status == GateStatus.FAIL

    def test_audit_logging_enabled(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        audit_check = next(c for c in result.checks if c.name == "audit_logging_enabled")
        assert audit_check.status == GateStatus.PASS

    def test_audit_logging_disabled(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=None,
            firewall=firewall,
        )

        audit_check = next(c for c in result.checks if c.name == "audit_logging_enabled")
        assert audit_check.status == GateStatus.FAIL

    def test_sensitive_actions_requirement_in_safe_mode(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        approval_check = next(
            c for c in result.checks if c.name == "sensitive_actions_require_approval"
        )
        assert approval_check.status == GateStatus.PASS


class TestQualityGateRunner:
    def test_runner_includes_security_gate(self) -> None:
        runner = QualityGateRunner()
        gate_names = [g.GATE_NAME for g in runner.gates]
        assert "Security Gate" in gate_names

    def test_runner_security_gate_passes(
        self, trusted_agent_data: dict
    ) -> None:
        runner = QualityGateRunner()
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        results = runner.run_all(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
            retention_days=90,
        )

        security_result = next(r for r in results if r.gate_name == "Security Gate")
        assert security_result.status == GateStatus.PASS
        assert security_result.overall_passed is True

    def test_runner_all_passed_property(
        self, trusted_agent_data: dict
    ) -> None:
        runner = QualityGateRunner()
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        runner.run_all(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
            retention_days=90,
        )

        assert runner.all_passed is True


class TestPrivacyGate:
    def test_privacy_gate_passes_with_default_retention(
        self, security_gate: SecurityGate, trusted_agent_data: dict
    ) -> None:
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        result = security_gate.evaluate(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        assert result.status == GateStatus.PASS
        assert result.failed == 0
        assert result.overall_passed is True


class TestPrivacyGateDirect:
    def test_privacy_gate_id(self) -> None:
        gate = PrivacyGate()
        assert gate.GATE_ID == "gate-20"
        assert gate.GATE_NAME == "Privacy Gate"
        assert gate.LEVEL.value == "critical"

    def test_privacy_gate_passes_with_retention_90(self) -> None:
        gate = PrivacyGate()
        result = gate.evaluate(retention_days=90)
        assert result.status == GateStatus.PASS
        assert result.failed == 0
        retention_check = next(c for c in result.checks if c.name == "retention_policy_set")
        assert retention_check.status == GateStatus.PASS
        assert "90" in retention_check.detail

    def test_privacy_gate_fails_without_retention(self) -> None:
        gate = PrivacyGate()
        result = gate.evaluate(retention_days=None)
        assert result.status == GateStatus.FAIL
        retention_check = next(c for c in result.checks if c.name == "retention_policy_set")
        assert retention_check.status == GateStatus.FAIL

    def test_privacy_gate_all_checks_pass(self) -> None:
        gate = PrivacyGate()
        result = gate.evaluate(
            telemetry_enabled=False,
            data_minimization=True,
            pii_detection=True,
            data_residency="EU",
            retention_days=90,
            pseudonymous_ids=True,
        )
        assert result.status == GateStatus.PASS
        assert result.failed == 0
        assert result.overall_passed is True


class TestArchitectureGateDirect:
    def test_architecture_gate_id(self) -> None:
        gate = ArchitectureGate()
        assert gate.GATE_ID == "gate-21"
        assert gate.GATE_NAME == "Architecture Gate"
        assert gate.LEVEL.value == "high"

    def test_architecture_gate_passes_with_defaults(self) -> None:
        gate = ArchitectureGate()
        result = gate.evaluate()
        assert result.status == GateStatus.PASS
        assert result.failed == 0
        assert result.overall_passed is True


class TestAllGatesPass:
    def test_all_3_gates_pass_with_runner(
        self, trusted_agent_data: dict
    ) -> None:
        runner = QualityGateRunner()
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        results = runner.run_all(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        gate_names = [r.gate_name for r in results]
        assert "Security Gate" in gate_names
        assert "Privacy Gate" in gate_names
        assert "Architecture Gate" in gate_names

        for result in results:
            assert result.status == GateStatus.PASS, (
                f"{result.gate_name} failed: {result.failed} failures"
            )
            assert result.overall_passed is True

    def test_runner_all_passed_with_all_gates(
        self, trusted_agent_data: dict
    ) -> None:
        runner = QualityGateRunner()
        gateway = PermissionGateway(PolicyEngine(mode=PolicyMode.SAFE))
        engine = PolicyEngine(mode=PolicyMode.SAFE)
        audit = AuditLog()
        firewall = AIFirewall()

        runner.run_all(
            agents=[trusted_agent_data],
            permission_gateway=gateway,
            policy_engine=engine,
            audit_log=audit,
            firewall=firewall,
        )

        assert runner.all_passed is True
        assert len(runner.gates) == 3


class TestSecretLeakDetection:
    def test_inspect_tool_result_blocks_api_key(self) -> None:
        firewall = AIFirewall()
        result = firewall.inspect_tool_result(
            'Here is your data: sk-abc123def456ghi789jkl012mno345pqr'
        )
        assert result.blocked
        assert any(
            t.threat_type.value == "credential_access" for t in result.threats
        )

    def test_inspect_model_output_blocks_private_key(self) -> None:
        firewall = AIFirewall()
        result = firewall.inspect_model_output(
            "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"
        )
        assert result.blocked

    def test_inspect_tool_result_allows_clean_output(self) -> None:
        firewall = AIFirewall()
        result = firewall.inspect_tool_result("Results computed successfully.")
        assert not result.blocked
