"""
HRM Repository Implementations - Time, Attendance, Leave, Payroll.

پیاده‌سازی repositoryهای حضور/غیاب، مرخصی و حقوق با Django ORM.
"""
from datetime import date
from typing import Optional, List
from uuid import UUID

from ...domain.entities.time_attendance import (
    WorkSchedule, ShiftPattern, Timesheet, AttendanceRecord,
)
from ...domain.entities.leave import LeavePolicy, LeaveBalance, LeaveRequest
from ...domain.entities.payroll import (
    PayGrade, CompensationRecord, BankAccount, CostAllocation,
)
from ...domain.repositories.interfaces import (
    IWorkScheduleRepository,
    IShiftPatternRepository,
    ITimesheetRepository,
    IAttendanceRecordRepository,
    ILeavePolicyRepository,
    ILeaveBalanceRepository,
    ILeaveRequestRepository,
    IPayGradeRepository,
    ICompensationRecordRepository,
    IBankAccountRepository,
    ICostAllocationRepository,
)
from ..persistence.models_extended import (
    WorkScheduleModel,
    ShiftPatternModel,
    TimesheetModel,
    AttendanceRecordModel,
    LeavePolicyModel,
    LeaveBalanceModel,
    LeaveRequestModel,
    PayGradeModel,
    CompensationRecordModel,
    BankAccountModel,
    CostAllocationModel,
)
from .mappers import (
    WorkScheduleMapper,
    TimesheetMapper,
    AttendanceRecordMapper,
    LeavePolicyMapper,
    LeaveBalanceMapper,
    LeaveRequestMapper,
    PayGradeMapper,
    CompensationRecordMapper,
    BankAccountMapper,
    CostAllocationMapper,
)


# ═══════════════════════════════════════════════════
# Time & Attendance
# ═══════════════════════════════════════════════════

