"""Shared domain value objects."""

from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal
from typing import Any

from .exceptions import InvalidMoneyError, InvalidQuantityError, InvalidSKUError


def _to_decimal(value: Any) -> Decimal:
    if isinstance(value, bool):
        raise InvalidMoneyError("money value must be a decimal-compatible number")
    if isinstance(value, Decimal):
        return value
    if isinstance(value, int):
        return Decimal(value)
    if isinstance(value, str):
        return Decimal(value)
    raise InvalidMoneyError(f"unsupported money value: {value!r}")


@dataclass(frozen=True)
class Money:
    amount: Decimal

    def __post_init__(self) -> None:
        amount = _to_decimal(self.amount)
        if amount < 0:
            raise InvalidMoneyError("money amount cannot be negative")
        object.__setattr__(self, "amount", amount)

    @classmethod
    def zero(cls) -> "Money":
        return cls(Decimal("0"))

    def scaled_by(self, multiplier: int) -> "Money":
        if isinstance(multiplier, bool) or not isinstance(multiplier, int):
            raise InvalidMoneyError("multiplier must be an integer")
        if multiplier < 0:
            raise InvalidMoneyError("multiplier cannot be negative")
        return Money(self.amount * Decimal(multiplier))

    def percentage(self, rate: Decimal) -> "Money":
        rate_decimal = _to_decimal(rate)
        if rate_decimal < 0 or rate_decimal > 1:
            raise InvalidMoneyError("percentage rate must be between 0 and 1")
        return Money(self.amount * rate_decimal)

    def __add__(self, other: Any) -> "Money":
        if not isinstance(other, Money):
            return NotImplemented
        return Money(self.amount + other.amount)

    def __radd__(self, other: Any) -> "Money":
        if other == 0:
            return self
        return self.__add__(other)

    def __sub__(self, other: Any) -> "Money":
        if not isinstance(other, Money):
            return NotImplemented
        result = self.amount - other.amount
        if result < 0:
            raise InvalidMoneyError("money subtraction cannot produce a negative amount")
        return Money(result)


@dataclass(frozen=True)
class Quantity:
    amount: int

    def __post_init__(self) -> None:
        if isinstance(self.amount, bool) or not isinstance(self.amount, int):
            raise InvalidQuantityError("quantity must be an integer")
        if self.amount <= 0:
            raise InvalidQuantityError("quantity must be greater than zero")


@dataclass(frozen=True)
class SKU:
    code: str

    def __post_init__(self) -> None:
        if not isinstance(self.code, str):
            raise InvalidSKUError("sku must be a string")
        code = self.code.strip()
        if not code:
            raise InvalidSKUError("sku cannot be empty")
        object.__setattr__(self, "code", code)
