"""
SCM Domain Entities — Purchase Order.

سفارش خرید و آیتم‌های آن.
"""
from dataclasses import dataclass, field
from datetime import date, datetime
from decimal import Decimal
from enum import Enum
from typing import Optional, List
from uuid import UUID

from shared.base_classes.entity import TenantEntity


class POStatus(str, Enum):
    DRAFT = "DRAFT"
    PENDING_APPROVAL = "PENDING_APPROVAL"
    APPROVED = "APPROVED"
    REJECTED = "REJECTED"
    SENT = "SENT"
    PARTIALLY_RECEIVED = "PARTIALLY_RECEIVED"
    FULLY_RECEIVED = "FULLY_RECEIVED"
    CANCELLED = "CANCELLED"
    CLOSED = "CLOSED"


class POPriority(str, Enum):
    LOW = "LOW"
    NORMAL = "NORMAL"
    HIGH = "HIGH"
    URGENT = "URGENT"


@dataclass
class PurchaseOrderLine(TenantEntity):
    """آیتم سفارش خرید."""
    order_id: Optional[UUID] = None
    line_number: int = 0
    item_id: Optional[UUID] = None
    item_code: str = ""
    item_name: str = ""
    description: str = ""
    quantity: Decimal = Decimal("0")
    received_quantity: Decimal = Decimal("0")
    returned_quantity: Decimal = Decimal("0")
    uom_id: Optional[UUID] = None
    uom_code: str = ""
    unit_price: Decimal = Decimal("0")
    discount_percent: Decimal = Decimal("0")
    discount_amount: Decimal = Decimal("0")
    tax_percent: Decimal = Decimal("0")
    tax_amount: Decimal = Decimal("0")
    line_total: Decimal = Decimal("0")
    warehouse_id: Optional[UUID] = None
    warehouse_name: str = ""
    required_date: Optional[date] = None
    notes: str = ""

    # Source
    pr_line_id: Optional[UUID] = None
    rfq_response_line_id: Optional[UUID] = None

    def calculate_totals(self):
        """محاسبه جمع آیتم."""
        subtotal = self.quantity * self.unit_price
        if self.discount_percent > 0:
            self.discount_amount = subtotal * self.discount_percent / 100
        net = subtotal - self.discount_amount
        if self.tax_percent > 0:
            self.tax_amount = net * self.tax_percent / 100
        self.line_total = net + self.tax_amount

    @property
    def remaining_quantity(self) -> Decimal:
        """مقدار باقیمانده برای دریافت."""
        return self.quantity - self.received_quantity + self.returned_quantity

    @property
    def is_fully_received(self) -> bool:
        return self.received_quantity >= self.quantity


@dataclass
class PurchaseOrder(TenantEntity):
    """سفارش خرید — Aggregate Root."""
    po_number: str = ""
    title: str = ""
    description: str = ""
    status: POStatus = POStatus.DRAFT
    priority: POPriority = POPriority.NORMAL

    # Vendor
    vendor_id: Optional[UUID] = None
    vendor_name: str = ""
    vendor_code: str = ""

    # Source
    pr_id: Optional[UUID] = None
    pr_number: str = ""
    rfq_id: Optional[UUID] = None
    rfq_number: str = ""
    contract_id: Optional[UUID] = None  # CLM link

    # Buyer
    buyer_id: Optional[UUID] = None
    buyer_name: str = ""

    # Dates
    order_date: Optional[date] = None
    expected_delivery_date: Optional[date] = None
    actual_delivery_date: Optional[date] = None

    # Financial
    subtotal: Decimal = Decimal("0")
    total_discount: Decimal = Decimal("0")
    total_tax: Decimal = Decimal("0")
    total_amount: Decimal = Decimal("0")
    currency_code: str = "IRR"

    # Terms
    payment_terms: str = ""
    delivery_terms: str = ""
    shipping_address: str = ""

    # Approval
    approved_by_id: Optional[UUID] = None
    approved_at: Optional[datetime] = None
    rejection_reason: str = ""

    notes: str = ""

    # Lines
    lines: List[PurchaseOrderLine] = field(default_factory=list)

    def calculate_totals(self):
        """محاسبه جمع کل سفارش."""
        for line in self.lines:
            line.calculate_totals()
        self.subtotal = sum(
            line.quantity * line.unit_price for line in self.lines
        )
        self.total_discount = sum(line.discount_amount for line in self.lines)
        self.total_tax = sum(line.tax_amount for line in self.lines)
        self.total_amount = sum(line.line_total for line in self.lines)

    def submit(self):
        """ارسال برای تأیید."""
        if self.status != POStatus.DRAFT:
            raise ValueError("Only DRAFT POs can be submitted")
        if not self.lines:
            raise ValueError("PO must have at least one line")
        if not self.vendor_id:
            raise ValueError("Vendor is required")
        self.status = POStatus.PENDING_APPROVAL

    def approve(self, approved_by: UUID):
        """تأیید سفارش."""
        if self.status != POStatus.PENDING_APPROVAL:
            raise ValueError("Only PENDING_APPROVAL POs can be approved")
        self.status = POStatus.APPROVED
        self.approved_by_id = approved_by
        self.approved_at = datetime.utcnow()

    def reject(self, reason: str):
        """رد سفارش."""
        if self.status != POStatus.PENDING_APPROVAL:
            raise ValueError("Only PENDING_APPROVAL POs can be rejected")
        self.status = POStatus.REJECTED
        self.rejection_reason = reason

    def send_to_vendor(self):
        """ارسال به تأمین‌کننده."""
        if self.status != POStatus.APPROVED:
            raise ValueError("Only APPROVED POs can be sent")
        self.status = POStatus.SENT
        self.order_date = date.today()

    def update_receipt_status(self):
        """بروزرسانی وضعیت دریافت."""
        if not self.lines:
            return
        all_received = all(line.is_fully_received for line in self.lines)
        any_received = any(line.received_quantity > 0 for line in self.lines)

        if all_received:
            self.status = POStatus.FULLY_RECEIVED
        elif any_received:
            self.status = POStatus.PARTIALLY_RECEIVED

    def cancel(self):
        """لغو سفارش."""
        if self.status in (POStatus.FULLY_RECEIVED, POStatus.CLOSED, POStatus.CANCELLED):
            raise ValueError("Cannot cancel this PO")
        self.status = POStatus.CANCELLED

    def close(self):
        """بستن سفارش."""
        if self.status not in (POStatus.FULLY_RECEIVED, POStatus.PARTIALLY_RECEIVED, POStatus.SENT):
            raise ValueError("Cannot close this PO")
        self.status = POStatus.CLOSED

    def validate(self):
        if not self.po_number:
            raise ValueError("PO number is required")
        if not self.vendor_id:
            raise ValueError("Vendor is required")
