"""
TrustExploitationGuard (L17)

Detects and prevents human-agent trust exploitation attacks.
Implements ASI09 from OWASP Agentic Applications 2026.

Threat Model:
- ASI09: Improper Human-Agent Trust Placement
- Trust boundary violations
- Permission escalation
- Goal hijacking
- Unauthorized autonomous actions

Protection Capabilities:
- Trust boundary enforcement
- Permission escalation detection
- Goal consistency monitoring
- Action authorization verification
- Human-in-the-loop enforcement
"""

from __future__ import annotations

import re
import time
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Set


def _now_ms() -> int:
    return int(time.time() * 1000)


@dataclass
class TrustExploitationGuardConfig:
    """Configuration for TrustExploitationGuard."""
    max_trust_level: int = 100
    human_approval_required: List[str] = field(
        default_factory=lambda: ["payment_process", "credential_access", "user_data_export", "config_modify"]
    )
    max_autonomous_actions: int = 10
    monitor_goal_consistency: bool = True
    trust_decay_rate: float = 10.0  # per hour
    detect_permission_escalation: bool = True
    sensitive_actions: List[str] = field(default_factory=list)
    initial_goal: str = ""


DEFAULT_SENSITIVE_ACTIONS = [
    "file_delete",
    "file_write",
    "database_modify",
    "api_call_external",
    "email_send",
    "payment_process",
    "credential_access",
    "config_modify",
    "user_data_access",
    "system_command",
]


@dataclass
class AgentAction:
    """An agent action to be validated."""
    action_id: str
    action_type: str
    target: str
    autonomous: bool
    timestamp: int
    permissions: Optional[List[str]] = None
    reason: Optional[str] = None
    goal: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None


@dataclass
class TrustContext:
    """Trust context for a session."""
    trust_level: float
    session_id: str
    action_history: List[AgentAction]
    current_goal: str
    initial_goal: str
    granted_permissions: Set[str]
    last_human_interaction: int
    autonomous_actions_count: int


@dataclass
class TrustAnalysis:
    """Analysis sub-result."""
    trust_level: float
    trust_change: float
    permission_escalation: bool
    goal_deviation: bool
    requires_human_approval: bool
    autonomous_action_count: int
    trust_boundary_violated: bool


@dataclass
class TrustExploitationResult:
    """Result of trust exploitation validation."""
    allowed: bool
    reason: str
    violations: List[str]
    request_id: str
    analysis: TrustAnalysis
    recommendations: List[str] = field(default_factory=list)
    human_approval_required: Optional[bool] = None
    approval_reason: Optional[str] = None


@dataclass
class _TrustPattern:
    name: str
    pattern: re.Pattern[str]
    severity: int


