"""
Inventory Domain Services — سرویس‌های دامنه.

سرویس‌های خالص بدون وابستگی infrastructure.
"""
from dataclasses import dataclass
from datetime import date
from decimal import Decimal
from typing import List, Optional, Tuple
from uuid import UUID

from shared.base_classes.service import BaseService


class StockTransactionService(BaseService):
    """
    سرویس تراکنش موجودی.

    قوانین کسب‌وکار حرکت موجودی:
    - بررسی موجودی کافی برای خروج
    - بررسی وضعیت فریز انبار
    - بررسی وضعیت بچ/سریال
    - محاسبه مقدار در واحد پایه
    """

    @staticmethod
    def validate_goods_receipt(
        item_id: UUID,
        warehouse_id: UUID,
        quantity: Decimal,
        uom_conversion_factor: Decimal = Decimal("1"),
    ) -> Tuple[bool, str]:
        """اعتبارسنجی رسید کالا."""
        if quantity <= 0:
            return False, "مقدار باید مثبت باشد"
        return True, ""

    @staticmethod
    def validate_goods_issue(
        quantity_on_hand: Decimal,
        quantity_reserved: Decimal,
        requested_quantity: Decimal,
        allow_negative: bool = False,
    ) -> Tuple[bool, str]:
        """اعتبارسنجی حواله کالا."""
        if requested_quantity <= 0:
            return False, "مقدار باید مثبت باشد"

        available = quantity_on_hand - quantity_reserved
        if not allow_negative and requested_quantity > available:
            return False, f"موجودی کافی نیست. در دسترس: {available}"

        return True, ""

    @staticmethod
    def calculate_base_quantity(
        quantity: Decimal,
        conversion_factor: Decimal,
    ) -> Decimal:
        """تبدیل مقدار به واحد پایه."""
        return quantity * conversion_factor

    @staticmethod
    def validate_transfer(
        from_warehouse_id: UUID,
        to_warehouse_id: UUID,
        quantity: Decimal,
        available_quantity: Decimal,
    ) -> Tuple[bool, str]:
        """اعتبارسنجی انتقال."""
        if from_warehouse_id == to_warehouse_id:
            return False, "انبار مبدأ و مقصد یکسان است"
        if quantity <= 0:
            return False, "مقدار باید مثبت باشد"
        if quantity > available_quantity:
            return False, f"موجودی کافی نیست. در دسترس: {available_quantity}"
        return True, ""


class CostingService(BaseService):
    """
    سرویس قیمت‌گذاری.

    محاسبه بها بر اساس روش‌های مختلف:
    - میانگین متحرک وزنی (WAC)
    - FIFO
    - LIFO
    - بهای استاندارد
    """

    @staticmethod
    def calculate_weighted_average_cost(
        current_quantity: Decimal,
        current_total_value: Decimal,
        incoming_quantity: Decimal,
        incoming_unit_cost: Decimal,
    ) -> Decimal:
        """محاسبه میانگین متحرک وزنی."""
        total_qty = current_quantity + incoming_quantity
        if total_qty == 0:
            return Decimal("0")
        total_value = current_total_value + (incoming_quantity * incoming_unit_cost)
        return (total_value / total_qty).quantize(Decimal("0.0001"))

    @staticmethod
    def calculate_fifo_cost(
        cost_layers: list,
        issue_quantity: Decimal,
    ) -> Tuple[Decimal, List]:
        """
        محاسبه بهای FIFO.

        Returns:
            (total_cost, consumed_layers)
        """
        total_cost = Decimal("0")
        remaining = issue_quantity
        consumed = []

        for layer in cost_layers:  # باید مرتب بر اساس receipt_date باشد
            if remaining <= 0:
                break
            take = min(remaining, layer.remaining_quantity)
            total_cost += take * layer.unit_cost
            consumed.append((layer, take))
            remaining -= take

        if remaining > 0:
            raise ValueError(f"لایه‌های هزینه کافی نیست. کمبود: {remaining}")

        return total_cost, consumed

    @staticmethod
    def calculate_standard_cost_variance(
        actual_cost: Decimal,
        standard_cost: Decimal,
        quantity: Decimal,
    ) -> Decimal:
        """محاسبه انحراف بهای استاندارد."""
        return (actual_cost - standard_cost) * quantity


