"""
HRM Use Cases - Leave Operations.

Single-purpose handlers for leave management.
"""
from dataclasses import dataclass
from decimal import Decimal
from uuid import uuid4

from ..dtos.hrm_dtos import (
    RequestLeaveDTO,
    ApproveLeaveDTO,
    RejectLeaveDTO,
    LeaveRequestResponseDTO,
    LeaveBalanceResponseDTO,
)
from ...domain.entities.leave import LeaveRequest, LeaveRequestStatus
from ...domain.repositories.interfaces import (
    ILeaveRequestRepository,
    ILeaveBalanceRepository,
    ILeavePolicyRepository,
)
from ...domain.exceptions.hrm_exceptions import (
    LeaveRequestNotFoundError,
    InsufficientLeaveBalanceError,
    InvalidLeaveRequestError,
)


@dataclass
class RequestLeaveUseCase:
    """
    Use Case - درخواست مرخصی.

    Flow:
    1. بررسی سیاست مرخصی
    2. بررسی مانده مرخصی
    3. رزرو مانده
    4. ایجاد درخواست
    5. ارسال برای تأیید
    """

    leave_request_repo: ILeaveRequestRepository
    leave_balance_repo: ILeaveBalanceRepository
    leave_policy_repo: ILeavePolicyRepository

    def execute(self, dto: RequestLeaveDTO, tenant_id=None) -> LeaveRequestResponseDTO:
        days = Decimal(dto.days_count)

        # 1. Check leave policy
        policy = self.leave_policy_repo.find_by_type(dto.leave_type, tenant_id)
        if not policy:
            raise InvalidLeaveRequestError(
                f"سیاست مرخصی نوع '{dto.leave_type}' یافت نشد"
            )

        # 2. Check balance (except for unpaid leave)
        if dto.leave_type != "UNPAID":
            import datetime
            current_year = datetime.date.today().year
            balance = self.leave_balance_repo.find_by_employee_type(
                dto.employee_id, dto.leave_type, current_year
            )
            if not balance:
                raise InsufficientLeaveBalanceError("0", str(days))
            if not balance.can_request(days):
                raise InsufficientLeaveBalanceError(
                    str(balance.available_days), str(days)
                )
            # 3. Reserve balance
            balance.reserve(days)
            self.leave_balance_repo.save(balance)

        # 4. Create request
        request = LeaveRequest(
            id=uuid4(),
            tenant_id=tenant_id,
            employee_id=dto.employee_id,
            leave_type=dto.leave_type,
            leave_policy_id=policy.id if policy else None,
            start_date=dto.start_date,
            end_date=dto.end_date,
            days_count=days,
            is_half_day=dto.is_half_day,
            half_day_period=dto.half_day_period,
            reason=dto.reason,
            substitute_employee_id=dto.substitute_employee_id,
        )
        request.validate()

        # 5. Submit
        request.submit()
        saved = self.leave_request_repo.save(request)

        return LeaveRequestResponseDTO(
            id=saved.id,
            employee_id=saved.employee_id,
            leave_type=saved.leave_type,
            start_date=saved.start_date,
            end_date=saved.end_date,
            days_count=str(saved.days_count),
            status=saved.status,
            reason=saved.reason,
        )


@dataclass
class ApproveLeaveUseCase:
    """Use Case - تأیید مرخصی."""

    leave_request_repo: ILeaveRequestRepository
    leave_balance_repo: ILeaveBalanceRepository

    def execute(self, dto: ApproveLeaveDTO) -> LeaveRequestResponseDTO:
        request = self.leave_request_repo.get_by_id(dto.request_id)
        if not request:
            raise LeaveRequestNotFoundError(str(dto.request_id))

        request.approve(dto.approver_id)

        # Confirm balance
        if request.leave_type != "UNPAID":
            import datetime
            current_year = datetime.date.today().year
            balance = self.leave_balance_repo.find_by_employee_type(
                request.employee_id, request.leave_type, current_year
            )
            if balance:
                balance.confirm(request.days_count)
                self.leave_balance_repo.save(balance)

        saved = self.leave_request_repo.save(request)

        return LeaveRequestResponseDTO(
            id=saved.id,
            employee_id=saved.employee_id,
            leave_type=saved.leave_type,
            start_date=saved.start_date,
            end_date=saved.end_date,
            days_count=str(saved.days_count),
            status=saved.status,
        )


@dataclass
class RejectLeaveUseCase:
    """Use Case - رد مرخصی."""

    leave_request_repo: ILeaveRequestRepository
    leave_balance_repo: ILeaveBalanceRepository

    def execute(self, dto: RejectLeaveDTO) -> LeaveRequestResponseDTO:
        request = self.leave_request_repo.get_by_id(dto.request_id)
        if not request:
            raise LeaveRequestNotFoundError(str(dto.request_id))

        request.reject(dto.approver_id, dto.reason)

        # Release reserved balance
        if request.leave_type != "UNPAID":
            import datetime
            current_year = datetime.date.today().year
            balance = self.leave_balance_repo.find_by_employee_type(
                request.employee_id, request.leave_type, current_year
            )
            if balance:
                balance.release(request.days_count)
                self.leave_balance_repo.save(balance)

        saved = self.leave_request_repo.save(request)

        return LeaveRequestResponseDTO(
            id=saved.id,
            employee_id=saved.employee_id,
            leave_type=saved.leave_type,
            start_date=saved.start_date,
            end_date=saved.end_date,
            days_count=str(saved.days_count),
            status=saved.status,
        )


@dataclass
class CancelLeaveUseCase:
    """Use Case - لغو مرخصی."""

    leave_request_repo: ILeaveRequestRepository
    leave_balance_repo: ILeaveBalanceRepository

    def execute(self, request_id, employee_id=None) -> LeaveRequestResponseDTO:
        request = self.leave_request_repo.get_by_id(request_id)
        if not request:
            raise LeaveRequestNotFoundError(str(request_id))

        was_pending = request.status == LeaveRequestStatus.PENDING
        request.cancel()

        # Release reserved balance
        if was_pending and request.leave_type != "UNPAID":
            import datetime
            current_year = datetime.date.today().year
            balance = self.leave_balance_repo.find_by_employee_type(
                request.employee_id, request.leave_type, current_year
            )
            if balance:
                balance.release(request.days_count)
                self.leave_balance_repo.save(balance)

        saved = self.leave_request_repo.save(request)

        return LeaveRequestResponseDTO(
            id=saved.id,
            employee_id=saved.employee_id,
            leave_type=saved.leave_type,
            start_date=saved.start_date,
            end_date=saved.end_date,
            days_count=str(saved.days_count),
            status=saved.status,
        )
