"""
APE Standard Library - Error Types

Explicit error types for deterministic runtime behavior.
All errors are designed to be:
- Deterministic (same input → same error)
- Observable (traceable in execution context)
- Explicit (no implicit conversions or assumptions)
- Localization-ready (code-based, structured context)
"""

from typing import Dict, Any, Optional


class StdLibError(Exception):
    """
    Base class for all stdlib errors.
    
    Structured error format:
    - code: Stable string identifier (API contract)
    - message: English human-readable message
    - context: Structured, serializable context data
    
    This format enables:
    - Machine-readable error codes
    - Localization without code changes
    - Structured error logging
    """
    
    def __init__(self, code: str, message: str, context: Optional[Dict[str, Any]] = None):
        """
        Initialize structured stdlib error.
        
        Args:
            code: Stable error code (e.g., "TYPE_MISMATCH")
            message: English error message
            context: Optional structured context data
        """
        self.code = code
        self.message = message
        self.context = context or {}
        super().__init__(self._format_message())
    
    def _format_message(self) -> str:
        """Format error message for display"""
        if self.context:
            context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
            return f"[{self.code}] {self.message} ({context_str})"
        return f"[{self.code}] {self.message}"
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization"""
        return {
            "code": self.code,
            "message": self.message,
            "context": self.context,
            "error_type": self.__class__.__name__,
        }
    
    def __repr__(self) -> str:
        """Machine-readable representation"""
        return f"{self.__class__.__name__}(code={self.code!r}, message={self.message!r}, context={self.context!r})"


class ComparisonTypeError(StdLibError, TypeError):
    """
    Raised when comparison operations receive incompatible types.
    
    APE requires explicit type matching for comparisons - no implicit coercion.
    """
    
    def __init__(self, operation: str, left_type: type, right_type: type):
        self.operation = operation
        self.left_type = left_type
        self.right_type = right_type
        
        super().__init__(
            code="COMPARISON_TYPE_MISMATCH",
            message="Type mismatch in comparison operation",
            context={
                "operation": operation,
                "left_type": left_type.__name__ if hasattr(left_type, '__name__') else str(left_type),
                "right_type": right_type.__name__ if hasattr(right_type, '__name__') else str(right_type),
            }
        )


class MathTypeError(StdLibError, TypeError):
    """
    Raised when math operations receive non-numeric types.
    
    APE math operations require explicit numeric types (int or float).
    """
    
    def __init__(self, operation: str, param: str, received_type: type):
        self.operation = operation
        self.param = param
        self.received_type = received_type
        
        super().__init__(
            code="MATH_TYPE_ERROR",
            message="Invalid type for math operation",
            context={
                "operation": operation,
                "param": param,
                "received_type": received_type.__name__ if hasattr(received_type, '__name__') else str(received_type),
            }
        )


class DivisionByZeroError(StdLibError, ZeroDivisionError):
    """
    Raised when division by zero is attempted.
    
    APE never allows implicit handling of division by zero.
    """
    
    def __init__(self, numerator):
        self.numerator = numerator
        
        super().__init__(
            code="DIVISION_BY_ZERO",
            message="Division by zero",
            context={
                "numerator": numerator,
            }
        )


class LogicTypeError(StdLibError, TypeError):
    """
    Raised when logical operations receive non-boolean types.
    
    APE requires explicit booleans - no truthy/falsy coercion.
    """
    
    def __init__(self, operation: str, param: str, received_type: type):
        self.operation = operation
        self.param = param
        self.received_type = received_type
        
        super().__init__(
            code="LOGIC_TYPE_ERROR",
            message="Invalid type for logic operation",
            context={
                "operation": operation,
                "param": param,
                "received_type": received_type.__name__ if hasattr(received_type, '__name__') else str(received_type),
            }
        )


class CollectionTypeError(StdLibError, TypeError):
    """
    Raised when collection operations receive wrong types.
    
    APE collection operations are type-strict and deterministic.
    """
    
    def __init__(self, operation: str, expected: str, received_type: str):
        self.operation = operation
        self.expected = expected
        self.received_type = received_type
        
        super().__init__(
            code="COLLECTION_TYPE_ERROR",
            message="Invalid type for collection operation",
            context={
                "operation": operation,
                "expected": expected,
                "received_type": received_type,
            }
        )


class EmptyCollectionError(StdLibError, ValueError):
    """
    Raised when operations require non-empty collections.
    
    APE makes empty collection behavior explicit.
    """
    
    def __init__(self, operation: str):
        self.operation = operation
        
        super().__init__(
            code="EMPTY_COLLECTION",
            message="Operation requires non-empty collection",
            context={
                "operation": operation,
            }
        )


__all__ = [
    'StdLibError',
    'ComparisonTypeError',
    'MathTypeError',
    'DivisionByZeroError',
    'LogicTypeError',
    'CollectionTypeError',
    'EmptyCollectionError',
]
