"""
HRM Entity-Model Mappers.

تبدیل بین Domain Entities و ORM Models.
"""
from datetime import date, datetime
from decimal import Decimal
from typing import Optional
from uuid import UUID

from ..persistence.models import (
    LegalEntityModel,
    LocationModel,
    EmployeeModel,
    EmploymentRecordModel,
    EmployeeDocumentModel,
    JobFamilyModel,
    JobTitleModel,
    PositionModel,
    ReportingLineModel,
)
from ..persistence.models_extended import (
    WorkScheduleModel,
    ShiftPatternModel,
    TimesheetModel,
    AttendanceRecordModel,
    LeavePolicyModel,
    LeaveBalanceModel,
    LeaveRequestModel,
    PayGradeModel,
    CompensationRecordModel,
    BankAccountModel,
    CostAllocationModel,
)
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.entities.position import JobFamily, JobTitle, Position, ReportingLine
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,
)


# ═══════════════════════════════════════════════════
# Organization Structure Mappers
# ═══════════════════════════════════════════════════

class LegalEntityMapper:

    @staticmethod
    def to_entity(model: LegalEntityModel) -> LegalEntity:
        return LegalEntity(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            name_en=model.name_en,
            registration_number=model.registration_number,
            national_id=model.national_id,
            economic_code=model.economic_code,
            entity_type=model.entity_type,
            parent_entity_id=model.parent_entity_id,
            address=model.address,
            phone=model.phone,
            email=model.email,
            website=model.website,
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: LegalEntity, model: Optional[LegalEntityModel] = None) -> LegalEntityModel:
        if model is None:
            model = LegalEntityModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.name_en = entity.name_en
        model.registration_number = entity.registration_number
        model.national_id = entity.national_id
        model.economic_code = entity.economic_code
        model.entity_type = entity.entity_type
        model.parent_entity_id = entity.parent_entity_id
        model.address = entity.address
        model.phone = entity.phone
        model.email = entity.email
        model.website = entity.website
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class LocationMapper:

    @staticmethod
    def to_entity(model: LocationModel) -> Location:
        return Location(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            name_en=model.name_en,
            code=model.code,
            location_type=model.location_type,
            legal_entity_id=model.legal_entity_id,
            address=model.address,
            city=model.city,
            province=model.province,
            postal_code=model.postal_code,
            latitude=model.latitude,
            longitude=model.longitude,
            capacity=model.capacity,
            timezone=model.timezone,
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: Location, model: Optional[LocationModel] = None) -> LocationModel:
        if model is None:
            model = LocationModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.name_en = entity.name_en
        model.code = entity.code
        model.location_type = entity.location_type
        model.legal_entity_id = entity.legal_entity_id
        model.address = entity.address
        model.city = entity.city
        model.province = entity.province
        model.postal_code = entity.postal_code
        model.latitude = entity.latitude
        model.longitude = entity.longitude
        model.capacity = entity.capacity
        model.timezone = entity.timezone
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


# ═══════════════════════════════════════════════════
# Employee Master Data Mappers
# ═══════════════════════════════════════════════════

