"""Shared domain entities."""

from __future__ import annotations

from dataclasses import dataclass, field

from .exceptions import DomainError, InsufficientStockError, InvalidOrderError
from .value_objects import Money, Quantity, SKU


@dataclass
class Customer:
    name: str
    customer_type: "CustomerType" = None  # type: ignore[assignment]

    def __post_init__(self) -> None:
        from .enums import CustomerType

        if not isinstance(self.name, str) or not self.name.strip():
            raise DomainError("customer name cannot be empty")
        if self.customer_type is None:
            self.customer_type = CustomerType.NORMAL
        if not isinstance(self.customer_type, CustomerType):
            raise DomainError("invalid customer type")


@dataclass
class Product:
    sku: SKU
    name: str
    price: Money
    stock_qty: int

    def __post_init__(self) -> None:
        if not isinstance(self.sku, SKU):
            raise DomainError("product sku must be a SKU value object")
        if not isinstance(self.name, str) or not self.name.strip():
            raise DomainError("product name cannot be empty")
        if not isinstance(self.price, Money):
            raise DomainError("product price must be a Money value object")
        if isinstance(self.stock_qty, bool) or not isinstance(self.stock_qty, int):
            raise DomainError("stock_qty must be an integer")
        if self.stock_qty < 0:
            raise DomainError("stock_qty cannot be negative")

    def can_reserve(self, quantity: Quantity) -> bool:
        if not isinstance(quantity, Quantity):
            raise DomainError("quantity must be a Quantity value object")
        return quantity.amount <= self.stock_qty

    def reserve(self, quantity: Quantity) -> None:
        if not self.can_reserve(quantity):
            raise InsufficientStockError(
                f"not enough stock for {self.sku.code}: requested={quantity.amount}, available={self.stock_qty}"
            )
        self.stock_qty -= quantity.amount


@dataclass
class OrderLine:
    product: Product
    quantity: Quantity

    def __post_init__(self) -> None:
        if not isinstance(self.product, Product):
            raise InvalidOrderError("order line requires a valid product")
        if not isinstance(self.quantity, Quantity):
            raise InvalidOrderError("order line requires a valid quantity")

    @property
    def subtotal(self) -> Money:
        return self.product.price.scaled_by(self.quantity.amount)


@dataclass
class Order:
    customer: Customer
    lines: list[OrderLine] = field(default_factory=list)

    def __post_init__(self) -> None:
        if not isinstance(self.customer, Customer):
            raise DomainError("order requires a valid customer")
        if self.lines is None:
            self.lines = []

    def add_line(self, product: Product, quantity: Quantity) -> OrderLine:
        line = OrderLine(product=product, quantity=quantity)
        self.lines.append(line)
        return line

    def total_before_discount(self) -> Money:
        total = Money.zero()
        for line in self.lines:
            total = total + line.subtotal
        return total
