"""
HRM Repository Implementations - Organization & Employee.

پیاده‌سازی repositoryهای ساختار سازمانی و کارمندان با Django ORM.
"""
from datetime import date
from typing import Optional, List
from uuid import UUID

from django.db.models import Q

from ...domain.entities.legal_entity import LegalEntity
from ...domain.entities.location import Location
from ...domain.entities.employee import Employee
from ...domain.entities.employment_record import EmploymentRecord, EmployeeDocument
from ...domain.repositories.interfaces import (
    ILegalEntityRepository,
    ILocationRepository,
    IEmployeeRepository,
    IEmploymentRecordRepository,
    IEmployeeDocumentRepository,
)
from ..persistence.models import (
    LegalEntityModel,
    LocationModel,
    EmployeeModel,
    EmploymentRecordModel,
    EmployeeDocumentModel,
)
from .mappers import (
    LegalEntityMapper,
    LocationMapper,
    EmployeeMapper,
    EmploymentRecordMapper,
    EmployeeDocumentMapper,
)


class DjangoLegalEntityRepository(ILegalEntityRepository):
    """پیاده‌سازی مخزن شخصیت حقوقی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: LegalEntity) -> LegalEntity:
        try:
            model = LegalEntityModel.objects.get(id=entity.id)
            model = LegalEntityMapper.to_model(entity, model)
        except LegalEntityModel.DoesNotExist:
            model = LegalEntityMapper.to_model(entity)
        model.save()
        return LegalEntityMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[LegalEntity]:
        try:
            model = LegalEntityModel.objects.get(id=id, tenant_id=self._tenant_id)
            return LegalEntityMapper.to_entity(model)
        except LegalEntityModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[LegalEntity]:
        qs = LegalEntityModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [LegalEntityMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = LegalEntityModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_national_id(self, national_id: str) -> Optional[LegalEntity]:
        try:
            model = LegalEntityModel.objects.get(
                national_id=national_id, tenant_id=self._tenant_id
            )
            return LegalEntityMapper.to_entity(model)
        except LegalEntityModel.DoesNotExist:
            return None

    def find_by_registration_number(self, reg_number: str) -> Optional[LegalEntity]:
        try:
            model = LegalEntityModel.objects.get(
                registration_number=reg_number, tenant_id=self._tenant_id
            )
            return LegalEntityMapper.to_entity(model)
        except LegalEntityModel.DoesNotExist:
            return None

    def find_active(self, tenant_id: UUID) -> List[LegalEntity]:
        qs = LegalEntityModel.objects.filter(tenant_id=tenant_id, is_active=True)
        return [LegalEntityMapper.to_entity(m) for m in qs]


class DjangoLocationRepository(ILocationRepository):
    """پیاده‌سازی مخزن مکان با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Location) -> Location:
        try:
            model = LocationModel.objects.get(id=entity.id)
            model = LocationMapper.to_model(entity, model)
        except LocationModel.DoesNotExist:
            model = LocationMapper.to_model(entity)
        model.save()
        return LocationMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Location]:
        try:
            model = LocationModel.objects.get(id=id, tenant_id=self._tenant_id)
            return LocationMapper.to_entity(model)
        except LocationModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Location]:
        qs = LocationModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [LocationMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = LocationModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[Location]:
        try:
            model = LocationModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return LocationMapper.to_entity(model)
        except LocationModel.DoesNotExist:
            return None

    def find_by_legal_entity(self, legal_entity_id: UUID) -> List[Location]:
        qs = LocationModel.objects.filter(
            legal_entity_id=legal_entity_id, tenant_id=self._tenant_id
        )
        return [LocationMapper.to_entity(m) for m in qs]

    def find_active(self, tenant_id: UUID) -> List[Location]:
        qs = LocationModel.objects.filter(tenant_id=tenant_id, is_active=True)
        return [LocationMapper.to_entity(m) for m in qs]


class DjangoEmployeeRepository(IEmployeeRepository):
    """پیاده‌سازی مخزن کارمند با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Employee) -> Employee:
        try:
            model = EmployeeModel.objects.get(id=entity.id)
            model = EmployeeMapper.to_model(entity, model)
        except EmployeeModel.DoesNotExist:
            model = EmployeeMapper.to_model(entity)
        model.save()
        return EmployeeMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Employee]:
        try:
            model = EmployeeModel.objects.get(id=id, tenant_id=self._tenant_id)
            return EmployeeMapper.to_entity(model)
        except EmployeeModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Employee]:
        qs = EmployeeModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [EmployeeMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = EmployeeModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee_code(self, code: str) -> Optional[Employee]:
        try:
            model = EmployeeModel.objects.get(
                employee_code=code, tenant_id=self._tenant_id
            )
            return EmployeeMapper.to_entity(model)
        except EmployeeModel.DoesNotExist:
            return None

    def find_by_national_code(self, national_code: str) -> Optional[Employee]:
        try:
            model = EmployeeModel.objects.get(
                national_code=national_code, tenant_id=self._tenant_id
            )
            return EmployeeMapper.to_entity(model)
        except EmployeeModel.DoesNotExist:
            return None

    def find_by_org_unit(self, org_unit_id: UUID) -> List[Employee]:
        qs = EmployeeModel.objects.filter(
            org_unit_id=org_unit_id, tenant_id=self._tenant_id
        )
        return [EmployeeMapper.to_entity(m) for m in qs]

    def find_by_position(self, position_id: UUID) -> List[Employee]:
        qs = EmployeeModel.objects.filter(
            primary_position_id=position_id, tenant_id=self._tenant_id
        )
        return [EmployeeMapper.to_entity(m) for m in qs]

    def find_by_manager(self, manager_id: UUID) -> List[Employee]:
        qs = EmployeeModel.objects.filter(
            manager_id=manager_id, tenant_id=self._tenant_id
        )
        return [EmployeeMapper.to_entity(m) for m in qs]

    def find_active(self, tenant_id: UUID) -> List[Employee]:
        qs = EmployeeModel.objects.filter(
            tenant_id=tenant_id, is_active=True
        )
        return [EmployeeMapper.to_entity(m) for m in qs]

    def search(self, query: str, tenant_id: UUID) -> List[Employee]:
        qs = EmployeeModel.objects.filter(
            tenant_id=tenant_id
        ).filter(
            Q(first_name__icontains=query)
            | Q(last_name__icontains=query)
            | Q(employee_code__icontains=query)
            | Q(national_code__icontains=query)
            | Q(first_name_en__icontains=query)
            | Q(last_name_en__icontains=query)
            | Q(email__icontains=query)
        )
        return [EmployeeMapper.to_entity(m) for m in qs]


class DjangoEmploymentRecordRepository(IEmploymentRecordRepository):
    """پیاده‌سازی مخزن سوابق استخدامی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: EmploymentRecord) -> EmploymentRecord:
        try:
            model = EmploymentRecordModel.objects.get(id=entity.id)
            model = EmploymentRecordMapper.to_model(entity, model)
        except EmploymentRecordModel.DoesNotExist:
            model = EmploymentRecordMapper.to_model(entity)
        model.save()
        return EmploymentRecordMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[EmploymentRecord]:
        try:
            model = EmploymentRecordModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return EmploymentRecordMapper.to_entity(model)
        except EmploymentRecordModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[EmploymentRecord]:
        qs = EmploymentRecordModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [EmploymentRecordMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = EmploymentRecordModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[EmploymentRecord]:
        qs = EmploymentRecordModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        ).order_by("-start_date")
        return [EmploymentRecordMapper.to_entity(m) for m in qs]

    def find_current(self, employee_id: UUID) -> Optional[EmploymentRecord]:
        try:
            model = EmploymentRecordModel.objects.get(
                employee_id=employee_id,
                is_current=True,
                tenant_id=self._tenant_id,
            )
            return EmploymentRecordMapper.to_entity(model)
        except EmploymentRecordModel.DoesNotExist:
            return None


