"""
HRM Use Cases - Employee Operations.

Single-purpose handlers for employee management operations.
"""
from dataclasses import dataclass
from uuid import uuid4

from ..dtos.hrm_dtos import (
    HireEmployeeDTO,
    UpdateEmployeeDTO,
    TerminateEmployeeDTO,
    TransferEmployeeDTO,
    EmployeeResponseDTO,
)
from ...domain.entities.employee import Employee, EmploymentStatus
from ...domain.repositories.interfaces import IEmployeeRepository, IPositionRepository
from ...domain.exceptions.hrm_exceptions import (
    DuplicateEmployeeCodeError,
    DuplicateNationalCodeError,
    EmployeeNotFoundError,
)
from ...domain.events.hrm_events import (
    EmployeeHired,
    EmployeeUpdated,
    EmployeeTerminated,
    EmployeeTransferred,
    PositionFilled,
)


@dataclass
class HireEmployeeUseCase:
    """
    Use Case - استخدام کارمند جدید.

    Flow:
    1. بررسی یکتایی کد پرسنلی و کد ملی
    2. ایجاد entity دامنه
    3. اعتبارسنجی
    4. ذخیره
    5. تخصیص به پست (در صورت وجود)
    6. انتشار رویداد
    """

    employee_repo: IEmployeeRepository
    position_repo: IPositionRepository = None

    def execute(self, dto: HireEmployeeDTO, tenant_id=None) -> EmployeeResponseDTO:
        # 1. Check uniqueness
        existing = self.employee_repo.find_by_employee_code(dto.employee_code)
        if existing:
            raise DuplicateEmployeeCodeError(dto.employee_code)

        existing = self.employee_repo.find_by_national_code(dto.national_code)
        if existing:
            raise DuplicateNationalCodeError(dto.national_code)

        # 2. Create domain entity
        employee = Employee(
            id=uuid4(),
            tenant_id=tenant_id,
            employee_code=dto.employee_code,
            first_name=dto.first_name,
            last_name=dto.last_name,
            first_name_en=dto.first_name_en,
            last_name_en=dto.last_name_en,
            national_code=dto.national_code,
            date_of_birth=dto.date_of_birth,
            gender=dto.gender,
            marital_status=dto.marital_status,
            military_status=dto.military_status,
            phone=dto.phone,
            mobile=dto.mobile,
            email=dto.email,
            address=dto.address,
            hire_date=dto.hire_date,
            employment_type=dto.employment_type,
            employment_status=EmploymentStatus.PROBATION,
            legal_entity_id=dto.legal_entity_id,
            org_unit_id=dto.org_unit_id,
            location_id=dto.location_id,
            primary_position_id=dto.position_id,
        )

        # 3. Validate
        employee.validate()

        # 4. Persist
        saved = self.employee_repo.save(employee)

        # 5. Fill position if provided
        if dto.position_id and self.position_repo:
            position = self.position_repo.get_by_id(dto.position_id)
            if position and position.is_vacant:
                position.fill(saved.id)
                self.position_repo.save(position)

        # 6. Return response
        return EmployeeResponseDTO(
            id=saved.id,
            employee_code=saved.employee_code,
            first_name=saved.first_name,
            last_name=saved.last_name,
            full_name=saved.full_name,
            national_code=saved.national_code,
            gender=saved.gender,
            employment_status=saved.employment_status,
            employment_type=saved.employment_type,
            hire_date=saved.hire_date,
            org_unit_id=saved.org_unit_id,
            position_id=saved.primary_position_id,
            is_active=saved.is_active,
            created_at=saved.created_at,
        )


@dataclass
class TerminateEmployeeUseCase:
    """Use Case - خاتمه همکاری کارمند."""

    employee_repo: IEmployeeRepository
    position_repo: IPositionRepository = None

    def execute(self, dto: TerminateEmployeeDTO) -> EmployeeResponseDTO:
        employee = self.employee_repo.get_by_id(dto.employee_id)
        if not employee:
            raise EmployeeNotFoundError(str(dto.employee_id))

        # Vacate position
        if employee.primary_position_id and self.position_repo:
            position = self.position_repo.get_by_id(employee.primary_position_id)
            if position:
                position.vacate()
                self.position_repo.save(position)

        employee.terminate(dto.termination_date, dto.reason)
        saved = self.employee_repo.save(employee)

        return EmployeeResponseDTO(
            id=saved.id,
            employee_code=saved.employee_code,
            first_name=saved.first_name,
            last_name=saved.last_name,
            full_name=saved.full_name,
            national_code=saved.national_code,
            gender=saved.gender,
            employment_status=saved.employment_status,
            employment_type=saved.employment_type,
            hire_date=saved.hire_date,
            org_unit_id=saved.org_unit_id,
            is_active=saved.is_active,
        )


@dataclass
class TransferEmployeeUseCase:
    """Use Case - انتقال کارمند."""

    employee_repo: IEmployeeRepository
    position_repo: IPositionRepository = None

    def execute(self, dto: TransferEmployeeDTO) -> EmployeeResponseDTO:
        employee = self.employee_repo.get_by_id(dto.employee_id)
        if not employee:
            raise EmployeeNotFoundError(str(dto.employee_id))

        old_org_unit_id = employee.org_unit_id
        old_position_id = employee.primary_position_id

        # Vacate old position
        if old_position_id and self.position_repo:
            old_position = self.position_repo.get_by_id(old_position_id)
            if old_position:
                old_position.vacate()
                self.position_repo.save(old_position)

        # Transfer
        employee.transfer(dto.new_org_unit_id, dto.new_position_id)

        # Fill new position
        if dto.new_position_id and self.position_repo:
            new_position = self.position_repo.get_by_id(dto.new_position_id)
            if new_position:
                new_position.fill(employee.id)
                self.position_repo.save(new_position)

        saved = self.employee_repo.save(employee)

        return EmployeeResponseDTO(
            id=saved.id,
            employee_code=saved.employee_code,
            first_name=saved.first_name,
            last_name=saved.last_name,
            full_name=saved.full_name,
            national_code=saved.national_code,
            gender=saved.gender,
            employment_status=saved.employment_status,
            employment_type=saved.employment_type,
            hire_date=saved.hire_date,
            org_unit_id=saved.org_unit_id,
            position_id=saved.primary_position_id,
            is_active=saved.is_active,
        )
