"""
Autonomy Gate - Controls agent autonomy based on risk level.

Implements four-tier autonomy:
- LOW: Auto-allow (read files, run tests)
- MEDIUM: Conditional (edits OK, commands need review)
- HIGH: Approval required
- CRITICAL: Auto-reject
"""

from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Any, Callable
import logging

from .policy import RiskPolicy, RiskLevel, RiskDecision
from .blocklist import is_blocklisted, get_matching_pattern

logger = logging.getLogger(__name__)


@dataclass
class GateDecision:
    """Decision from the autonomy gate."""
    allowed: bool
    level: RiskLevel
    reason: str
    requires_approval: bool = False
    auto_rejected: bool = False
    matched_blocklist: Optional[str] = None
    timestamp: datetime = None

    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()


class AutonomyGate:
    """
    Controls agent autonomy based on risk classification.

    Routes actions through appropriate approval flows based on
    their risk level.
    """

    def __init__(
        self,
        policy: Optional[RiskPolicy] = None,
        interrupt_handler: Optional[Any] = None,
        db: Optional[Any] = None,
    ):
        """
        Initialize autonomy gate.

        Args:
            policy: Risk policy for classification
            interrupt_handler: Handler for approval requests
            db: Database for logging decisions
        """
        self.policy = policy or RiskPolicy()
        self.interrupt_handler = interrupt_handler
        self.db = db

        # Statistics
        self._stats = {
            "allowed": 0,
            "rejected": 0,
            "pending_approval": 0,
            "auto_rejected": 0,
        }

    def check_action(
        self,
        action_type: str,
        file_path: Optional[str] = None,
        command: Optional[str] = None,
        agent_id: Optional[str] = None,
    ) -> GateDecision:
        """
        Check if an action is allowed.

        Args:
            action_type: Type of action (read, write, execute, etc.)
            file_path: Optional file path involved
            command: Optional command to execute
            agent_id: Agent requesting the action

        Returns:
            GateDecision with allow/deny and reason
        """
        # First check blocklist for commands
        if command and is_blocklisted(command):
            pattern = get_matching_pattern(command)
            decision = GateDecision(
                allowed=False,
                level=RiskLevel.CRITICAL,
                reason=f"Command matches critical blocklist",
                auto_rejected=True,
                matched_blocklist=pattern,
            )
            self._log_decision(decision, agent_id)
            self._stats["auto_rejected"] += 1
            return decision

        # Get risk classification
        if command:
            risk_decision = self.policy.get_decision(command=command)
        elif file_path:
            risk_decision = self.policy.get_decision(file_path=file_path)
        else:
            risk_decision = RiskDecision(
                level=RiskLevel.LOW,
                reason="No file or command provided",
            )

        # Apply autonomy rules based on risk level
        if risk_decision.level == RiskLevel.LOW:
            decision = GateDecision(
                allowed=True,
                level=RiskLevel.LOW,
                reason="Action is low risk, auto-allowed",
            )
            self._stats["allowed"] += 1

        elif risk_decision.level == RiskLevel.MEDIUM:
            # For MEDIUM, allow reads and edits, require approval for commands
            if action_type in ("read", "edit", "write"):
                decision = GateDecision(
                    allowed=True,
                    level=RiskLevel.MEDIUM,
                    reason="Medium risk edit/read allowed",
                )
                self._stats["allowed"] += 1
            else:
                decision = GateDecision(
                    allowed=False,
                    level=RiskLevel.MEDIUM,
                    reason="Medium risk command requires review",
                    requires_approval=True,
                )
                self._stats["pending_approval"] += 1

        elif risk_decision.level == RiskLevel.HIGH:
            decision = GateDecision(
                allowed=False,
                level=RiskLevel.HIGH,
                reason="High risk action requires approval",
                requires_approval=True,
            )
            self._stats["pending_approval"] += 1

        else:  # CRITICAL
            decision = GateDecision(
                allowed=False,
                level=RiskLevel.CRITICAL,
                reason="Critical risk action auto-rejected",
                auto_rejected=True,
            )
            self._stats["auto_rejected"] += 1

        self._log_decision(decision, agent_id)
        return decision

    async def request_approval(
        self,
        decision: GateDecision,
        context: dict,
        agent_id: str,
    ) -> bool:
        """
        Request approval for a blocked action.

        Args:
            decision: The gate decision requiring approval
            context: Additional context for the approval request
            agent_id: Agent requesting approval

        Returns:
            True if approved, False if denied
        """
        if not decision.requires_approval:
            return decision.allowed

        if self.interrupt_handler is None:
            logger.warning("No interrupt handler configured, denying by default")
            return False

        # Request approval through handler
        try:
            response = await self.interrupt_handler.request_approval(
                agent_id=agent_id,
                action_type=context.get("action_type", "unknown"),
                description=decision.reason,
                risk_level=decision.level.value,
                context=context,
            )
            return response.approved
        except Exception as e:
            logger.error(f"Approval request failed: {e}")
            return False

    def _log_decision(self, decision: GateDecision, agent_id: Optional[str]) -> None:
        """Log decision to database if available."""
        if self.db is None:
            return

        try:
            # Log to database
            pass  # Implementation depends on DB schema
        except Exception as e:
            logger.error(f"Failed to log decision: {e}")

    def get_stats(self) -> dict:
        """Get gate statistics."""
        return self._stats.copy()

    def reset_stats(self) -> None:
        """Reset statistics."""
        for key in self._stats:
            self._stats[key] = 0