class DjangoEmployeeDocumentRepository(IEmployeeDocumentRepository):
    """پیاده‌سازی مخزن مدارک کارمند با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: EmployeeDocument) -> EmployeeDocument:
        try:
            model = EmployeeDocumentModel.objects.get(id=entity.id)
            model = EmployeeDocumentMapper.to_model(entity, model)
        except EmployeeDocumentModel.DoesNotExist:
            model = EmployeeDocumentMapper.to_model(entity)
        model.save()
        return EmployeeDocumentMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[EmployeeDocument]:
        try:
            model = EmployeeDocumentModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return EmployeeDocumentMapper.to_entity(model)
        except EmployeeDocumentModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[EmployeeDocument]:
        qs = EmployeeDocumentModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [EmployeeDocumentMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = EmployeeDocumentModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[EmployeeDocument]:
        qs = EmployeeDocumentModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        )
        return [EmployeeDocumentMapper.to_entity(m) for m in qs]

    def find_expiring(self, before_date: date) -> List[EmployeeDocument]:
        qs = EmployeeDocumentModel.objects.filter(
            tenant_id=self._tenant_id,
            expiry_date__isnull=False,
            expiry_date__lte=before_date,
        )
        return [EmployeeDocumentMapper.to_entity(m) for m in qs]