class EmployeeMapper:

    @staticmethod
    def to_entity(model: EmployeeModel) -> Employee:
        return Employee(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_code=model.employee_code,
            first_name=model.first_name,
            last_name=model.last_name,
            first_name_en=model.first_name_en or "",
            last_name_en=model.last_name_en or "",
            national_code=model.national_code,
            birth_certificate_number=model.birth_certificate_number or "",
            date_of_birth=model.date_of_birth,
            gender=model.gender,
            marital_status=model.marital_status,
            military_status=model.military_status,
            nationality=model.nationality or "IR",
            phone=model.phone or "",
            mobile=model.mobile or "",
            email=model.email or "",
            address=model.address or "",
            postal_code=model.postal_code or "",
            city=model.city or "",
            province=model.province or "",
            emergency_contact_name=model.emergency_contact_name or "",
            emergency_contact_phone=model.emergency_contact_phone or "",
            emergency_contact_relation=model.emergency_contact_relation or "",
            user_id=model.user_id,
            hire_date=model.hire_date,
            termination_date=model.termination_date,
            employment_status=model.employment_status,
            employment_type=model.employment_type,
            probation_end_date=model.probation_end_date,
            legal_entity_id=model.legal_entity_id,
            org_unit_id=model.org_unit_id,
            location_id=model.location_id,
            primary_position_id=model.primary_position_id,
            manager_id=model.manager_id,
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: Employee, model: Optional[EmployeeModel] = None) -> EmployeeModel:
        if model is None:
            model = EmployeeModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_code = entity.employee_code
        model.first_name = entity.first_name
        model.last_name = entity.last_name
        model.first_name_en = entity.first_name_en
        model.last_name_en = entity.last_name_en
        model.national_code = entity.national_code
        model.birth_certificate_number = entity.birth_certificate_number
        model.date_of_birth = entity.date_of_birth
        model.gender = entity.gender
        model.marital_status = entity.marital_status
        model.military_status = entity.military_status
        model.nationality = entity.nationality
        model.phone = entity.phone
        model.mobile = entity.mobile
        model.email = entity.email
        model.address = entity.address
        model.postal_code = entity.postal_code
        model.city = entity.city
        model.province = entity.province
        model.emergency_contact_name = entity.emergency_contact_name
        model.emergency_contact_phone = entity.emergency_contact_phone
        model.emergency_contact_relation = entity.emergency_contact_relation
        model.user_id = entity.user_id
        model.hire_date = entity.hire_date
        model.termination_date = entity.termination_date
        model.employment_status = entity.employment_status
        model.employment_type = entity.employment_type
        model.probation_end_date = entity.probation_end_date
        model.legal_entity_id = entity.legal_entity_id
        model.org_unit_id = entity.org_unit_id
        model.location_id = entity.location_id
        model.primary_position_id = entity.primary_position_id
        model.manager_id = entity.manager_id
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class EmploymentRecordMapper:

    @staticmethod
    def to_entity(model: EmploymentRecordModel) -> EmploymentRecord:
        return EmploymentRecord(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            contract_type=model.contract_type,
            contract_number=model.contract_number or "",
            start_date=model.start_date,
            end_date=model.end_date,
            position_id=model.position_id,
            org_unit_id=model.org_unit_id,
            notes=model.notes or "",
            is_current=model.is_current,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: EmploymentRecord, model: Optional[EmploymentRecordModel] = None) -> EmploymentRecordModel:
        if model is None:
            model = EmploymentRecordModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.contract_type = entity.contract_type
        model.contract_number = entity.contract_number
        model.start_date = entity.start_date
        model.end_date = entity.end_date
        model.position_id = entity.position_id
        model.org_unit_id = entity.org_unit_id
        model.notes = entity.notes
        model.is_current = entity.is_current
        model.metadata = entity.metadata
        return model


class EmployeeDocumentMapper:

    @staticmethod
    def to_entity(model: EmployeeDocumentModel) -> EmployeeDocument:
        return EmployeeDocument(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            document_type=model.document_type,
            title=model.title,
            file_path=model.file_path.name if model.file_path else "",
            issue_date=model.issue_date,
            expiry_date=model.expiry_date,
            notes=model.notes or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: EmployeeDocument, model: Optional[EmployeeDocumentModel] = None) -> EmployeeDocumentModel:
        if model is None:
            model = EmployeeDocumentModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.document_type = entity.document_type
        model.title = entity.title
        model.issue_date = entity.issue_date
        model.expiry_date = entity.expiry_date
        model.notes = entity.notes
        model.metadata = entity.metadata
        return model


# ═══════════════════════════════════════════════════
# Position & Job Mappers
# ═══════════════════════════════════════════════════

