"""
Agendex SDK Error Types

These exceptions are raised by AgendexClient.invoke() to indicate
policy decisions that require handling by the caller.
"""
from dataclasses import dataclass
from typing import Optional, Dict, Any


class AgendexError(Exception):
    """Base exception for all Agendex SDK errors."""
    pass


class ConnectionError(AgendexError):
    """Raised when unable to connect to the Agendex proxy."""
    
    def __init__(self, url: str, message: str):
        self.url = url
        self.message = message
        super().__init__(f"Cannot connect to Agendex at {url}: {message}")


class ConfigurationError(AgendexError):
    """Raised when SDK is misconfigured."""
    
    def __init__(self, message: str):
        self.message = message
        super().__init__(message)


@dataclass
class DeniedError(AgendexError):
    """
    Raised when an action is denied by policy.
    
    Attributes:
        action: The action that was denied
        reason: Human-readable explanation
        matched_rule: The policy rule that caused the denial
        request_id: Unique ID for this evaluation request
    """
    action: str
    reason: str
    matched_rule: Optional[str] = None
    request_id: Optional[str] = None
    
    def __str__(self) -> str:
        return f"Action '{self.action}' denied: {self.reason}"
    
    def __post_init__(self):
        # Required for Exception inheritance to work with dataclass
        super().__init__(str(self))


@dataclass
class PendingApprovalError(AgendexError):
    """
    Raised when an action requires human approval.
    
    The caller should surface the approval_id to the user or
    integrate with the approval workflow.
    
    Attributes:
        action: The action that requires approval
        approval_id: Unique ID to track and resolve the approval
        reason: Human-readable explanation
        matched_rule: The policy rule that triggered approval requirement
    """
    action: str
    approval_id: str
    reason: str
    matched_rule: Optional[str] = None
    
    def __str__(self) -> str:
        return f"Action '{self.action}' requires approval (id: {self.approval_id}): {self.reason}"
    
    def __post_init__(self):
        super().__init__(str(self))


@dataclass
class ShadowDecisionError(AgendexError):
    """
    Raised in shadow mode when an action would have been denied.
    
    In shadow mode, actions are not blocked but this error indicates
    what WOULD have happened in enforce mode.
    
    Attributes:
        action: The action that was evaluated
        outcome: The shadow decision (would_allow or would_deny)
        reason: Human-readable explanation
    """
    action: str
    outcome: str  # "would_allow" or "would_deny"
    reason: str
    
    def __str__(self) -> str:
        return f"Action '{self.action}' evaluated in shadow mode: {self.outcome} - {self.reason}"
    
    def __post_init__(self):
        super().__init__(str(self))


