"""
Policy analyzer for attribution.

Analyzes policy files (tools, context, safety) to find rules
that gated or allowed agent behavior.
"""

from __future__ import annotations

import re
from typing import Any, Dict, List

from llmhq_releaseops.attribution.models import Influence
from llmhq_releaseops.attribution.utils.text_search import (
    extract_keywords,
    truncate_snippet,
)


class PolicyAnalyzer:
    """Analyzes policy files to find rules that influenced behavior."""

    def analyze(
        self,
        action: str,
        policy_type: str,
        policy_content: Dict[str, Any],
        policy_ref: str = "",
        trace_context: Dict[str, Any] = None,
    ) -> List[Influence]:
        """
        Find policy rules that influenced the given action.

        Args:
            action: What agent did.
            policy_type: "tools" | "context" | "safety".
            policy_content: Parsed YAML as dict.
            policy_ref: Artifact reference.
            trace_context: Additional context.

        Returns:
            List of Influence objects, sorted by confidence.
        """
        if policy_type == "tools":
            return self._analyze_tools_policy(action, policy_content, policy_ref)
        elif policy_type == "context":
            return self._analyze_context_policy(action, policy_content, policy_ref, trace_context or {})
        elif policy_type == "safety":
            return self._analyze_safety_policy(action, policy_content, policy_ref)
        else:
            return []

    def _analyze_tools_policy(
        self, action: str, policy: Dict[str, Any], policy_ref: str
    ) -> List[Influence]:
        """Analyze tools policy for matching rules."""
        influences = []
        tools = policy.get("tools", [])
        if not isinstance(tools, list):
            return []

        action_keywords = extract_keywords(action)
        action_numbers = set(re.findall(r"\d+(?:\.\d+)?", action))

        for i, tool in enumerate(tools):
            if not isinstance(tool, dict):
                continue
            tool_name = tool.get("name", "")
            tool_keywords = extract_keywords(tool_name)

            # Check if action keywords match tool name
            overlap = set(action_keywords) & set(tool_keywords)
            if not overlap:
                continue

            confidence = 0.7  # Base: relevant match
            confidence_reasons = ["tool name keyword match"]
            reasoning_parts = [f"Tool rule '{tool_name}' matches action keywords"]

            # Check conditions
            conditions = tool.get("conditions", [])
            for cond in conditions:
                if not isinstance(cond, dict):
                    continue
                value = cond.get("value")
                if value is not None and str(value) in action_numbers:
                    confidence = 0.9  # Exact condition match
                    confidence_reasons.append(f"condition value {value} matches")
                    reasoning_parts.append(
                        f"Condition value {value} matches action"
                    )

            # Check allowed/denied
            if "allowed" in tool:
                allowed = tool["allowed"]
                reasoning_parts.append(
                    f"Tool is {'allowed' if allowed else 'denied'}"
                )
                if allowed:
                    confidence = max(confidence, 0.85)
                    confidence_reasons.append("tool explicitly allowed")

            influences.append(Influence(
                artifact_type="policy",
                artifact_ref=policy_ref,
                location=f"rule {i + 1}",
                content_snippet=truncate_snippet(str(tool)),
                confidence=min(confidence, 0.95),
                reasoning=". ".join(reasoning_parts),
                keywords_matched=list(overlap),
                confidence_reason="; ".join(confidence_reasons),
            ))

        return sorted(influences, key=lambda i: i.confidence, reverse=True)

    def _analyze_context_policy(
        self,
        action: str,
        policy: Dict[str, Any],
        policy_ref: str,
        trace_context: Dict[str, Any],
    ) -> List[Influence]:
        """Analyze context/retrieval policy."""
        influences = []
        retrieval = policy.get("retrieval", {})
        if not retrieval:
            return []

        sources = retrieval.get("sources", [])
        if sources:
            influences.append(Influence(
                artifact_type="policy",
                artifact_ref=policy_ref,
                location="retrieval.sources",
                content_snippet=truncate_snippet(str(sources)),
                confidence=0.5,
                reasoning=f"Context policy defines {len(sources)} retrieval sources",
                confidence_reason="context policy source listing (indirect influence)",
            ))

        return influences

    def _analyze_safety_policy(
        self, action: str, policy: Dict[str, Any], policy_ref: str
    ) -> List[Influence]:
        """Analyze safety/constraint policy."""
        influences = []
        constraints = policy.get("constraints", [])
        if not isinstance(constraints, list):
            return []

        action_keywords = extract_keywords(action)
        action_numbers = set(re.findall(r"\d+(?:\.\d+)?", action))

        for i, constraint in enumerate(constraints):
            if not isinstance(constraint, dict):
                continue
            constraint_type = constraint.get("type", "")
            constraint_keywords = extract_keywords(constraint_type)

            overlap = set(action_keywords) & set(constraint_keywords)
            if not overlap:
                # Check numeric values in constraint
                max_val = constraint.get("max_amount") or constraint.get("max_value")
                if max_val is not None and action_numbers:
                    influences.append(Influence(
                        artifact_type="policy",
                        artifact_ref=policy_ref,
                        location=f"constraint {i + 1}",
                        content_snippet=truncate_snippet(str(constraint)),
                        confidence=0.7,
                        reasoning=f"Constraint has limit {max_val}, action involves numbers {action_numbers}",
                        confidence_reason=f"numeric constraint limit {max_val} with action numbers",
                    ))
                continue

            influences.append(Influence(
                artifact_type="policy",
                artifact_ref=policy_ref,
                location=f"constraint {i + 1}",
                content_snippet=truncate_snippet(str(constraint)),
                confidence=0.7,
                reasoning=f"Safety constraint '{constraint_type}' matches action keywords",
                keywords_matched=list(overlap),
                confidence_reason="safety constraint keyword match",
            ))

        return sorted(influences, key=lambda i: i.confidence, reverse=True)