class JobFamilyMapper:

    @staticmethod
    def to_entity(model: JobFamilyModel) -> JobFamily:
        return JobFamily(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            name_en=model.name_en or "",
            code=model.code,
            description=model.description or "",
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: JobFamily, model: Optional[JobFamilyModel] = None) -> JobFamilyModel:
        if model is None:
            model = JobFamilyModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.name_en = entity.name_en
        model.code = entity.code
        model.description = entity.description
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class JobTitleMapper:

    @staticmethod
    def to_entity(model: JobTitleModel) -> JobTitle:
        return JobTitle(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            name_en=model.name_en or "",
            code=model.code,
            job_family_id=model.job_family_id,
            level=model.level,
            description=model.description or "",
            requirements=model.requirements or {},
            competencies=model.competencies or [],
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: JobTitle, model: Optional[JobTitleModel] = None) -> JobTitleModel:
        if model is None:
            model = JobTitleModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.name_en = entity.name_en
        model.code = entity.code
        model.job_family_id = entity.job_family_id
        model.level = entity.level
        model.description = entity.description
        model.requirements = entity.requirements
        model.competencies = entity.competencies
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class PositionMapper:

    @staticmethod
    def to_entity(model: PositionModel) -> Position:
        return Position(
            id=model.id,
            tenant_id=model.tenant_id,
            code=model.code,
            title=model.title,
            title_en=model.title_en or "",
            job_title_id=model.job_title_id,
            org_unit_id=model.org_unit_id,
            location_id=model.location_id,
            reports_to_id=model.reports_to_id,
            incumbent_id=model.incumbent_id,
            status=model.status,
            is_key_position=model.is_key_position,
            headcount=model.headcount,
            fte=float(model.fte) if model.fte else 1.0,
            budget_code=model.budget_code or "",
            cost_center_id=model.cost_center_id,
            effective_date=model.effective_date,
            end_date=model.end_date,
            notes=model.notes or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: Position, model: Optional[PositionModel] = None) -> PositionModel:
        if model is None:
            model = PositionModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.code = entity.code
        model.title = entity.title
        model.title_en = entity.title_en
        model.job_title_id = entity.job_title_id
        model.org_unit_id = entity.org_unit_id
        model.location_id = entity.location_id
        model.reports_to_id = entity.reports_to_id
        model.incumbent_id = entity.incumbent_id
        model.status = entity.status
        model.is_key_position = entity.is_key_position
        model.headcount = entity.headcount
        model.fte = Decimal(str(entity.fte))
        model.budget_code = entity.budget_code
        model.cost_center_id = entity.cost_center_id
        model.effective_date = entity.effective_date
        model.end_date = entity.end_date
        model.notes = entity.notes
        model.metadata = entity.metadata
        return model


class ReportingLineMapper:

    @staticmethod
    def to_entity(model: ReportingLineModel) -> ReportingLine:
        return ReportingLine(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            manager_id=model.manager_id,
            line_type=model.line_type,
            is_primary=model.is_primary,
            effective_date=model.effective_date,
            end_date=model.end_date,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: ReportingLine, model: Optional[ReportingLineModel] = None) -> ReportingLineModel:
        if model is None:
            model = ReportingLineModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.manager_id = entity.manager_id
        model.line_type = entity.line_type
        model.is_primary = entity.is_primary
        model.effective_date = entity.effective_date
        model.end_date = entity.end_date
        model.metadata = entity.metadata
        return model


# ═══════════════════════════════════════════════════
# Time & Attendance Mappers
# ═══════════════════════════════════════════════════