class DjangoWorkScheduleRepository(IWorkScheduleRepository):
    """پیاده‌سازی مخزن برنامه کاری."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: WorkSchedule) -> WorkSchedule:
        try:
            model = WorkScheduleModel.objects.get(id=entity.id)
            model = WorkScheduleMapper.to_model(entity, model)
        except WorkScheduleModel.DoesNotExist:
            model = WorkScheduleMapper.to_model(entity)
        model.save()
        return WorkScheduleMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[WorkSchedule]:
        try:
            model = WorkScheduleModel.objects.get(id=id, tenant_id=self._tenant_id)
            return WorkScheduleMapper.to_entity(model)
        except WorkScheduleModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[WorkSchedule]:
        qs = WorkScheduleModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [WorkScheduleMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = WorkScheduleModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[WorkSchedule]:
        try:
            model = WorkScheduleModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return WorkScheduleMapper.to_entity(model)
        except WorkScheduleModel.DoesNotExist:
            return None

    def find_active(self, tenant_id: UUID) -> List[WorkSchedule]:
        qs = WorkScheduleModel.objects.filter(tenant_id=tenant_id, is_active=True)
        return [WorkScheduleMapper.to_entity(m) for m in qs]


class DjangoShiftPatternRepository(IShiftPatternRepository):
    """پیاده‌سازی مخزن الگوی شیفت."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: ShiftPattern) -> ShiftPattern:
        from .mappers import ShiftPatternModel as _  # noqa
        try:
            model = ShiftPatternModel.objects.get(id=entity.id)
        except ShiftPatternModel.DoesNotExist:
            model = ShiftPatternModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.code = entity.code
        model.cycle_days = entity.cycle_days
        model.shifts = entity.shifts
        model.is_active = entity.is_active
        model.save()
        return entity

    def get_by_id(self, id: UUID) -> Optional[ShiftPattern]:
        try:
            model = ShiftPatternModel.objects.get(id=id, tenant_id=self._tenant_id)
            return ShiftPattern(
                id=model.id,
                tenant_id=model.tenant_id,
                name=model.name,
                code=model.code,
                cycle_days=model.cycle_days,
                shifts=model.shifts or [],
                is_active=model.is_active,
                metadata={},
                created_at=model.created_at,
                updated_at=model.updated_at,
            )
        except ShiftPatternModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[ShiftPattern]:
        qs = ShiftPatternModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [self._to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = ShiftPatternModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    @staticmethod
    def _to_entity(model) -> ShiftPattern:
        return ShiftPattern(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            code=model.code,
            cycle_days=model.cycle_days,
            shifts=model.shifts or [],
            is_active=model.is_active,
            metadata={},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )


class DjangoTimesheetRepository(ITimesheetRepository):
    """پیاده‌سازی مخزن تایم‌شیت."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: Timesheet) -> Timesheet:
        try:
            model = TimesheetModel.objects.get(id=entity.id)
            model = TimesheetMapper.to_model(entity, model)
        except TimesheetModel.DoesNotExist:
            model = TimesheetMapper.to_model(entity)
        model.save()
        return TimesheetMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[Timesheet]:
        try:
            model = TimesheetModel.objects.get(id=id, tenant_id=self._tenant_id)
            return TimesheetMapper.to_entity(model)
        except TimesheetModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [TimesheetMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = TimesheetModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee_period(
        self, employee_id: UUID, period_start: date, period_end: date
    ) -> Optional[Timesheet]:
        try:
            model = TimesheetModel.objects.get(
                employee_id=employee_id,
                period_start=period_start,
                period_end=period_end,
                tenant_id=self._tenant_id,
            )
            return TimesheetMapper.to_entity(model)
        except TimesheetModel.DoesNotExist:
            return None

    def find_pending_approval(self, tenant_id: UUID) -> List[Timesheet]:
        qs = TimesheetModel.objects.filter(
            tenant_id=tenant_id,
            status=TimesheetModel.TimesheetStatus.SUBMITTED,
        ).order_by("submitted_at")
        return [TimesheetMapper.to_entity(m) for m in qs]


class DjangoAttendanceRecordRepository(IAttendanceRecordRepository):
    """پیاده‌سازی مخزن حضور و غیاب."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: AttendanceRecord) -> AttendanceRecord:
        try:
            model = AttendanceRecordModel.objects.get(id=entity.id)
            model = AttendanceRecordMapper.to_model(entity, model)
        except AttendanceRecordModel.DoesNotExist:
            model = AttendanceRecordMapper.to_model(entity)
        model.save()
        return AttendanceRecordMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[AttendanceRecord]:
        try:
            model = AttendanceRecordModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return AttendanceRecordMapper.to_entity(model)
        except AttendanceRecordModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[AttendanceRecord]:
        qs = AttendanceRecordModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [AttendanceRecordMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = AttendanceRecordModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee_date(
        self, employee_id: UUID, attendance_date: date
    ) -> Optional[AttendanceRecord]:
        try:
            model = AttendanceRecordModel.objects.get(
                employee_id=employee_id,
                attendance_date=attendance_date,
                tenant_id=self._tenant_id,
            )
            return AttendanceRecordMapper.to_entity(model)
        except AttendanceRecordModel.DoesNotExist:
            return None

    def find_by_employee_range(
        self, employee_id: UUID, start_date: date, end_date: date
    ) -> List[AttendanceRecord]:
        qs = AttendanceRecordModel.objects.filter(
            employee_id=employee_id,
            attendance_date__gte=start_date,
            attendance_date__lte=end_date,
            tenant_id=self._tenant_id,
        ).order_by("attendance_date")
        return [AttendanceRecordMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Leave Management
# ═══════════════════════════════════════════════════

class DjangoLeavePolicyRepository(ILeavePolicyRepository):
    """پیاده‌سازی مخزن سیاست مرخصی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: LeavePolicy) -> LeavePolicy:
        try:
            model = LeavePolicyModel.objects.get(id=entity.id)
            model = LeavePolicyMapper.to_model(entity, model)
        except LeavePolicyModel.DoesNotExist:
            model = LeavePolicyMapper.to_model(entity)
        model.save()
        return LeavePolicyMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[LeavePolicy]:
        try:
            model = LeavePolicyModel.objects.get(id=id, tenant_id=self._tenant_id)
            return LeavePolicyMapper.to_entity(model)
        except LeavePolicyModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[LeavePolicy]:
        qs = LeavePolicyModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [LeavePolicyMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = LeavePolicyModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_type(self, leave_type: str, tenant_id: UUID) -> Optional[LeavePolicy]:
        try:
            model = LeavePolicyModel.objects.get(
                leave_type=leave_type, tenant_id=tenant_id, is_active=True
            )
            return LeavePolicyMapper.to_entity(model)
        except LeavePolicyModel.DoesNotExist:
            return None

    def find_active(self, tenant_id: UUID) -> List[LeavePolicy]:
        qs = LeavePolicyModel.objects.filter(tenant_id=tenant_id, is_active=True)
        return [LeavePolicyMapper.to_entity(m) for m in qs]


class DjangoLeaveBalanceRepository(ILeaveBalanceRepository):
    """پیاده‌سازی مخزن مانده مرخصی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: LeaveBalance) -> LeaveBalance:
        try:
            model = LeaveBalanceModel.objects.get(id=entity.id)
            model = LeaveBalanceMapper.to_model(entity, model)
        except LeaveBalanceModel.DoesNotExist:
            model = LeaveBalanceMapper.to_model(entity)
        model.save()
        return LeaveBalanceMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[LeaveBalance]:
        try:
            model = LeaveBalanceModel.objects.get(id=id, tenant_id=self._tenant_id)
            return LeaveBalanceMapper.to_entity(model)
        except LeaveBalanceModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[LeaveBalance]:
        qs = LeaveBalanceModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [LeaveBalanceMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = LeaveBalanceModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee_year(
        self, employee_id: UUID, year: int
    ) -> List[LeaveBalance]:
        qs = LeaveBalanceModel.objects.filter(
            employee_id=employee_id, year=year, tenant_id=self._tenant_id
        )
        return [LeaveBalanceMapper.to_entity(m) for m in qs]

    def find_by_employee_type(
        self, employee_id: UUID, leave_type: str, year: int
    ) -> Optional[LeaveBalance]:
        try:
            model = LeaveBalanceModel.objects.get(
                employee_id=employee_id,
                leave_type=leave_type,
                year=year,
                tenant_id=self._tenant_id,
            )
            return LeaveBalanceMapper.to_entity(model)
        except LeaveBalanceModel.DoesNotExist:
            return None


class DjangoLeaveRequestRepository(ILeaveRequestRepository):
    """پیاده‌سازی مخزن درخواست مرخصی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: LeaveRequest) -> LeaveRequest:
        try:
            model = LeaveRequestModel.objects.get(id=entity.id)
            model = LeaveRequestMapper.to_model(entity, model)
        except LeaveRequestModel.DoesNotExist:
            model = LeaveRequestMapper.to_model(entity)
        model.save()
        return LeaveRequestMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[LeaveRequest]:
        try:
            model = LeaveRequestModel.objects.get(id=id, tenant_id=self._tenant_id)
            return LeaveRequestMapper.to_entity(model)
        except LeaveRequestModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[LeaveRequest]:
        qs = LeaveRequestModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [LeaveRequestMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = LeaveRequestModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[LeaveRequest]:
        qs = LeaveRequestModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        ).order_by("-created_at")
        return [LeaveRequestMapper.to_entity(m) for m in qs]

    def find_pending_approval(self, tenant_id: UUID) -> List[LeaveRequest]:
        qs = LeaveRequestModel.objects.filter(
            tenant_id=tenant_id,
            status=LeaveRequestModel.LeaveRequestStatus.PENDING,
        ).order_by("created_at")
        return [LeaveRequestMapper.to_entity(m) for m in qs]

    def find_by_date_range(
        self, employee_id: UUID, start_date: date, end_date: date
    ) -> List[LeaveRequest]:
        qs = LeaveRequestModel.objects.filter(
            employee_id=employee_id,
            tenant_id=self._tenant_id,
            start_date__lte=end_date,
            end_date__gte=start_date,
        ).order_by("start_date")
        return [LeaveRequestMapper.to_entity(m) for m in qs]


# ═══════════════════════════════════════════════════
# Payroll Foundation
# ═══════════════════════════════════════════════════

class DjangoPayGradeRepository(IPayGradeRepository):
    """پیاده‌سازی مخزن رتبه حقوقی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: PayGrade) -> PayGrade:
        try:
            model = PayGradeModel.objects.get(id=entity.id)
            model = PayGradeMapper.to_model(entity, model)
        except PayGradeModel.DoesNotExist:
            model = PayGradeMapper.to_model(entity)
        model.save()
        return PayGradeMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[PayGrade]:
        try:
            model = PayGradeModel.objects.get(id=id, tenant_id=self._tenant_id)
            return PayGradeMapper.to_entity(model)
        except PayGradeModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[PayGrade]:
        qs = PayGradeModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [PayGradeMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = PayGradeModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_code(self, code: str) -> Optional[PayGrade]:
        try:
            model = PayGradeModel.objects.get(
                code=code, tenant_id=self._tenant_id
            )
            return PayGradeMapper.to_entity(model)
        except PayGradeModel.DoesNotExist:
            return None

    def find_active(self, tenant_id: UUID) -> List[PayGrade]:
        qs = PayGradeModel.objects.filter(tenant_id=tenant_id, is_active=True)
        return [PayGradeMapper.to_entity(m) for m in qs]


class DjangoCompensationRecordRepository(ICompensationRecordRepository):
    """پیاده‌سازی مخزن سوابق حقوقی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: CompensationRecord) -> CompensationRecord:
        try:
            model = CompensationRecordModel.objects.get(id=entity.id)
            model = CompensationRecordMapper.to_model(entity, model)
        except CompensationRecordModel.DoesNotExist:
            model = CompensationRecordMapper.to_model(entity)
        model.save()
        return CompensationRecordMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[CompensationRecord]:
        try:
            model = CompensationRecordModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return CompensationRecordMapper.to_entity(model)
        except CompensationRecordModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[CompensationRecord]:
        qs = CompensationRecordModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [CompensationRecordMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = CompensationRecordModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_current(self, employee_id: UUID) -> Optional[CompensationRecord]:
        try:
            model = CompensationRecordModel.objects.get(
                employee_id=employee_id,
                is_current=True,
                tenant_id=self._tenant_id,
            )
            return CompensationRecordMapper.to_entity(model)
        except CompensationRecordModel.DoesNotExist:
            return None

    def find_by_employee(self, employee_id: UUID) -> List[CompensationRecord]:
        qs = CompensationRecordModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        ).order_by("-effective_date")
        return [CompensationRecordMapper.to_entity(m) for m in qs]


class DjangoBankAccountRepository(IBankAccountRepository):
    """پیاده‌سازی مخزن حساب بانکی."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: BankAccount) -> BankAccount:
        try:
            model = BankAccountModel.objects.get(id=entity.id)
            model = BankAccountMapper.to_model(entity, model)
        except BankAccountModel.DoesNotExist:
            model = BankAccountMapper.to_model(entity)
        model.save()
        return BankAccountMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[BankAccount]:
        try:
            model = BankAccountModel.objects.get(id=id, tenant_id=self._tenant_id)
            return BankAccountMapper.to_entity(model)
        except BankAccountModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[BankAccount]:
        qs = BankAccountModel.objects.filter(tenant_id=self._tenant_id, **criteria)
        return [BankAccountMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = BankAccountModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[BankAccount]:
        qs = BankAccountModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id
        )
        return [BankAccountMapper.to_entity(m) for m in qs]

    def find_primary(self, employee_id: UUID) -> Optional[BankAccount]:
        try:
            model = BankAccountModel.objects.get(
                employee_id=employee_id,
                is_primary=True,
                tenant_id=self._tenant_id,
            )
            return BankAccountMapper.to_entity(model)
        except BankAccountModel.DoesNotExist:
            return None


class DjangoCostAllocationRepository(ICostAllocationRepository):
    """پیاده‌سازی مخزن تخصیص هزینه."""

    def __init__(self, tenant_id: UUID):
        self._tenant_id = tenant_id

    def save(self, entity: CostAllocation) -> CostAllocation:
        try:
            model = CostAllocationModel.objects.get(id=entity.id)
            model = CostAllocationMapper.to_model(entity, model)
        except CostAllocationModel.DoesNotExist:
            model = CostAllocationMapper.to_model(entity)
        model.save()
        return CostAllocationMapper.to_entity(model)

    def get_by_id(self, id: UUID) -> Optional[CostAllocation]:
        try:
            model = CostAllocationModel.objects.get(
                id=id, tenant_id=self._tenant_id
            )
            return CostAllocationMapper.to_entity(model)
        except CostAllocationModel.DoesNotExist:
            return None

    def find(self, **criteria) -> List[CostAllocation]:
        qs = CostAllocationModel.objects.filter(
            tenant_id=self._tenant_id, **criteria
        )
        return [CostAllocationMapper.to_entity(m) for m in qs]

    def delete(self, id: UUID) -> bool:
        deleted, _ = CostAllocationModel.objects.filter(
            id=id, tenant_id=self._tenant_id
        ).delete()
        return deleted > 0

    def find_by_employee(self, employee_id: UUID) -> List[CostAllocation]:
        qs = CostAllocationModel.objects.filter(
            employee_id=employee_id, tenant_id=self._tenant_id, is_active=True
        )
        return [CostAllocationMapper.to_entity(m) for m in qs]

    def find_by_target(
        self, allocation_type: str, target_id: UUID
    ) -> List[CostAllocation]:
        qs = CostAllocationModel.objects.filter(
            allocation_type=allocation_type,
            target_id=target_id,
            tenant_id=self._tenant_id,
            is_active=True,
        )
        return [CostAllocationMapper.to_entity(m) for m in qs]