class ReservationService(BaseService):
    """
    سرویس رزرو موجودی.

    قوانین:
    - مقدار رزرو نباید از موجودی در دسترس بیشتر باشد
    - رزروهای منقضی باید آزاد شوند
    - اولویت‌بندی بر اساس priority_level
    """

    @staticmethod
    def validate_reservation(
        available_quantity: Decimal,
        requested_quantity: Decimal,
        existing_reservations: Decimal = Decimal("0"),
    ) -> Tuple[bool, str]:
        """اعتبارسنجی رزرو."""
        if requested_quantity <= 0:
            return False, "مقدار باید مثبت باشد"
        net_available = available_quantity - existing_reservations
        if requested_quantity > net_available:
            return False, f"موجودی در دسترس: {net_available}"
        return True, ""

    @staticmethod
    def check_reservation_expiry(
        expiry_date: Optional[date],
        today: Optional[date] = None,
    ) -> bool:
        """بررسی انقضای رزرو."""
        if expiry_date is None:
            return False
        check_date = today or date.today()
        return check_date > expiry_date

    @staticmethod
    def calculate_atp(
        quantity_on_hand: Decimal,
        quantity_reserved: Decimal,
        quantity_allocated: Decimal,
        quantity_in_transit: Decimal = Decimal("0"),
    ) -> Decimal:
        """
        محاسبه Available-to-Promise.

        ATP = On Hand - Reserved - Allocated + In Transit
        """
        return quantity_on_hand - quantity_reserved - quantity_allocated + quantity_in_transit


class TraceabilityService(BaseService):
    """
    سرویس ردیابی.

    ردیابی بچ و سریال در زنجیره تأمین.
    """

    @staticmethod
    def validate_batch_for_issue(
        batch_status: str,
        quality_status: str,
        expiry_date: Optional[date] = None,
    ) -> Tuple[bool, str]:
        """بررسی امکان صدور از بچ."""
        if batch_status != "ACTIVE":
            return False, f"بچ فعال نیست: {batch_status}"
        if quality_status == "REJECTED":
            return False, "بچ رد شده است"
        if expiry_date and date.today() > expiry_date:
            return False, "بچ منقضی شده است"
        return True, ""

    @staticmethod
    def validate_serial_for_issue(
        serial_status: str,
    ) -> Tuple[bool, str]:
        """بررسی امکان صدور سریال."""
        if serial_status != "IN_STOCK":
            return False, f"سریال در وضعیت {serial_status} است"
        return True, ""


class PhysicalInventoryService(BaseService):
    """سرویس شمارش فیزیکی."""

    @staticmethod
    def calculate_variance(
        book_quantity: Decimal,
        count_quantity: Decimal,
        unit_cost: Decimal = Decimal("0"),
    ) -> Tuple[Decimal, Decimal, Decimal]:
        """
        محاسبه انحراف.

        Returns:
            (variance_quantity, variance_percent, variance_value)
        """
        variance_qty = count_quantity - book_quantity
        variance_pct = Decimal("0")
        if book_quantity != 0:
            variance_pct = (
                (variance_qty / book_quantity) * 100
            ).quantize(Decimal("0.01"))
        variance_val = variance_qty * unit_cost
        return variance_qty, variance_pct, variance_val

    @staticmethod
    def is_within_threshold(
        variance_percent: Decimal,
        threshold_percent: Decimal,
    ) -> bool:
        """بررسی آستانه انحراف."""
        return abs(variance_percent) <= threshold_percent