class WorkScheduleMapper:

    @staticmethod
    def to_entity(model: WorkScheduleModel) -> WorkSchedule:
        return WorkSchedule(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            code=model.code,
            schedule_type=model.schedule_type,
            start_time=model.start_time,
            end_time=model.end_time,
            break_duration_minutes=model.break_duration_minutes,
            working_days=model.working_days or [],
            daily_hours=float(model.daily_hours),
            weekly_hours=float(model.weekly_hours),
            is_night_shift=model.is_night_shift,
            overtime_threshold_daily=float(model.overtime_threshold_daily),
            overtime_threshold_weekly=float(model.overtime_threshold_weekly),
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: WorkSchedule, model: Optional[WorkScheduleModel] = None) -> WorkScheduleModel:
        if model is None:
            model = WorkScheduleModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.code = entity.code
        model.schedule_type = entity.schedule_type
        model.start_time = entity.start_time
        model.end_time = entity.end_time
        model.break_duration_minutes = entity.break_duration_minutes
        model.working_days = entity.working_days
        model.daily_hours = Decimal(str(entity.daily_hours))
        model.weekly_hours = Decimal(str(entity.weekly_hours))
        model.is_night_shift = entity.is_night_shift
        model.overtime_threshold_daily = Decimal(str(entity.overtime_threshold_daily))
        model.overtime_threshold_weekly = Decimal(str(entity.overtime_threshold_weekly))
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class TimesheetMapper:

    @staticmethod
    def to_entity(model: TimesheetModel) -> Timesheet:
        return Timesheet(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            period_start=model.period_start,
            period_end=model.period_end,
            status=model.status,
            total_regular_hours=float(model.total_regular_hours),
            total_overtime_hours=float(model.total_overtime_hours),
            total_working_days=model.total_working_days,
            submitted_at=model.submitted_at,
            approved_at=model.approved_at,
            approved_by=model.approved_by,
            rejection_reason=model.rejection_reason or "",
            notes=model.notes or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: Timesheet, model: Optional[TimesheetModel] = None) -> TimesheetModel:
        if model is None:
            model = TimesheetModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.period_start = entity.period_start
        model.period_end = entity.period_end
        model.status = entity.status
        model.total_regular_hours = Decimal(str(entity.total_regular_hours))
        model.total_overtime_hours = Decimal(str(entity.total_overtime_hours))
        model.total_working_days = entity.total_working_days
        model.submitted_at = entity.submitted_at
        model.approved_at = entity.approved_at
        model.approved_by = entity.approved_by
        model.rejection_reason = entity.rejection_reason
        model.notes = entity.notes
        model.metadata = entity.metadata
        return model


class AttendanceRecordMapper:

    @staticmethod
    def to_entity(model: AttendanceRecordModel) -> AttendanceRecord:
        return AttendanceRecord(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            attendance_date=model.attendance_date,
            check_in=model.check_in,
            check_out=model.check_out,
            status=model.status,
            scheduled_start=model.scheduled_start,
            scheduled_end=model.scheduled_end,
            late_minutes=model.late_minutes,
            early_departure_minutes=model.early_departure_minutes,
            overtime_minutes=model.overtime_minutes,
            break_minutes=model.break_minutes,
            effective_hours=float(model.effective_hours),
            source=model.source,
            location_id=model.location_id if model.location else None,
            notes=model.notes or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: AttendanceRecord, model: Optional[AttendanceRecordModel] = None) -> AttendanceRecordModel:
        if model is None:
            model = AttendanceRecordModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.attendance_date = entity.attendance_date
        model.check_in = entity.check_in
        model.check_out = entity.check_out
        model.status = entity.status
        model.scheduled_start = entity.scheduled_start
        model.scheduled_end = entity.scheduled_end
        model.late_minutes = entity.late_minutes
        model.early_departure_minutes = entity.early_departure_minutes
        model.overtime_minutes = entity.overtime_minutes
        model.break_minutes = entity.break_minutes
        model.effective_hours = Decimal(str(entity.effective_hours))
        model.source = entity.source
        model.location_id = entity.location_id
        model.notes = entity.notes
        model.metadata = entity.metadata
        return model


# ═══════════════════════════════════════════════════
# Leave Management Mappers
# ═══════════════════════════════════════════════════

