"""
Leave Management - Domain Entities

مدیریت مرخصی، سیاست‌ها و مانده مرخصی.
"""
from dataclasses import dataclass, field
from datetime import date, datetime
from decimal import Decimal
from typing import Optional
from uuid import UUID

from shared.base_classes.entity import TenantEntity


class LeaveType:
    """انواع مرخصی."""
    ANNUAL = "ANNUAL"
    SICK = "SICK"
    MATERNITY = "MATERNITY"
    PATERNITY = "PATERNITY"
    UNPAID = "UNPAID"
    STUDY = "STUDY"
    COMPENSATORY = "COMPENSATORY"
    BEREAVEMENT = "BEREAVEMENT"
    MARRIAGE = "MARRIAGE"
    PILGRIMAGE = "PILGRIMAGE"
    HOURLY = "HOURLY"


class LeaveRequestStatus:
    """وضعیت درخواست مرخصی."""
    DRAFT = "DRAFT"
    PENDING = "PENDING"
    APPROVED = "APPROVED"
    REJECTED = "REJECTED"
    CANCELLED = "CANCELLED"


class AccrualMethod:
    """روش محاسبه حق مرخصی."""
    ANNUAL = "ANNUAL"
    MONTHLY = "MONTHLY"
    DAILY = "DAILY"
    CUSTOM = "CUSTOM"


@dataclass
class LeavePolicy(TenantEntity):
    """
    سیاست مرخصی.

    Rules:
    - هر tenant یک مجموعه سیاست مرخصی دارد
    - حداکثر روز قابل ذخیره باید مثبت باشد
    """

    name: str = ""
    code: str = ""
    leave_type: str = LeaveType.ANNUAL
    annual_entitlement_days: Decimal = Decimal("26")
    max_carryover_days: Decimal = Decimal("10")
    max_accumulation_days: Decimal = Decimal("36")
    min_service_days_required: int = 0
    accrual_method: str = AccrualMethod.MONTHLY
    requires_approval: bool = True
    max_consecutive_days: int = 30
    min_notice_days: int = 1
    allow_half_day: bool = True
    allow_hourly: bool = False
    is_paid: bool = True
    applicable_genders: list = field(default_factory=lambda: ["MALE", "FEMALE"])
    applicable_employment_types: list = field(
        default_factory=lambda: ["FULL_TIME", "PART_TIME", "CONTRACT"]
    )
    is_active: bool = True
    metadata: dict = field(default_factory=dict)

    def validate(self) -> None:
        if not self.name:
            raise ValueError("نام سیاست مرخصی الزامی است")
        if self.annual_entitlement_days < 0:
            raise ValueError("تعداد روز مرخصی سالانه نمی‌تواند منفی باشد")
        if self.max_carryover_days < 0:
            raise ValueError("حداکثر ذخیره مرخصی نمی‌تواند منفی باشد")


@dataclass
class LeaveBalance(TenantEntity):
    """
    مانده مرخصی کارمند.

    Rules:
    - مانده نمی‌تواند از حد مجاز بیشتر شود
    - مصرف نمی‌تواند از مانده بیشتر باشد (مگر مرخصی بدون حقوق)
    """

    employee_id: UUID = field(default_factory=lambda: None)
    leave_policy_id: UUID = field(default_factory=lambda: None)
    leave_type: str = LeaveType.ANNUAL
    year: int = 0
    entitled_days: Decimal = Decimal("0")
    carried_over_days: Decimal = Decimal("0")
    used_days: Decimal = Decimal("0")
    pending_days: Decimal = Decimal("0")
    adjustment_days: Decimal = Decimal("0")
    metadata: dict = field(default_factory=dict)

    @property
    def available_days(self) -> Decimal:
        """محاسبه مانده قابل استفاده."""
        return (
            self.entitled_days
            + self.carried_over_days
            + self.adjustment_days
            - self.used_days
            - self.pending_days
        )

    @property
    def total_entitled(self) -> Decimal:
        """مجموع حق مرخصی."""
        return self.entitled_days + self.carried_over_days + self.adjustment_days

    def can_request(self, days: Decimal) -> bool:
        """بررسی امکان درخواست مرخصی."""
        return self.available_days >= days

    def reserve(self, days: Decimal) -> None:
        """رزرو مرخصی (pending)."""
        if not self.can_request(days):
            raise ValueError(
                f"مانده مرخصی کافی نیست. مانده: {self.available_days}, درخواست: {days}"
            )
        self.pending_days += days
        self.mark_updated()

    def confirm(self, days: Decimal) -> None:
        """تأیید مرخصی رزرو شده."""
        self.pending_days -= days
        self.used_days += days
        self.mark_updated()

    def release(self, days: Decimal) -> None:
        """آزادسازی مرخصی رزرو شده (رد یا لغو)."""
        self.pending_days -= days
        self.mark_updated()

    def validate(self) -> None:
        if not self.employee_id:
            raise ValueError("شناسه کارمند الزامی است")
        if self.year <= 0:
            raise ValueError("سال باید مثبت باشد")


