from __future__ import annotations

import unittest

from aegis.engine import assess


class AssessTests(unittest.TestCase):
    def setUp(self) -> None:
        self.context = {
            "version": "0.2",
            "services": [
                {
                    "id": "user-api",
                    "name": "User API",
                    "description": "Public user API",
                }
            ],
            "trust_boundaries": [
                {
                    "id": "public-to-internal",
                    "name": "Public to internal",
                    "crossing_requirements": [
                        {
                            "id": "enforce-authn",
                            "text": "Must authenticate public calls",
                            "severity": "critical",
                        },
                        {
                            "id": "shared-requirement",
                            "text": "Boundary value",
                            "severity": "high",
                        },
                    ],
                }
            ],
            "data_classifications": [
                {
                    "id": "user-pii",
                    "name": "PII",
                    "handling_rules": [
                        {
                            "id": "no-pii-logs",
                            "text": "Never log PII",
                            "severity": "critical",
                        },
                        {
                            "id": "shared-requirement",
                            "text": "Data value",
                            "severity": "medium",
                        },
                    ],
                }
            ],
            "approved_patterns": [
                {
                    "id": "auth-middleware",
                    "name": "Auth middleware",
                    "description": "Use middleware",
                    "when": {
                        "boundary_crossings": ["public-to-internal"],
                        "actions": ["create-endpoint"],
                    },
                    "requirements": [],
                }
            ],
            "anti_patterns": [
                {
                    "id": "raw-sql",
                    "name": "Raw SQL",
                    "description": "Avoid raw SQL",
                    "severity": "critical",
                    "when": {"actions": ["database-query"]},
                    "requirements": [],
                }
            ],
            "authorization_policies": [
                {
                    "id": "admin-only",
                    "name": "Admin only",
                    "description": "Privileged access",
                    "required_controls": [
                        {
                            "id": "enforce-admin-role-check",
                            "text": "Must enforce privileged role check",
                            "severity": "critical",
                        }
                    ],
                }
            ],
            "input_validations": [
                {
                    "id": "request-body",
                    "name": "Request body",
                    "description": "Validate body input",
                    "validation_rules": [
                        {
                            "id": "validate-request-schema",
                            "text": "Request body must match schema",
                            "severity": "high",
                        }
                    ],
                }
            ],
            "security_configs": [
                {
                    "id": "security-headers",
                    "name": "Security headers",
                    "description": "Set security headers",
                    "scope": "edge",
                    "when": {
                        "boundary_crossings": ["public-to-internal"],
                        "auth_scopes": ["admin-only"],
                        "input_sources": ["request-body"],
                        "actions": ["create-endpoint"],
                    },
                    "rules": [
                        {
                            "id": "set-security-headers",
                            "text": "Set baseline security headers",
                            "severity": "medium",
                        }
                    ],
                }
            ],
            "secrets_management": [
                {
                    "id": "no-hardcoded-secrets",
                    "name": "No hardcoded secrets",
                    "description": "Do not hardcode secrets",
                    "when": {"actions": ["create-endpoint"]},
                    "rules": [
                        {
                            "id": "store-secrets-in-manager",
                            "text": "Store secrets in approved manager",
                            "severity": "critical",
                        }
                    ],
                }
            ],
            "pipeline_policies": [
                {
                    "id": "dependency-scanning",
                    "name": "Dependency scanning",
                    "description": "Run dependency scanning in CI",
                    "when": {"actions": ["pipeline-change"]},
                    "rules": [
                        {
                            "id": "run-dependency-vulnerability-scan",
                            "text": "Scan dependencies in CI",
                            "severity": "high",
                        }
                    ],
                }
            ],
            "error_handling": [
                {
                    "id": "safe-error-responses",
                    "name": "Safe error responses",
                    "description": "Do not leak internals",
                    "when": {"actions": ["create-endpoint"]},
                    "rules": [
                        {
                            "id": "no-sensitive-error-details",
                            "text": "Do not disclose sensitive internals in error responses",
                            "severity": "high",
                        }
                    ],
                }
            ],
        }

    def test_assess_matches_extended_security_sections(self) -> None:
        result = assess(
            context=self.context,
            services=["user-api"],
            data_types=["user-pii"],
            boundary_crossing="public-to-internal",
            auth_scope="admin-only",
            input_sources=["request-body"],
            action="create-endpoint",
        )

        requirement_ids = {item["id"] for item in result["requirements"]}
        self.assertIn("enforce-authn", requirement_ids)
        self.assertIn("no-pii-logs", requirement_ids)
        self.assertIn("enforce-admin-role-check", requirement_ids)
        self.assertIn("validate-request-schema", requirement_ids)
        self.assertIn("set-security-headers", requirement_ids)
        self.assertIn("store-secrets-in-manager", requirement_ids)
        self.assertIn("no-sensitive-error-details", requirement_ids)
        self.assertEqual(result["matched"]["trust_boundaries"], ["public-to-internal"])
        self.assertEqual(result["matched"]["data_types"], ["user-pii"])
        self.assertEqual(result["matched"]["services"], ["user-api"])
        self.assertEqual(result["matched"]["authorization_policies"], ["admin-only"])
        self.assertEqual(result["matched"]["input_validations"], ["request-body"])
        self.assertFalse(result["no_match"])
        self.assertEqual(result["warnings"], [])
        self.assertEqual(result["approved_patterns"][0]["id"], "auth-middleware")
        self.assertEqual(result["security_configs"][0]["id"], "security-headers")
        self.assertEqual(result["secrets_management"][0]["id"], "no-hardcoded-secrets")
        self.assertEqual(result["error_handling"][0]["id"], "safe-error-responses")
        self.assertEqual(result["pipeline_policies"], [])

    def test_unknown_boundary_sets_no_match(self) -> None:
        result = assess(
            context=self.context,
            services=["user-api"],
            data_types=["user-pii"],
            boundary_crossing="public-to-unknown",
            auth_scope="admin-only",
            input_sources=["request-body"],
            action="create-endpoint",
        )

        self.assertTrue(result["no_match"])
        self.assertTrue(any("Unknown boundary_crossing" in item for item in result["warnings"]))

    def test_unknown_auth_scope_and_input_source_add_warnings(self) -> None:
        result = assess(
            context=self.context,
            boundary_crossing="public-to-internal",
            auth_scope="unknown-scope",
            input_sources=["unknown-source"],
            action="create-endpoint",
        )

        self.assertFalse(result["no_match"])
        self.assertTrue(any("Unknown auth_scope" in item for item in result["warnings"]))
        self.assertTrue(any("Unknown input source" in item for item in result["warnings"]))

    def test_conflict_resolution_prefers_trust_boundary(self) -> None:
        result = assess(
            context=self.context,
            services=[],
            data_types=["user-pii"],
            boundary_crossing="public-to-internal",
            auth_scope=None,
            input_sources=[],
            action="create-endpoint",
        )

        shared = [item for item in result["requirements"] if item["id"] == "shared-requirement"]
        self.assertEqual(len(shared), 1)
        self.assertEqual(shared[0]["source"], "trust_boundary:public-to-internal")
        self.assertEqual(len(result["conflicts"]), 1)

    def test_security_config_requires_auth_scope_and_input_source_match(self) -> None:
        result = assess(
            context=self.context,
            services=["user-api"],
            data_types=["user-pii"],
            boundary_crossing="public-to-internal",
            action="create-endpoint",
        )

        self.assertEqual(result["security_configs"], [])


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