class LeavePolicyMapper:

    @staticmethod
    def to_entity(model: LeavePolicyModel) -> LeavePolicy:
        return LeavePolicy(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            code=model.code,
            leave_type=model.leave_type,
            annual_entitlement_days=float(model.annual_entitlement_days),
            max_carryover_days=float(model.max_carryover_days),
            max_accumulation_days=float(model.max_accumulation_days),
            min_service_days_required=model.min_service_days_required,
            accrual_method=model.accrual_method,
            requires_approval=model.requires_approval,
            max_consecutive_days=model.max_consecutive_days,
            min_notice_days=model.min_notice_days,
            allow_half_day=model.allow_half_day,
            allow_hourly=model.allow_hourly,
            is_paid=model.is_paid,
            applicable_genders=model.applicable_genders or [],
            applicable_employment_types=model.applicable_employment_types or [],
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: LeavePolicy, model: Optional[LeavePolicyModel] = None) -> LeavePolicyModel:
        if model is None:
            model = LeavePolicyModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.code = entity.code
        model.leave_type = entity.leave_type
        model.annual_entitlement_days = Decimal(str(entity.annual_entitlement_days))
        model.max_carryover_days = Decimal(str(entity.max_carryover_days))
        model.max_accumulation_days = Decimal(str(entity.max_accumulation_days))
        model.min_service_days_required = entity.min_service_days_required
        model.accrual_method = entity.accrual_method
        model.requires_approval = entity.requires_approval
        model.max_consecutive_days = entity.max_consecutive_days
        model.min_notice_days = entity.min_notice_days
        model.allow_half_day = entity.allow_half_day
        model.allow_hourly = entity.allow_hourly
        model.is_paid = entity.is_paid
        model.applicable_genders = entity.applicable_genders
        model.applicable_employment_types = entity.applicable_employment_types
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class LeaveBalanceMapper:

    @staticmethod
    def to_entity(model: LeaveBalanceModel) -> LeaveBalance:
        return LeaveBalance(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            leave_policy_id=model.leave_policy_id,
            leave_type=model.leave_type,
            year=model.year,
            entitled_days=float(model.entitled_days),
            carried_over_days=float(model.carried_over_days),
            used_days=float(model.used_days),
            pending_days=float(model.pending_days),
            adjustment_days=float(model.adjustment_days),
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: LeaveBalance, model: Optional[LeaveBalanceModel] = None) -> LeaveBalanceModel:
        if model is None:
            model = LeaveBalanceModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.leave_policy_id = entity.leave_policy_id
        model.leave_type = entity.leave_type
        model.year = entity.year
        model.entitled_days = Decimal(str(entity.entitled_days))
        model.carried_over_days = Decimal(str(entity.carried_over_days))
        model.used_days = Decimal(str(entity.used_days))
        model.pending_days = Decimal(str(entity.pending_days))
        model.adjustment_days = Decimal(str(entity.adjustment_days))
        model.metadata = entity.metadata
        return model


class LeaveRequestMapper:

    @staticmethod
    def to_entity(model: LeaveRequestModel) -> LeaveRequest:
        return LeaveRequest(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            leave_type=model.leave_type,
            leave_policy_id=model.leave_policy_id,
            start_date=model.start_date,
            end_date=model.end_date,
            days_count=float(model.days_count),
            is_half_day=model.is_half_day,
            half_day_period=model.half_day_period or "",
            status=model.status,
            reason=model.reason or "",
            substitute_id=model.substitute_id,
            approved_by=model.approved_by,
            approved_at=model.approved_at,
            rejection_reason=model.rejection_reason or "",
            notes=model.notes or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: LeaveRequest, model: Optional[LeaveRequestModel] = None) -> LeaveRequestModel:
        if model is None:
            model = LeaveRequestModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.leave_type = entity.leave_type
        model.leave_policy_id = entity.leave_policy_id
        model.start_date = entity.start_date
        model.end_date = entity.end_date
        model.days_count = Decimal(str(entity.days_count))
        model.is_half_day = entity.is_half_day
        model.half_day_period = entity.half_day_period
        model.status = entity.status
        model.reason = entity.reason
        model.substitute_id = entity.substitute_id
        model.approved_by = entity.approved_by
        model.approved_at = entity.approved_at
        model.rejection_reason = entity.rejection_reason
        model.notes = entity.notes
        model.metadata = entity.metadata
        return model


# ═══════════════════════════════════════════════════
# Payroll Foundation Mappers
# ═══════════════════════════════════════════════════

