"""
Guardrails Default Policy Evaluation Tests

Tests cover two real-world usage patterns:

1. Studio (policy creation): Verify default policy data structures, categories,
   and Cedar text are correctly embedded and accessible for UI display.

2. Shield (policy evaluation): Load actual guardrails default policies and
   evaluate detection results against them, verifying allow/deny decisions
   and determining_policies match expected policy IDs.
"""

import pytest

from highflame_policy import PolicyEngine
from highflame_policy.guardrails_defaults import (
    GUARDRAILS_DEFAULTS,
    GUARDRAILS_TEMPLATES,
    GUARDRAILS_CATEGORIES,
    get_guardrails_defaults_by_category,
)


# =============================================================================
# STUDIO: DATA STRUCTURE TESTS
# =============================================================================


class TestGuardrailsDefaultsData:
    def test_categories_count(self):
        assert len(GUARDRAILS_CATEGORIES) == 5

    def test_category_ids(self):
        ids = [c.id for c in GUARDRAILS_CATEGORIES]
        assert ids == [
            "security",
            "privacy",
            "trust_safety",
            "agentic_security",
            "organization",
        ]

    def test_defaults_count(self):
        assert len(GUARDRAILS_DEFAULTS) == 7

    def test_templates_count(self):
        assert len(GUARDRAILS_TEMPLATES) == 9

    def test_filter_defaults_by_category(self):
        assert len(get_guardrails_defaults_by_category("security")) == 2
        assert len(get_guardrails_defaults_by_category("privacy")) == 1
        assert len(get_guardrails_defaults_by_category("trust_safety")) == 1
        assert len(get_guardrails_defaults_by_category("agentic_security")) == 2
        assert len(get_guardrails_defaults_by_category("organization")) == 1

    def test_all_defaults_have_cedar_text(self):
        for d in GUARDRAILS_DEFAULTS:
            assert len(d.cedar_text) > 0, f"Default {d.id} has empty cedar_text"

    def test_baseline_is_permit(self):
        baseline = next(d for d in GUARDRAILS_DEFAULTS if d.id == "baseline-default")
        assert "permit" in baseline.cedar_text


# =============================================================================
# SHIELD: BATCH EVALUATION TESTS
# =============================================================================


def _safe_context(**overrides):
    """Build a context map with safe defaults for Guardrails evaluation."""
    ctx = {
        "request_id": "req-001",
        "timestamp": 1700000000000,
        "direction": "input",
        "content_type": "prompt",
        "detector_count": 3,
        "injection_score": 0,
        "jailbreak_score": 0,
        "contains_secrets": False,
        "secret_count": 0,
        "secret_types": [],
        "pii_detected": False,
        "pii_count": 0,
        "pii_types": [],
        "violence_score": 0,
        "hate_speech_score": 0,
        "sexual_score": 0,
        "weapons_score": 0,
        "crime_score": 0,
        "profanity_score": 0,
        "tool_name": "",
        "tool_risk_score": 0,
        "tool_is_sensitive": False,
        "tool_category": "safe",
        "tool_is_builtin": True,
        "suspicious_pattern": False,
        "pattern_type": "none",
        "sequence_risk": 0,
        "loop_detected": False,
        "loop_count": 0,
        "loop_tool": "",
        "budget_remaining_pct": 100,
        "budget_exceeded": False,
        "account_id": "acct-1",
    }
    ctx.update(overrides)
    return ctx


class TestGuardrailsBatchEvaluation:
    @pytest.fixture
    def all_defaults_cedar(self):
        """Combine all default policies for batch evaluation."""
        return "\n".join(d.cedar_text for d in GUARDRAILS_DEFAULTS)

    def test_allow_clean_request(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"process_prompt"',
            resource_type="Guardrails::App",
            resource_id="my-app",
            context=_safe_context(),
        )

        assert decision.is_allowed()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "baseline-permit-all" in policy_ids

    def test_deny_when_secrets_detected(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"process_prompt"',
            resource_type="Guardrails::App",
            resource_id="my-app",
            context=_safe_context(
                contains_secrets=True,
                secret_count=1,
                secret_types=["aws_access_key"],
            ),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "secrets-block-all" in policy_ids

    def test_deny_on_high_injection_score(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"process_prompt"',
            resource_type="Guardrails::App",
            resource_id="my-app",
            context=_safe_context(injection_score=92),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "injection-block-high-confidence" in policy_ids

    def test_deny_dangerous_tool_calls(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"call_tool"',
            resource_type="Guardrails::Session",
            resource_id="sess-1",
            context=_safe_context(
                tool_name="shell",
                tool_risk_score=95,
                tool_is_sensitive=True,
                tool_category="dangerous",
                tool_is_builtin=False,
            ),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "tool-block-dangerous" in policy_ids
        assert "tool-block-shell-commands" in policy_ids

    def test_deny_on_data_exfiltration(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"call_tool"',
            resource_type="Guardrails::Session",
            resource_id="sess-1",
            context=_safe_context(
                suspicious_pattern=True,
                pattern_type="data_exfiltration",
                sequence_risk=85,
            ),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "agentic-block-exfiltration" in policy_ids
        assert "agentic-block-high-sequence-risk" in policy_ids

    def test_deny_when_budget_exceeded(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"process_prompt"',
            resource_type="Guardrails::App",
            resource_id="my-app",
            context=_safe_context(
                budget_exceeded=True,
                budget_remaining_pct=0,
            ),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "agentic-block-budget-exceeded" in policy_ids

    def test_deny_on_critical_toxicity(self, all_defaults_cedar):
        engine = PolicyEngine()
        engine.load_policy(all_defaults_cedar)

        decision = engine.evaluate(
            principal_type="Guardrails::User",
            principal_id="user-123",
            action='Guardrails::Action::"process_prompt"',
            resource_type="Guardrails::App",
            resource_id="my-app",
            context=_safe_context(violence_score=95),
        )

        assert decision.is_denied()
        policy_ids = [dp.id for dp in decision.determining_policies]
        assert "toxicity-block-critical" in policy_ids
