"""Money value object.

Money represents a monetary amount with currency.
"""

from decimal import Decimal
from typing import Any, Union


class Money:
    """Money value object.
    
    Represents a monetary amount with currency.
    Immutable and provides monetary operations.
    
    Attributes:
        amount: Decimal amount
        currency: Currency code (e.g., 'IRR', 'USD')
    """
    
    def __init__(self, amount: Union[Decimal, int, float, str], currency: str = "IRR"):
        """Initialize money.
        
        Args:
            amount: Monetary amount
            currency: Currency code (default: IRR for Iranian Rial)
            
        Raises:
            ValueError: If amount is negative or currency is invalid
        """
        self._amount = Decimal(str(amount))
        
        if self._amount < 0:
            raise ValueError("Amount cannot be negative")
        
        if not currency or len(currency) != 3:
            raise ValueError(f"Invalid currency code: {currency}")
        
        self._currency = currency.upper()
    
    @property
    def amount(self) -> Decimal:
        """Get amount.
        
        Returns:
            Decimal: Amount
        """
        return self._amount
    
    @property
    def currency(self) -> str:
        """Get currency.
        
        Returns:
            str: Currency code
        """
        return self._currency
    
    def add(self, other: "Money") -> "Money":
        """Add two money values.
        
        Args:
            other: Other money value
            
        Returns:
            Money: New money object with sum
            
        Raises:
            ValueError: If currencies don't match
        """
        self._check_currency(other)
        return Money(self._amount + other._amount, self._currency)
    
    def subtract(self, other: "Money") -> "Money":
        """Subtract money value.
        
        Args:
            other: Other money value
            
        Returns:
            Money: New money object with difference
            
        Raises:
            ValueError: If currencies don't match or result is negative
        """
        self._check_currency(other)
        result = self._amount - other._amount
        if result < 0:
            raise ValueError("Result cannot be negative")
        return Money(result, self._currency)
    
    def multiply(self, multiplier: Union[int, float, Decimal]) -> "Money":
        """Multiply amount by a factor.
        
        Args:
            multiplier: Multiplication factor
            
        Returns:
            Money: New money object
        """
        result = self._amount * Decimal(str(multiplier))
        return Money(result, self._currency)
    
    def _check_currency(self, other: "Money") -> None:
        """Check if currencies match.
        
        Args:
            other: Other money value
            
        Raises:
            ValueError: If currencies don't match
        """
        if self._currency != other._currency:
            raise ValueError(
                f"Currency mismatch: {self._currency} vs {other._currency}"
            )
    
    def __str__(self) -> str:
        """String representation.
        
        Returns:
            str: Formatted money string
        """
        return f"{self._amount:,.2f} {self._currency}"
    
    def __repr__(self) -> str:
        """Developer representation.
        
        Returns:
            str: Money representation
        """
        return f"Money({self._amount}, '{self._currency}')"
    
    def __eq__(self, other: Any) -> bool:
        """Check equality.
        
        Args:
            other: Other object
            
        Returns:
            bool: True if equal
        """
        if not isinstance(other, Money):
            return False
        return self._amount == other._amount and self._currency == other._currency
    
    def __lt__(self, other: "Money") -> bool:
        """Less than comparison.
        
        Args:
            other: Other money value
            
        Returns:
            bool: True if less than
        """
        self._check_currency(other)
        return self._amount < other._amount
    
    def __le__(self, other: "Money") -> bool:
        """Less than or equal comparison.
        
        Args:
            other: Other money value
            
        Returns:
            bool: True if less than or equal
        """
        self._check_currency(other)
        return self._amount <= other._amount
    
    def __gt__(self, other: "Money") -> bool:
        """Greater than comparison.
        
        Args:
            other: Other money value
            
        Returns:
            bool: True if greater than
        """
        self._check_currency(other)
        return self._amount > other._amount
    
    def __ge__(self, other: "Money") -> bool:
        """Greater than or equal comparison.
        
        Args:
            other: Other money value
            
        Returns:
            bool: True if greater than or equal
        """
        self._check_currency(other)
        return self._amount >= other._amount
    
    def __hash__(self) -> int:
        """Hash value.
        
        Returns:
            int: Hash
        """
        return hash((self._amount, self._currency))

