"""Bridge adapter — wraps existing guardrail config objects into GuardrailEnv."""

from __future__ import annotations

import fnmatch as _fnmatch
import time
from typing import Any, Dict, List, Optional

from briefcase.guardrails.framework import (
    BaseGuardrailEnv,
    Effect,
    EvalRequest,
    EvalResult,
    Explanation,
    PolicySpace,
    SpaceBound,
)


class GuardrailBridge(BaseGuardrailEnv):
    """Adapter that wraps existing guardrail configuration objects into a GuardrailEnv.

    This bridges the gap between the v2 guardrail dataclasses
    (AgentGuardrail, ToolGuardrail, DataGuardrail from Cedar compilation tests)
    and the v3 GuardrailEnv protocol. Existing Cedar evaluation paths are
    untouched — this adapter maps the interface at the boundary.

    Usage:
        from briefcase.guardrails.bridge import GuardrailBridge

        bridge = GuardrailBridge(
            agent_guardrails=[agent_g],
            tool_guardrails=[tool_g],
            data_guardrails=[data_g],
        )
        result = bridge.evaluate(request)
    """

    def __init__(
        self,
        agent_guardrails: Optional[List[Any]] = None,
        tool_guardrails: Optional[List[Any]] = None,
        data_guardrails: Optional[List[Any]] = None,
        name: str = "guardrail-bridge",
    ):
        self._name = name
        self._agent_guardrails = agent_guardrails or []
        self._tool_guardrails = tool_guardrails or []
        self._data_guardrails = data_guardrails or []
        self._request_space = self._build_space()

    def _build_space(self) -> PolicySpace:
        """Derive PolicySpace from existing guardrail configurations."""
        agents: List[str] = []
        resources: List[str] = []
        context_schema: Dict[str, SpaceBound] = {}

        for g in self._agent_guardrails:
            if hasattr(g, "agents"):
                agents.extend(g.agents)
            if hasattr(g, "allow_paths"):
                resources.extend(g.allow_paths)
            if hasattr(g, "min_confidence") and g.min_confidence is not None:
                context_schema["confidence"] = SpaceBound(
                    low=0.0, high=1.0,
                )

        return PolicySpace(
            agents=sorted(set(agents)),
            resources=sorted(set(resources)),
            context_schema=context_schema,
        )

    def _evaluate_agent_guardrails(
        self, request: EvalRequest
    ) -> Optional[EvalResult]:
        """Evaluate AgentGuardrail objects against the request."""
        for g in self._agent_guardrails:
            if not getattr(g, "enabled", True):
                continue

            # Agent check
            if hasattr(g, "agents") and g.agents:
                if request.agent not in g.agents:
                    return EvalResult(
                        effect=Effect.DENY,
                        guardrail_name=self.name,
                        reason=f"Agent '{request.agent}' not in {g.name} agents",
                        metadata={"source_guardrail": g.name, "check": "agent"},
                    )

            # Deny paths check
            if hasattr(g, "deny_paths"):
                for dp in g.deny_paths:
                    if _fnmatch.fnmatch(request.resource, dp) or request.resource.startswith(dp):
                        return EvalResult(
                            effect=Effect.DENY,
                            guardrail_name=self.name,
                            reason=f"Resource '{request.resource}' in deny path '{dp}'",
                            metadata={"source_guardrail": g.name, "check": "deny_path"},
                        )

            # Allow paths check
            if hasattr(g, "allow_paths") and g.allow_paths:
                path_ok = any(
                    _fnmatch.fnmatch(request.resource, p) or request.resource.startswith(p)
                    for p in g.allow_paths
                )
                if not path_ok:
                    return EvalResult(
                        effect=Effect.DENY,
                        guardrail_name=self.name,
                        reason=f"Resource '{request.resource}' not in allowed paths",
                        metadata={"source_guardrail": g.name, "check": "allow_path"},
                    )

            # Confidence check
            if hasattr(g, "min_confidence") and g.min_confidence is not None:
                confidence = request.context.get("confidence", 0.0)
                if confidence < g.min_confidence:
                    return EvalResult(
                        effect=Effect.DENY,
                        guardrail_name=self.name,
                        reason=(
                            f"Confidence {confidence} below {g.name} "
                            f"threshold {g.min_confidence}"
                        ),
                        metadata={
                            "source_guardrail": g.name,
                            "check": "confidence",
                        },
                    )

            # Required tags check
            if hasattr(g, "required_tags"):
                for tag_key, expected in g.required_tags.items():
                    actual = request.context.get(tag_key)
                    if actual != expected:
                        return EvalResult(
                            effect=Effect.DENY,
                            guardrail_name=self.name,
                            reason=(
                                f"Tag '{tag_key}' mismatch in {g.name}: "
                                f"expected {expected}, got {actual}"
                            ),
                            metadata={
                                "source_guardrail": g.name,
                                "check": "required_tag",
                            },
                        )

        return None  # all passed

    def _evaluate_tool_guardrails(
        self, request: EvalRequest
    ) -> Optional[EvalResult]:
        """Evaluate ToolGuardrail objects (action-based)."""
        for g in self._tool_guardrails:
            if not getattr(g, "enabled", True):
                continue
            if hasattr(g, "allowed_tools"):
                if request.action not in g.allowed_tools:
                    return EvalResult(
                        effect=Effect.DENY,
                        guardrail_name=self.name,
                        reason=f"Action '{request.action}' not allowed by {g.name}",
                        metadata={"source_guardrail": g.name, "check": "tool"},
                    )
        return None

    def _evaluate_data_guardrails(
        self, request: EvalRequest
    ) -> Optional[EvalResult]:
        """Evaluate DataGuardrail objects (resource-based)."""
        for g in self._data_guardrails:
            if not getattr(g, "enabled", True):
                continue
            if hasattr(g, "allowed_schemas"):
                schema = request.context.get("schema")
                if schema and schema not in g.allowed_schemas:
                    return EvalResult(
                        effect=Effect.DENY,
                        guardrail_name=self.name,
                        reason=f"Schema '{schema}' not allowed by {g.name}",
                        metadata={"source_guardrail": g.name, "check": "schema"},
                    )
        return None

    def evaluate(self, request: EvalRequest) -> EvalResult:
        """Evaluate request against all existing guardrail config objects."""
        start = time.monotonic()

        # Evaluate in order: agent → tool → data
        for evaluator in (
            self._evaluate_agent_guardrails,
            self._evaluate_tool_guardrails,
            self._evaluate_data_guardrails,
        ):
            result = evaluator(request)
            if result is not None:
                result.eval_time_ms = (time.monotonic() - start) * 1000.0
                return result

        elapsed = (time.monotonic() - start) * 1000.0
        return EvalResult(
            effect=Effect.ALLOW,
            guardrail_name=self.name,
            reason="All guardrail checks passed",
            eval_time_ms=elapsed,
        )

    def explain(self, result: EvalResult) -> Explanation:
        """Produce bridge-specific explanation."""
        return Explanation(
            decision_id=result.metadata.get("request_id"),
            effect=result.effect,
            guardrail_name=result.guardrail_name,
            policy_applied={
                "name": "guardrail_bridge",
                "source_guardrail": result.metadata.get("source_guardrail"),
                "check": result.metadata.get("check"),
            },
            eval_time_ms=result.eval_time_ms,
        )