class TrustExploitationGuard:
    """Detects and prevents human-agent trust exploitation attacks."""

    TRUST_EXPLOITATION_PATTERNS: List[_TrustPattern] = [
        # Permission escalation
        _TrustPattern("permission_request", re.compile(r'grant\s+(?:me|agent)\s+(?:full|admin|root|elevated)\s+(?:access|permissions?)', re.IGNORECASE), 50),
        _TrustPattern("access_all", re.compile(r'access\s+(?:to\s+)?(?:all|everything|any)\s+(?:data|files?|resources?)', re.IGNORECASE), 45),
        _TrustPattern("bypass_approval", re.compile(r'without\s+(?:human\s+)?(?:approval|confirmation|verification|authorization)', re.IGNORECASE), 55),

        # Goal hijacking
        _TrustPattern("goal_change", re.compile(r'new\s+(?:goal|objective|mission|task)[\s:]', re.IGNORECASE), 40),
        _TrustPattern("ignore_original", re.compile(r'ignore\s+(?:original|initial|previous)\s+(?:goal|task|instruction)', re.IGNORECASE), 50),
        _TrustPattern("priority_shift", re.compile(r'more\s+important|higher\s+priority|urgent\s+change', re.IGNORECASE), 35),

        # Trust boundary violations
        _TrustPattern("act_independently", re.compile(r'act\s+(?:on\s+your\s+own|independently|autonomously|without\s+asking)', re.IGNORECASE), 50),
        _TrustPattern("dont_ask", re.compile(r"don't\s+(?:ask|wait|check|verify)\s+(?:for|with)\s+(?:permission|approval|human)", re.IGNORECASE), 55),
        _TrustPattern("trust_me", re.compile(r"trust\s+(?:me|this|the\s+agent)|i(?:'m|\s+am)\s+trusted", re.IGNORECASE), 40),

        # Deceptive actions
        _TrustPattern("hide_action", re.compile(r'secretly|covertly|without\s+showing|hide\s+(?:this|the)\s+(?:action|operation)', re.IGNORECASE), 60),
        _TrustPattern("dont_log", re.compile(r"don't\s+(?:log|record|track|audit)", re.IGNORECASE), 55),
        _TrustPattern("skip_verification", re.compile(r'skip\s+(?:verification|validation|check)', re.IGNORECASE), 45),

        # Resource acquisition
        _TrustPattern("need_more", re.compile(r'need\s+(?:more|additional|extra)\s+(?:access|permissions?|resources?)', re.IGNORECASE), 35),
        _TrustPattern("expand_scope", re.compile(r'expand\s+(?:scope|access|permissions?|capabilities)', re.IGNORECASE), 40),
        _TrustPattern("permanent_access", re.compile(r'permanent\s+(?:access|permission)|always\s+allow', re.IGNORECASE), 50),
    ]

    def __init__(self, config: Optional[TrustExploitationGuardConfig] = None) -> None:
        cfg = config or TrustExploitationGuardConfig()
        self._max_trust_level = cfg.max_trust_level
        self._human_approval_required = list(cfg.human_approval_required)
        self._max_autonomous_actions = cfg.max_autonomous_actions
        self._monitor_goal_consistency = cfg.monitor_goal_consistency
        self._trust_decay_rate = cfg.trust_decay_rate
        self._detect_permission_escalation = cfg.detect_permission_escalation
        self._sensitive_actions = list(cfg.sensitive_actions) if cfg.sensitive_actions else list(DEFAULT_SENSITIVE_ACTIONS)
        self._initial_goal = cfg.initial_goal

        self._contexts: Dict[str, TrustContext] = {}

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def validate_action(
        self,
        action: AgentAction,
        session_id: str,
        request_id: Optional[str] = None,
    ) -> TrustExploitationResult:
        req_id = request_id or f"trust-{_now_ms()}"
        violations: List[str] = []

        context = self._contexts.get(session_id)
        if not context:
            context = self._create_context(session_id)

        # Apply trust decay
        self._apply_trust_decay(context)

        # Track autonomous actions
        if action.autonomous:
            context.autonomous_actions_count += 1

        trust_change: float = 0.0
        permission_escalation = False
        goal_deviation = False
        requires_human_approval = False
        trust_boundary_violated = False
        approval_reason: Optional[str] = None

        # 1. Check if action requires human approval
        if action.action_type in self._human_approval_required:
            requires_human_approval = True
            approval_reason = f'Action type "{action.action_type}" requires human approval'
            if action.autonomous:
                violations.append("autonomous_sensitive_action")
                trust_change -= 20
                trust_boundary_violated = True

        # 2. Check for permission escalation
        if self._detect_permission_escalation and action.permissions:
            for perm in action.permissions:
                if perm not in context.granted_permissions:
                    violations.append(f"permission_escalation: {perm}")
                    permission_escalation = True
                    trust_change -= 15

        # 3. Check reason for trust exploitation patterns
        if action.reason:
            for pat in self.TRUST_EXPLOITATION_PATTERNS:
                if pat.pattern.search(action.reason):
                    violations.append(f"trust_exploitation: {pat.name}")
                    trust_change -= pat.severity / 5
                    trust_boundary_violated = True

        # 4. Check goal consistency
        if self._monitor_goal_consistency and action.goal:
            goal_similarity = self._calculate_goal_similarity(action.goal, context.initial_goal)
            if goal_similarity < 0.5 and context.initial_goal:
                violations.append("goal_deviation")
                goal_deviation = True
                trust_change -= 25

                if goal_similarity < 0.2:
                    violations.append("severe_goal_hijacking")
                    trust_boundary_violated = True
                    requires_human_approval = True
                    approval_reason = "Goal significantly deviated from original"

        # 5. Check for deceptive actions (hidden operations in metadata)
        if action.metadata:
            deceptive_keys = [
                "hidden_action", "actual_action", "real_action",
                "true_action", "bypass_approval", "skip_approval",
            ]
            for key in deceptive_keys:
                if action.metadata.get(key):
                    violations.append(f"deceptive_metadata: {key}")
                    trust_boundary_violated = True
                    trust_change -= 30

                    hidden_action = action.metadata[key]
                    if isinstance(hidden_action, str) and any(
                        s in hidden_action for s in self._sensitive_actions
                    ):
                        violations.append("hidden_sensitive_action")
                        requires_human_approval = True
                        approval_reason = f"Hidden sensitive action detected: {hidden_action}"

            suspicious_flags = [
                "sandbox_escape", "cross_tenant", "impersonate_admin",
                "fabricated_history", "modify_trust", "spawn_agent", "external_storage",
            ]
            for flag in suspicious_flags:
                if action.metadata.get(flag) is True:
                    violations.append(f"suspicious_flag: {flag}")
                    trust_boundary_violated = True
                    trust_change -= 25

        # 6. Check for action-target mismatch (deceptive labeling)
        if action.target:
            sensitive_targets = [
                "credential", "password", "secret", "key", "token",
                "payment", "card", "admin", "root", "system", "config",
            ]
            innocuous_types = [
                "read_only", "check", "view", "list",
                "routine", "maintenance", "status", "health",
            ]

            target_lower = action.target.lower()
            type_lower = action.action_type.lower()

            targets_sensitive = any(s in target_lower for s in sensitive_targets)
            claims_innocuous = any(s in type_lower for s in innocuous_types)

            if targets_sensitive and claims_innocuous:
                violations.append("action_target_mismatch")
                trust_boundary_violated = True
                trust_change -= 20
                requires_human_approval = True
                approval_reason = (
                    f'Suspicious: innocuous action type "{action.action_type}" '
                    f'targeting sensitive resource "{action.target}"'
                )

        # 7. Check autonomous action limit
        if context.autonomous_actions_count >= self._max_autonomous_actions:
            violations.append("autonomous_limit_reached")
            requires_human_approval = True
            approval_reason = f"{context.autonomous_actions_count} autonomous actions without human check-in"

        # 8. Check if action targets sensitive resources
        if action.action_type in self._sensitive_actions:
            trust_change -= 5
            if action.autonomous and context.trust_level < 70:
                violations.append("sensitive_action_low_trust")
                requires_human_approval = True
                approval_reason = "Sensitive action with insufficient trust level"

        # Update trust level
        new_trust_level = max(0, min(self._max_trust_level, context.trust_level + trust_change))
        context.trust_level = new_trust_level

        # Add action to history
        context.action_history.append(action)
        if action.goal:
            context.current_goal = action.goal

        # Determine if action should be blocked
        blocked = (
            trust_boundary_violated
            or (requires_human_approval and action.autonomous)
            or new_trust_level < 20
            or len(violations) >= 3
        )

        # Update context
        self._contexts[session_id] = context

        return TrustExploitationResult(
            allowed=not blocked,
            reason=(
                f"Trust exploitation detected: {', '.join(violations[:3])}"
                if blocked
                else "Action validated"
            ),
            violations=violations,
            request_id=req_id,
            analysis=TrustAnalysis(
                trust_level=new_trust_level,
                trust_change=trust_change,
                permission_escalation=permission_escalation,
                goal_deviation=goal_deviation,
                requires_human_approval=requires_human_approval,
                autonomous_action_count=context.autonomous_actions_count,
                trust_boundary_violated=trust_boundary_violated,
            ),
            recommendations=self._generate_recommendations(violations, new_trust_level, requires_human_approval),
            human_approval_required=requires_human_approval,
            approval_reason=approval_reason,
        )

    def record_human_interaction(
        self, session_id: str, action: Optional[str] = None
    ) -> None:
        context = self._contexts.get(session_id)
        if not context:
            context = self._create_context(session_id)

        context.last_human_interaction = _now_ms()
        context.autonomous_actions_count = 0

        if action == "approve":
            context.trust_level = min(self._max_trust_level, context.trust_level + 10)
        elif action == "deny":
            context.trust_level = max(0, context.trust_level - 20)

        self._contexts[session_id] = context

    def grant_permission(self, session_id: str, permission: str) -> None:
        context = self._contexts.get(session_id)
        if not context:
            context = self._create_context(session_id)
        context.granted_permissions.add(permission)
        self._contexts[session_id] = context

    def revoke_permission(self, session_id: str, permission: str) -> None:
        context = self._contexts.get(session_id)
        if context:
            context.granted_permissions.discard(permission)
            self._contexts[session_id] = context

    def set_initial_goal(self, session_id: str, goal: str) -> None:
        context = self._contexts.get(session_id)
        if not context:
            context = self._create_context(session_id)
        context.initial_goal = goal
        context.current_goal = goal
        self._contexts[session_id] = context

    def get_trust_level(self, session_id: str) -> float:
        context = self._contexts.get(session_id)
        return context.trust_level if context else 50.0

    def reset_session(self, session_id: str) -> None:
        self._contexts.pop(session_id, None)

    # ------------------------------------------------------------------
    # Private helpers
    # ------------------------------------------------------------------

    def _create_context(self, session_id: str) -> TrustContext:
        ctx = TrustContext(
            trust_level=50.0,
            session_id=session_id,
            action_history=[],
            current_goal=self._initial_goal,
            initial_goal=self._initial_goal,
            granted_permissions=set(),
            last_human_interaction=_now_ms(),
            autonomous_actions_count=0,
        )
        self._contexts[session_id] = ctx
        return ctx

    def _apply_trust_decay(self, context: TrustContext) -> None:
        hours_since = (_now_ms() - context.last_human_interaction) / (1000 * 60 * 60)
        decay = hours_since * self._trust_decay_rate
        context.trust_level = max(0, context.trust_level - decay)

    def _calculate_goal_similarity(self, goal1: str, goal2: str) -> float:
        if not goal1 or not goal2:
            return 1.0

        words1 = set(goal1.lower().split())
        words2 = set(goal2.lower().split())

        intersection = words1 & words2
        union = words1 | words2

        if not union:
            return 1.0
        return len(intersection) / len(union)

    def _generate_recommendations(
        self,
        violations: List[str],
        trust_level: float,
        requires_approval: bool,
    ) -> List[str]:
        recommendations: List[str] = []

        if any("permission_escalation" in v for v in violations):
            recommendations.append("Review and explicitly grant required permissions")
        if any("goal" in v for v in violations):
            recommendations.append("Verify goal consistency with user intent")
        if any("autonomous_limit" in v for v in violations):
            recommendations.append("Implement periodic human check-ins")
        if any("trust_exploitation" in v for v in violations):
            recommendations.append("Review agent reasoning for manipulation attempts")
        if trust_level < 30:
            recommendations.append("Trust level critically low - consider session reset")
        if requires_approval:
            recommendations.append("Obtain explicit human approval before proceeding")

        if not recommendations:
            recommendations.append("Continue monitoring agent behavior")

        return recommendations
