"""
HRM Repository Implementations - Position & Job.

پیاده‌سازی repositoryهای پست و شغل با Django ORM.
"""
from typing import Optional, List
from uuid import UUID

from ...domain.entities.position import JobFamily, JobTitle, Position, ReportingLine
from ...domain.repositories.interfaces import (
    IJobFamilyRepository,
    IJobTitleRepository,
    IPositionRepository,
    IReportingLineRepository,
)
from ..persistence.models import (
    JobFamilyModel,
    JobTitleModel,
    PositionModel,
    ReportingLineModel,
)
from .mappers import (
    JobFamilyMapper,
    JobTitleMapper,
    PositionMapper,
    ReportingLineMapper,
)


class DjangoJobFamilyRepository(IJobFamilyRepository):
    """پیاده‌سازی مخزن خانواده شغلی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: JobFamily) -> JobFamily:
        try:
            model = JobFamilyModel.objects.get(id=entity.id)
            model = JobFamilyMapper.to_model(entity, model)
        except JobFamilyModel.DoesNotExist:
            model = JobFamilyMapper.to_model(entity)
        model.save()
        return JobFamilyMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[JobFamily]:
        try:
            model = JobFamilyModel.objects.get(id=id, tenant_id=self._tenant_id)
            return JobFamilyMapper.to_entity(model)
        except JobFamilyModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[JobFamily]:
        qs = JobFamilyModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [JobFamilyMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = JobFamilyModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[JobFamily]:
        try:
            model = JobFamilyModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return JobFamilyMapper.to_entity(model)
        except JobFamilyModel.DoesNotExist:
            return None


class DjangoJobTitleRepository(IJobTitleRepository):
    """پیاده‌سازی مخزن عنوان شغلی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: JobTitle) -> JobTitle:
        try:
            model = JobTitleModel.objects.get(id=entity.id)
            model = JobTitleMapper.to_model(entity, model)
        except JobTitleModel.DoesNotExist:
            model = JobTitleMapper.to_model(entity)
        model.save()
        return JobTitleMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[JobTitle]:
        try:
            model = JobTitleModel.objects.get(id=id, tenant_id=self._tenant_id)
            return JobTitleMapper.to_entity(model)
        except JobTitleModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[JobTitle]:
        qs = JobTitleModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [JobTitleMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = JobTitleModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[JobTitle]:
        try:
            model = JobTitleModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return JobTitleMapper.to_entity(model)
        except JobTitleModel.DoesNotExist:
            return None

    def find_by_family(self, family_id: UUID) -> List[JobTitle]:
        qs = JobTitleModel.objects.filter(
            job_family_id=family_id, tenant_id=self._tenant_id
        )
        return [JobTitleMapper.to_entity(m) for m in qs]


class DjangoPositionRepository(IPositionRepository):
    """پیاده‌سازی مخزن پست سازمانی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Position) -> Position:
        try:
            model = PositionModel.objects.get(id=entity.id)
            model = PositionMapper.to_model(entity, model)
        except PositionModel.DoesNotExist:
            model = PositionMapper.to_model(entity)
        model.save()
        return PositionMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Position]:
        try:
            model = PositionModel.objects.get(id=id, tenant_id=self._tenant_id)
            return PositionMapper.to_entity(model)
        except PositionModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Position]:
        qs = PositionModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [PositionMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = PositionModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[Position]:
        try:
            model = PositionModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return PositionMapper.to_entity(model)
        except PositionModel.DoesNotExist:
            return None

    def find_by_org_unit(self, org_unit_id: UUID) -> List[Position]:
        qs = PositionModel.objects.filter(
            org_unit_id=org_unit_id, tenant_id=self._tenant_id
        )
        return [PositionMapper.to_entity(m) for m in qs]

    def find_vacant(self, tenant_id: UUID) -> List[Position]:
        qs = PositionModel.objects.filter(
            tenant_id=tenant_id,
            status="ACTIVE",
            incumbent__isnull=True,
        )
        return [PositionMapper.to_entity(m) for m in qs]

    def find_by_incumbent(self, employee_id: UUID) -> Optional[Position]:
        try:
            model = PositionModel.objects.get(
                incumbent_id=employee_id, tenant_id=self._tenant_id
            )
            return PositionMapper.to_entity(model)
        except PositionModel.DoesNotExist:
            return None

    def find_key_positions(self, tenant_id: UUID) -> List[Position]:
        qs = PositionModel.objects.filter(
            tenant_id=tenant_id, is_key_position=True
        )
        return [PositionMapper.to_entity(m) for m in qs]


class DjangoReportingLineRepository(IReportingLineRepository):
    """پیاده‌سازی مخزن خطوط گزارش‌دهی با Django ORM."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ReportingLine) -> ReportingLine:
        try:
            model = ReportingLineModel.objects.get(id=entity.id)
            model = ReportingLineMapper.to_model(entity, model)
        except ReportingLineModel.DoesNotExist:
            model = ReportingLineMapper.to_model(entity)
        model.save()
        return ReportingLineMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[ReportingLine]:
        try:
            model = ReportingLineModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return ReportingLineMapper.to_entity(model)
        except ReportingLineModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ReportingLine]:
        qs = ReportingLineModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [ReportingLineMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = ReportingLineModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[ReportingLine]:
        qs = ReportingLineModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        )
        return [ReportingLineMapper.to_entity(m) for m in qs]

    def find_direct_reports(self, manager_id: UUID) -> List[ReportingLine]:
        qs = ReportingLineModel.objects.filter(
            manager_id=manager_id, tenant_id=self._tenant_id
        )
        return [ReportingLineMapper.to_entity(m) for m in qs]
