"""Sales entities."""

from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal

from ..common.entities import Customer, Product
from ..common.value_objects import Money, Quantity
from .enums import SalesOrderState
from .exceptions import InvalidSalesOrderError, InvalidSalesOrderLineError
from .policies import DefaultSalesOrderTransitionPolicy, SalesOrderTransitionPolicy


@dataclass
class SalesOrderLine:
    product: Product
    quantity: Quantity
    unit_price: Money | None = None
    discount_rate: Decimal = Decimal("0")

    def __post_init__(self) -> None:
        if not isinstance(self.product, Product):
            raise InvalidSalesOrderLineError("product must be a Product instance")
        if not isinstance(self.quantity, Quantity):
            raise InvalidSalesOrderLineError("quantity must be a Quantity instance")
        if self.unit_price is None:
            self.unit_price = self.product.price
        if not isinstance(self.unit_price, Money):
            raise InvalidSalesOrderLineError("unit_price must be a Money instance")
        if isinstance(self.discount_rate, bool) or not isinstance(self.discount_rate, Decimal):
            raise InvalidSalesOrderLineError("discount_rate must be a Decimal")
        if self.discount_rate < 0 or self.discount_rate > 1:
            raise InvalidSalesOrderLineError("discount_rate must be between 0 and 1")

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

    @property
    def discount_amount(self) -> Money:
        return self.subtotal.percentage(self.discount_rate)

    @property
    def total(self) -> Money:
        return self.subtotal - self.discount_amount


@dataclass
class SalesOrder:
    customer: Customer
    state: SalesOrderState = SalesOrderState.QUOTATION
    lines: list[SalesOrderLine] = field(default_factory=list)
    name: str | None = None

    def __post_init__(self) -> None:
        if not isinstance(self.customer, Customer):
            raise InvalidSalesOrderError("customer must be a Customer instance")
        if not isinstance(self.state, SalesOrderState):
            raise InvalidSalesOrderError("invalid sales order state")
        if self.name is not None and (not isinstance(self.name, str) or not self.name.strip()):
            raise InvalidSalesOrderError("name must be a non-empty string when provided")
        if any(not isinstance(line, SalesOrderLine) for line in self.lines):
            raise InvalidSalesOrderError("lines must contain SalesOrderLine instances")

    def add_line(
        self,
        product: Product,
        quantity: Quantity,
        *,
        unit_price: Money | None = None,
        discount_rate: Decimal = Decimal("0"),
    ) -> SalesOrderLine:
        self._ensure_editable()
        line = SalesOrderLine(product=product, quantity=quantity, unit_price=unit_price, discount_rate=discount_rate)
        self.lines.append(line)
        return line

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

    @property
    def amount_discount(self) -> Money:
        total = Money.zero()
        for line in self.lines:
            total = total + line.discount_amount
        return total

    @property
    def amount_total(self) -> Money:
        return self.amount_untaxed - self.amount_discount

    def send(self, *, policy: SalesOrderTransitionPolicy | None = None) -> None:
        self._transition(SalesOrderState.SENT, policy=policy)

    def confirm(self, *, policy: SalesOrderTransitionPolicy | None = None) -> None:
        self._transition(SalesOrderState.SALE, policy=policy)

    def mark_done(self, *, policy: SalesOrderTransitionPolicy | None = None) -> None:
        self._transition(SalesOrderState.DONE, policy=policy)

    def cancel(self, *, policy: SalesOrderTransitionPolicy | None = None) -> None:
        self._transition(SalesOrderState.CANCELLED, policy=policy)

    def _transition(self, target_state: SalesOrderState, *, policy: SalesOrderTransitionPolicy | None = None) -> None:
        if policy is None:
            policy = DefaultSalesOrderTransitionPolicy()
        if not policy.can_transition(self.state, target_state):
            raise InvalidSalesOrderError(f"cannot move sales order from {self.state.value} to {target_state.value}")
        self.state = target_state

    def _ensure_editable(self) -> None:
        if self.state in {SalesOrderState.DONE, SalesOrderState.CANCELLED}:
            raise InvalidSalesOrderError("sales order is closed")