class PayGradeMapper:

    @staticmethod
    def to_entity(model: PayGradeModel) -> PayGrade:
        return PayGrade(
            id=model.id,
            tenant_id=model.tenant_id,
            name=model.name,
            code=model.code,
            grade_type=model.grade_type,
            min_amount=float(model.min_amount),
            mid_amount=float(model.mid_amount),
            max_amount=float(model.max_amount),
            currency=model.currency,
            step_count=model.step_count,
            step_increment=float(model.step_increment),
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: PayGrade, model: Optional[PayGradeModel] = None) -> PayGradeModel:
        if model is None:
            model = PayGradeModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.name = entity.name
        model.code = entity.code
        model.grade_type = entity.grade_type
        model.min_amount = Decimal(str(entity.min_amount))
        model.mid_amount = Decimal(str(entity.mid_amount))
        model.max_amount = Decimal(str(entity.max_amount))
        model.currency = entity.currency
        model.step_count = entity.step_count
        model.step_increment = Decimal(str(entity.step_increment))
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class CompensationRecordMapper:

    @staticmethod
    def to_entity(model: CompensationRecordModel) -> CompensationRecord:
        return CompensationRecord(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            pay_grade_id=model.pay_grade_id,
            base_salary=float(model.base_salary),
            housing_allowance=float(model.housing_allowance),
            transportation_allowance=float(model.transportation_allowance),
            food_allowance=float(model.food_allowance),
            family_allowance=float(model.family_allowance),
            overtime_rate=float(model.overtime_rate),
            holiday_rate=float(model.holiday_rate),
            night_shift_rate=float(model.night_shift_rate),
            currency=model.currency,
            pay_grade_step=model.pay_grade_step,
            effective_date=model.effective_date,
            end_date=model.end_date,
            is_current=model.is_current,
            change_reason=model.change_reason or "",
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: CompensationRecord, model: Optional[CompensationRecordModel] = None) -> CompensationRecordModel:
        if model is None:
            model = CompensationRecordModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.pay_grade_id = entity.pay_grade_id
        model.base_salary = Decimal(str(entity.base_salary))
        model.housing_allowance = Decimal(str(entity.housing_allowance))
        model.transportation_allowance = Decimal(str(entity.transportation_allowance))
        model.food_allowance = Decimal(str(entity.food_allowance))
        model.family_allowance = Decimal(str(entity.family_allowance))
        model.overtime_rate = Decimal(str(entity.overtime_rate))
        model.holiday_rate = Decimal(str(entity.holiday_rate))
        model.night_shift_rate = Decimal(str(entity.night_shift_rate))
        model.currency = entity.currency
        model.pay_grade_step = entity.pay_grade_step
        model.effective_date = entity.effective_date
        model.end_date = entity.end_date
        model.is_current = entity.is_current
        model.change_reason = entity.change_reason
        model.metadata = entity.metadata
        return model


class BankAccountMapper:

    @staticmethod
    def to_entity(model: BankAccountModel) -> BankAccount:
        return BankAccount(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            bank_name=model.bank_name,
            branch_name=model.branch_name or "",
            branch_code=model.branch_code or "",
            account_number=model.account_number,
            sheba_number=model.sheba_number or "",
            card_number=model.card_number or "",
            account_holder_name=model.account_holder_name or "",
            is_primary=model.is_primary,
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: BankAccount, model: Optional[BankAccountModel] = None) -> BankAccountModel:
        if model is None:
            model = BankAccountModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.bank_name = entity.bank_name
        model.branch_name = entity.branch_name
        model.branch_code = entity.branch_code
        model.account_number = entity.account_number
        model.sheba_number = entity.sheba_number
        model.card_number = entity.card_number
        model.account_holder_name = entity.account_holder_name
        model.is_primary = entity.is_primary
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model


class CostAllocationMapper:

    @staticmethod
    def to_entity(model: CostAllocationModel) -> CostAllocation:
        return CostAllocation(
            id=model.id,
            tenant_id=model.tenant_id,
            employee_id=model.employee_id,
            allocation_type=model.allocation_type,
            target_id=model.target_id,
            target_code=model.target_code or "",
            percentage=float(model.percentage),
            gl_account_code=model.gl_account_code or "",
            effective_date=model.effective_date,
            end_date=model.end_date,
            is_active=model.is_active,
            metadata=model.metadata or {},
            created_at=model.created_at,
            updated_at=model.updated_at,
        )

    @staticmethod
    def to_model(entity: CostAllocation, model: Optional[CostAllocationModel] = None) -> CostAllocationModel:
        if model is None:
            model = CostAllocationModel(id=entity.id)
        model.tenant_id = entity.tenant_id
        model.employee_id = entity.employee_id
        model.allocation_type = entity.allocation_type
        model.target_id = entity.target_id
        model.target_code = entity.target_code
        model.percentage = Decimal(str(entity.percentage))
        model.gl_account_code = entity.gl_account_code
        model.effective_date = entity.effective_date
        model.end_date = entity.end_date
        model.is_active = entity.is_active
        model.metadata = entity.metadata
        return model
