"""
CLM Domain Services.

سرویس‌های دامنه — منطق کسب‌وکار خالص بدون وابستگی زیرساختی.
"""
from datetime import date, timedelta
from decimal import Decimal
from typing import List, Optional
from uuid import UUID

from ..entities.contract import Contract, ContractStatus
from ..entities.milestone import ContractMilestone, MilestoneStatus
from ..entities.amendment import ContractAmendment, AmendmentType, AmendmentStatus
from ..entities.renewal import ContractRenewal, RenewalStatus
from ..exceptions.contract_exceptions import (
    InvalidContractStateTransition,
    MaxRenewalsExceededError,
    AmendmentNotAllowedError,
)


class ContractLifecycleService:
    """سرویس دامنه مدیریت چرخه حیات قرارداد."""

    VALID_TRANSITIONS = {
        ContractStatus.DRAFT: [ContractStatus.PENDING_APPROVAL, ContractStatus.CANCELLED],
        ContractStatus.PENDING_APPROVAL: [ContractStatus.APPROVED, ContractStatus.DRAFT],
        ContractStatus.APPROVED: [ContractStatus.ACTIVE, ContractStatus.CANCELLED],
        ContractStatus.ACTIVE: [
            ContractStatus.SUSPENDED, ContractStatus.COMPLETED,
            ContractStatus.TERMINATED, ContractStatus.EXPIRED,
        ],
        ContractStatus.SUSPENDED: [ContractStatus.ACTIVE, ContractStatus.TERMINATED, ContractStatus.CANCELLED],
        ContractStatus.COMPLETED: [],
        ContractStatus.TERMINATED: [],
        ContractStatus.EXPIRED: [ContractStatus.ACTIVE],  # via renewal
        ContractStatus.CANCELLED: [],
    }

    def can_transition(self, current: str, target: str) -> bool:
        """آیا تغییر وضعیت مجاز است؟"""
        allowed = self.VALID_TRANSITIONS.get(current, [])
        return target in allowed

    def transition(self, contract: Contract, target_status: str) -> None:
        """تغییر وضعیت قرارداد با بررسی اعتبار."""
        if not self.can_transition(contract.status, target_status):
            raise InvalidContractStateTransition(contract.status, target_status)
        contract.status = target_status


class ContractValuationService:
    """سرویس دامنه محاسبات مالی قرارداد."""

    def calculate_milestone_total(self, milestones: List[ContractMilestone]) -> Decimal:
        """مجموع مبلغ مراحل."""
        return sum(m.amount for m in milestones if m.status != MilestoneStatus.CANCELLED)

    def calculate_paid_amount(self, milestones: List[ContractMilestone]) -> Decimal:
        """مبلغ پرداخت‌شده."""
        return sum(
            m.amount for m in milestones
            if m.status == MilestoneStatus.COMPLETED and m.payment_status == "PAID"
        )

    def calculate_remaining_amount(self, contract: Contract, milestones: List[ContractMilestone]) -> Decimal:
        """مبلغ باقی‌مانده."""
        paid = self.calculate_paid_amount(milestones)
        return contract.total_value - paid

    def calculate_penalty(self, milestone: ContractMilestone) -> Decimal:
        """محاسبه جریمه تأخیر."""
        if not milestone.has_penalty or milestone.delay_days <= 0:
            return Decimal("0")
        return milestone.penalty_amount_per_day * milestone.delay_days

    def calculate_advance_payment(self, contract: Contract) -> Decimal:
        """مبلغ پیش‌پرداخت."""
        return contract.total_value * contract.advance_payment_percent / 100

    def calculate_retention(self, contract: Contract) -> Decimal:
        """مبلغ حسن انجام کار."""
        return contract.total_value * contract.retention_percent / 100


class ContractRenewalService:
    """سرویس دامنه تمدید قرارداد."""

    def can_renew(self, contract: Contract) -> bool:
        """آیا قرارداد قابل تمدید است؟"""
        if contract.max_renewals > 0 and contract.renewals_count >= contract.max_renewals:
            return False
        return contract.status in (ContractStatus.ACTIVE, ContractStatus.EXPIRED)

    def create_renewal(
        self,
        contract: Contract,
        new_end_date: date,
        new_value: Optional[Decimal] = None,
        is_auto: bool = False,
    ) -> ContractRenewal:
        """ایجاد تمدید."""
        if not self.can_renew(contract):
            raise MaxRenewalsExceededError("تعداد تمدید به حداکثر مجاز رسیده")

        renewal = ContractRenewal(
            tenant_id=contract.tenant_id,
            contract_id=contract.id,
            renewal_number=contract.renewals_count + 1,
            old_start_date=contract.start_date,
            old_end_date=contract.end_date,
            new_start_date=contract.end_date or date.today(),
            new_end_date=new_end_date,
            old_value=contract.total_value,
            new_value=new_value or contract.total_value,
            is_auto=is_auto,
            status=RenewalStatus.AUTO_RENEWED if is_auto else RenewalStatus.PENDING,
        )

        if renewal.old_value and renewal.old_value > 0:
            renewal.value_change_percent = (
                (renewal.new_value - renewal.old_value) / renewal.old_value * 100
            )

        return renewal

    def apply_renewal(self, contract: Contract, renewal: ContractRenewal) -> None:
        """اعمال تمدید روی قرارداد."""
        contract.start_date = renewal.new_start_date
        contract.end_date = renewal.new_end_date
        contract.expiry_date = renewal.new_end_date
        contract.total_value = renewal.new_value or contract.total_value
        contract.renewals_count += 1
        if contract.status == ContractStatus.EXPIRED:
            contract.status = ContractStatus.ACTIVE


class ContractAmendmentService:
    """سرویس دامنه الحاقیه."""

    def can_amend(self, contract: Contract) -> bool:
        """آیا ایجاد الحاقیه مجاز است؟"""
        return contract.status in (ContractStatus.ACTIVE, ContractStatus.SUSPENDED)

    def apply_amendment(self, contract: Contract, amendment: ContractAmendment) -> None:
        """اعمال الحاقیه روی قرارداد."""
        if amendment.status != AmendmentStatus.APPROVED:
            raise AmendmentNotAllowedError("فقط الحاقیه‌های تأیید شده قابل اعمال هستند")

        if amendment.amendment_type in (AmendmentType.VALUE_CHANGE, AmendmentType.COMBINED):
            if amendment.new_total_value is not None:
                contract.total_value = amendment.new_total_value

        if amendment.amendment_type in (AmendmentType.DURATION_CHANGE, AmendmentType.COMBINED):
            if amendment.new_end_date is not None:
                contract.end_date = amendment.new_end_date
                contract.expiry_date = amendment.new_end_date

        amendment.status = AmendmentStatus.APPLIED