@dataclass
class LeaveRequest(TenantEntity):
    """
    درخواست مرخصی - Aggregate Root.

    Rules:
    - فقط Draft/Pending قابل لغو
    - تاریخ شروع باید قبل یا مساوی پایان باشد
    - نیاز به تأیید مدیر (بر اساس سیاست)
    """

    employee_id: UUID = field(default_factory=lambda: None)
    leave_type: str = LeaveType.ANNUAL
    leave_policy_id: Optional[UUID] = None
    start_date: Optional[date] = None
    end_date: Optional[date] = None
    days_count: Decimal = Decimal("1")
    is_half_day: bool = False
    half_day_period: str = ""  # MORNING, AFTERNOON
    status: str = LeaveRequestStatus.DRAFT
    reason: str = ""
    substitute_employee_id: Optional[UUID] = None
    approved_by: Optional[UUID] = None
    approved_at: Optional[datetime] = None
    rejection_reason: str = ""
    attachment_path: str = ""
    notes: str = ""
    metadata: dict = field(default_factory=dict)

    def validate(self) -> None:
        if not self.employee_id:
            raise ValueError("شناسه کارمند الزامی است")
        if not self.start_date:
            raise ValueError("تاریخ شروع الزامی است")
        if not self.end_date:
            raise ValueError("تاریخ پایان الزامی است")
        if self.end_date < self.start_date:
            raise ValueError("تاریخ پایان نمی‌تواند قبل از تاریخ شروع باشد")
        if self.days_count <= 0:
            raise ValueError("تعداد روز باید مثبت باشد")

    def can_cancel(self) -> bool:
        return self.status in (LeaveRequestStatus.DRAFT, LeaveRequestStatus.PENDING)

    def submit(self) -> None:
        if self.status != LeaveRequestStatus.DRAFT:
            raise ValueError("فقط پیش‌نویس قابل ارسال است")
        self.status = LeaveRequestStatus.PENDING
        self.mark_updated()

    def approve(self, approver_id: UUID) -> None:
        if self.status != LeaveRequestStatus.PENDING:
            raise ValueError("فقط درخواست‌های در انتظار قابل تأیید هستند")
        self.status = LeaveRequestStatus.APPROVED
        self.approved_by = approver_id
        self.approved_at = datetime.utcnow()
        self.mark_updated()

    def reject(self, approver_id: UUID, reason: str) -> None:
        if self.status != LeaveRequestStatus.PENDING:
            raise ValueError("فقط درخواست‌های در انتظار قابل رد هستند")
        self.status = LeaveRequestStatus.REJECTED
        self.approved_by = approver_id
        self.rejection_reason = reason
        self.mark_updated()

    def cancel(self) -> None:
        if not self.can_cancel():
            raise ValueError("این درخواست قابل لغو نیست")
        self.status = LeaveRequestStatus.CANCELLED
        self.mark_updated()
