"""
filters.py

This page is used to register filter for attendance models

"""

import datetime
import uuid

import django_filters
from django import forms
from django.forms import DateTimeInput
from django.utils.translation import gettext_lazy as _

from attendance.models import (
    Attendance,
    AttendanceActivity,
    AttendanceLateComeEarlyOut,
    AttendanceOverTime,
    strtime_seconds,
)
from base.filters import FilterSet
from employee.filters import EmployeeFilter
from employee.models import Employee
from horilla.filters import filter_by_name


class DurationInSecondsFilter(django_filters.CharFilter):
    """
    Custom CharFilter class that applies specific filter process.
    """

    def filter(self, qs, value):
        """
        FilterSet filter method

        Args:
            qs (self): FilterSet instance
            value (str): duration formatted string

        Returns:
            qs: queryset object
        """
        if value:
            ftr = [3600, 60, 1]
            duration_sec = sum(a * b for a, b in zip(ftr, map(int, value.split(":"))))
            lookup = self.lookup_expr or "exact"
            return qs.filter(**{f"{self.field_name}__{lookup}": duration_sec})
        return qs


class AttendanceOverTimeFilter(FilterSet):
    """
    Filter set class for AttendanceOverTime model

    Args:
        FilterSet (class): custom filter set class to apply styling
    """

    MONTH_CHOICES = [
        ("January", _("January")),
        ("February", _("February")),
        ("March", _("March")),
        ("April", _("April")),
        ("May", _("May")),
        ("June", _("June")),
        ("July", _("July")),
        ("August", _("August")),
        ("September", _("September")),
        ("October", _("October")),
        ("November", _("November")),
        ("December", _("December")),
    ]
    search = django_filters.CharFilter(method=filter_by_name)

    worked_hours__gte = DurationInSecondsFilter(
        field_name="hour_account_second", lookup_expr="gte"
    )
    worked_hours__lte = DurationInSecondsFilter(
        field_name="hour_account_second", lookup_expr="lte"
    )
    pending_hours__lte = DurationInSecondsFilter(
        field_name="hour_pending_second", lookup_expr="lte"
    )
    pending_hours__gte = DurationInSecondsFilter(
        field_name="hour_pending_second", lookup_expr="gte"
    )
    overtime__gte = DurationInSecondsFilter(
        field_name="overtime_second", lookup_expr="gte"
    )
    overtime__lte = DurationInSecondsFilter(
        field_name="overtime_second", lookup_expr="lte"
    )
    month = django_filters.ChoiceFilter(choices=MONTH_CHOICES, lookup_expr="icontains")
    department_name = django_filters.CharFilter(
        field_name="employee_id__employee_work_info__department_id__department",
        lookup_expr="icontains",
    )

    class Meta:
        """
        Meta class to add additional options
        """

        model = AttendanceOverTime
        fields = [
            "employee_id",
            "month",
            "overtime",
            "worked_hours",
            "year",
            "department_name",
            "employee_id__employee_work_info__department_id",
            "employee_id__employee_work_info__company_id",
            "employee_id__employee_work_info__job_position_id",
            "employee_id__employee_work_info__location",
            "employee_id__employee_work_info__reporting_manager_id",
            "employee_id__employee_work_info__shift_id",
            "employee_id__employee_work_info__work_type_id",
        ]

    def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
        super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
        for field in self.form.fields.keys():
            self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"


class LateComeEarlyOutFilter(FilterSet):
    """
    LateComeEarlyOutFilter class
    """

    search = django_filters.CharFilter(method=filter_by_name)
    employee_id = django_filters.ModelMultipleChoiceFilter(
        queryset=Employee.objects.all(),
        widget=forms.SelectMultiple(),
    )
    attendance_date__gte = django_filters.DateFilter(
        field_name="attendance_id__attendance_date",
        lookup_expr="gte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    attendance_date__lte = django_filters.DateFilter(
        field_name="attendance_id__attendance_date",
        lookup_expr="lte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    attendance_clock_in__lte = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="lte",
    )
    attendance_clock_in__gte = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="gte",
    )
    attendance_clock_out__gte = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="gte",
    )
    attendance_clock_out__lte = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="lte",
    )
    attendance_clock_in = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="exact",
    )
    attendance_clock_out = django_filters.TimeFilter(
        field_name="attendance_id__attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="exact",
    )
    attendance_date = django_filters.DateFilter(
        field_name="attendance_id__attendance_date",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    overtime_second__lte = DurationInSecondsFilter(
        field_name="attendance_id__overtime_second", lookup_expr="lte"
    )
    overtime_second__gte = DurationInSecondsFilter(
        field_name="attendance_id__overtime_second", lookup_expr="gte"
    )
    at_work_second__lte = DurationInSecondsFilter(
        field_name="attendance_id__at_work_second", lookup_expr="lte"
    )
    at_work_second__gte = DurationInSecondsFilter(
        field_name="attendance_id__at_work_second", lookup_expr="gte"
    )
    department = django_filters.CharFilter(
        field_name="employee_id__employee_work_info__department_id__department",
        lookup_expr="icontains",
    )
    year = django_filters.CharFilter(
        field_name="attendance_id__attendance_date", lookup_expr="year"
    )
    month = django_filters.CharFilter(
        field_name="attendance_id__attendance_date", lookup_expr="month"
    )
    week = django_filters.CharFilter(
        field_name="attendance_id__attendance_date", lookup_expr="week"
    )

    class Meta:
        """
        Meta class for additional options"""

        model = AttendanceLateComeEarlyOut
        fields = [
            "employee_id",
            "type",
            "attendance_id__minimum_hour",
            "attendance_id__attendance_worked_hour",
            "attendance_id__attendance_overtime_approve",
            "attendance_id__attendance_validated",
            "employee_id__employee_work_info__department_id",
            "employee_id__employee_work_info__company_id",
            "employee_id__employee_work_info__job_position_id",
            "employee_id__employee_work_info__location",
            "employee_id__employee_work_info__reporting_manager_id",
            "attendance_id__shift_id",
            "attendance_id__work_type_id",
            "attendance_date__gte",
            "attendance_date__lte",
            "attendance_clock_in__lte",
            "attendance_clock_in__gte",
            "attendance_clock_out__gte",
            "attendance_clock_out__lte",
            "attendance_clock_in",
            "attendance_clock_out",
            "attendance_date",
            "department",
            "year",
            "month",
            "week",
        ]

    def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
        super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
        for field in self.form.fields.keys():
            self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"


class AttendanceActivityFilter(FilterSet):
    """
    Filter set class for AttendanceActivity model

    Args:
        FilterSet (class): custom filter set class to apply styling
    """

    search = django_filters.CharFilter(method=filter_by_name)

    attendance_date = django_filters.DateFilter(
        field_name="attendance_date", widget=forms.DateInput(attrs={"type": "date"})
    )
    attendance_date_from = django_filters.DateFilter(
        field_name="attendance_date",
        lookup_expr="gte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    attendance_date_till = django_filters.DateFilter(
        field_name="attendance_date",
        lookup_expr="lte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    in_from = django_filters.DateFilter(
        field_name="clock_in",
        lookup_expr="gte",
        widget=forms.DateInput(attrs={"type": "time"}),
    )
    out_from = django_filters.DateFilter(
        field_name="clock_out",
        lookup_expr="gte",
        widget=forms.DateInput(attrs={"type": "time"}),
    )
    in_till = django_filters.DateFilter(
        field_name="clock_in",
        lookup_expr="lte",
        widget=forms.DateInput(attrs={"type": "time"}),
    )
    out_till = django_filters.DateFilter(
        field_name="clock_out",
        lookup_expr="lte",
        widget=forms.DateInput(attrs={"type": "time"}),
    )
    clock_in_date = django_filters.DateFilter(
        field_name="clock_in_date", widget=forms.DateInput(attrs={"type": "date"})
    )
    clock_out_date = django_filters.DateFilter(
        field_name="clock_out_date", widget=forms.DateInput(attrs={"type": "date"})
    )

    class Meta:
        """
        Meta class to add additional options
        """

        fields = [
            "employee_id",
            "attendance_date",
            "attendance_date_from",
            "attendance_date_till",
            "in_from",
            "in_till",
            "out_from",
            "shift_day",
            "out_till",
            "clock_in_date",
            "clock_out_date",
            "employee_id__employee_work_info__department_id",
            "employee_id__employee_work_info__company_id",
            "employee_id__employee_work_info__shift_id",
            "employee_id__employee_work_info__work_type_id",
            "employee_id__employee_work_info__job_position_id",
            "employee_id__employee_work_info__location",
            "employee_id__employee_work_info__reporting_manager_id",
        ]
        model = AttendanceActivity

    def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
        super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
        for field in self.form.fields.keys():
            self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"


class AttendanceFilters(FilterSet):
    """
    Filter set class for Attendance model

    Args:
        FilterSet (class): custom filter set class to apply styling
    """

    id = django_filters.NumberFilter(field_name="id")
    search = django_filters.CharFilter(method="filter_by_name")
    employee = django_filters.CharFilter(field_name="employee_id__id")
    date_attendance = django_filters.DateFilter(field_name="attendance_date")
    employee_id = django_filters.ModelMultipleChoiceFilter(
        queryset=Employee.objects.all(),
        widget=forms.SelectMultiple(),
    )

    attendance_date__gte = django_filters.DateFilter(
        field_name="attendance_date",
        lookup_expr="gte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    attendance_date__lte = django_filters.DateFilter(
        field_name="attendance_date",
        lookup_expr="lte",
        widget=forms.DateInput(attrs={"type": "date"}),
    )

    attendance_clock_in__lte = django_filters.TimeFilter(
        field_name="attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="lte",
    )
    attendance_clock_in__gte = django_filters.TimeFilter(
        field_name="attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="gte",
    )
    attendance_clock_out__gte = django_filters.TimeFilter(
        field_name="attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="gte",
    )
    attendance_clock_out__lte = django_filters.TimeFilter(
        field_name="attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
        lookup_expr="lte",
    )
    attendance_clock_in = django_filters.TimeFilter(
        field_name="attendance_clock_in",
        widget=forms.TimeInput(attrs={"type": "time"}),
    )
    attendance_clock_out = django_filters.TimeFilter(
        field_name="attendance_clock_out",
        widget=forms.TimeInput(attrs={"type": "time"}),
    )

    attendance_date = django_filters.DateFilter(
        widget=forms.DateInput(attrs={"type": "date"}),
    )
    pending_hour__lte = DurationInSecondsFilter(
        method="filter_pending_hour",
    )
    pending_hour__gte = DurationInSecondsFilter(
        method="filter_pending_hour",
    )
    at_work_second__lte = DurationInSecondsFilter(
        field_name="at_work_second", lookup_expr="lte"
    )
    at_work_second__gte = DurationInSecondsFilter(
        field_name="at_work_second", lookup_expr="gte"
    )
    overtime_second__lte = DurationInSecondsFilter(
        field_name="overtime_second", lookup_expr="lte"
    )
    overtime_second__gte = DurationInSecondsFilter(
        field_name="overtime_second", lookup_expr="gte"
    )
    year = django_filters.CharFilter(field_name="attendance_date", lookup_expr="year")
    month = django_filters.CharFilter(field_name="attendance_date", lookup_expr="month")
    week = django_filters.CharFilter(field_name="attendance_date", lookup_expr="week")
    department = django_filters.CharFilter(
        field_name="employee_id__employee_work_info__department_id__department",
        lookup_expr="icontains",
    )

    def filter_pending_hour(self, queryset, name, value):
        """
        This method calculates the pending hours for each attendance record in the
        queryset and filters the records based on whether the pending hours are less
        than or equal to (`pending_hour__lte`) or greater than the specified value.
        """
        if value is not None:
            value = strtime_seconds(value)
            filtered_attendance = []
            for attendance in queryset:
                minimum_hour_second = strtime_seconds(attendance.minimum_hour)
                worked_hour_second = attendance.at_work_second
                pending_hour_second = minimum_hour_second - worked_hour_second
                if name == "pending_hour__lte":
                    if value >= pending_hour_second:
                        filtered_attendance.append(attendance)
                else:
                    if value <= pending_hour_second:
                        filtered_attendance.append(attendance)
        return queryset.filter(
            id__in=[attendance.id for attendance in filtered_attendance]
        )

    class Meta:
        """
        Meta class to add additional options
        """

        model = Attendance
        fields = [
            "id",
            "employee_id",
            "employee_id__employee_work_info__department_id",
            "employee_id__employee_work_info__company_id",
            "employee_id__employee_work_info__job_position_id",
            "employee_id__employee_work_info__location",
            "employee_id__employee_work_info__reporting_manager_id",
            "attendance_day",
            "attendance_date",
            "work_type_id",
            "shift_id",
            "minimum_hour",
            "attendance_validated",
            "attendance_clock_in",
            "attendance_clock_out",
            "at_work_second",
            "overtime_second",
            "late_come_early_out__type",
            "attendance_overtime_approve",
            "attendance_validated",
            "is_validate_request",
            "is_validate_request_approved",
            "is_bulk_request",
            "at_work_second__lte",
            "at_work_second__gte",
            "overtime_second__lte",
            "overtime_second__gte",
            "overtime_second",
            "department",
            "month",
            "year",
        ]

        widgets = {
            "attendance_date": DateTimeInput(attrs={"type": "date"}),
        }

    def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
        super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
        for field in self.form.fields.keys():
            self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"

    def filter_by_name(self, queryset, name, value):
        # Call the imported function
        """
        This method allows filtering by the employee's first and/or last name or by other
        fields such as day, shift, work type, department, job position, or company, depending
        on the value of `search_field` provided in the request data.
        """
        filter_method = {
            "day": "attendance_day__day__icontains",
            "shift": "shift_id__employee_shift__icontains",
            "work_type": "work_type_id__work_type__icontains",
            "department": "employee_id__employee_work_info__department_id__department__icontains",
            "job_position": "employee_id__employee_work_info__\
                job_position_id__job_position__icontains",
            "company": "employee_id__employee_work_info__company_id__company__icontains",
        }
        search_field = self.data.get("search_field")
        if not search_field:
            parts = value.split()
            first_name = parts[0]
            last_name = " ".join(parts[1:]) if len(parts) > 1 else ""

            # Filter the queryset by first name and last name
            if first_name and last_name:
                queryset = queryset.filter(
                    employee_id__employee_first_name__icontains=first_name,
                    employee_id__employee_last_name__icontains=last_name,
                )
            elif first_name:
                queryset = queryset.filter(
                    employee_id__employee_first_name__icontains=first_name
                )
            elif last_name:
                queryset = queryset.filter(
                    employee_id__employee_last_name__icontains=last_name
                )
        else:
            filter = filter_method.get(search_field)
            queryset = queryset.filter(**{filter: value})

        return queryset


class LateComeEarlyOutReGroup:
    """
    Class to keep the field name for group by option
    """

    fields = [
        ("", "Select"),
        ("employee_id", "Employee"),
        ("type", "Type"),
        ("attendance_id__attendance_date", "Attendance Date"),
        ("attendance_id__shift_id", "Shift"),
        ("attendance_id__work_type_id", "Work Type"),
        ("attendance_id__minimum_hour", "Minimum Hour"),
        ("attendance_id__employee_id__country", "Country"),
        (
            "attendance_id__employee_id__employee_work_info__reporting_manager_id",
            "Reporting Manager",
        ),
        ("attendance_id__employee_id__employee_work_info__department_id", "Department"),
        (
            "attendance_id__employee_id__employee_work_info__job_position_id",
            "Job Position",
        ),
        (
            "attendance_id__employee_id__employee_work_info__employee_type_id",
            "Employment Type",
        ),
        ("attendance_id__employee_id__employee_work_info__company_id", "Company"),
    ]


class AttendanceReGroup:
    """
    Class to keep the field name for group by option
    """

    fields = [
        ("", "Select"),
        ("employee_id", "Employee"),
        ("attendance_date", "Attendance Date"),
        ("shift_id", "Shift"),
        ("work_type_id", "Work Type"),
        ("minimum_hour", "Minimum Hour"),
        ("employee_id__country", "Country"),
        ("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
        ("employee_id__employee_work_info__department_id", "Department"),
        ("employee_id__employee_work_info__job_position_id", "Job Position"),
        ("employee_id__employee_work_info__employee_type_id", "Employment Type"),
        ("employee_id__employee_work_info__company_id", "Company"),
    ]


class AttendanceOvertimeReGroup:
    """
    Class to keep the field name for group by option
    """

    fields = [
        ("", "Select"),
        ("employee_id", "Employee"),
        ("month", "Month"),
        ("year", "Year"),
        ("employee_id__country", "Country"),
        ("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
        ("employee_id__employee_work_info__shift_id", "Shift"),
        ("employee_id__employee_work_info__work_type_id", "Work Type"),
        ("employee_id__employee_work_info__department_id", "Department"),
        ("employee_id__employee_work_info__job_position_id", "Job Position"),
        ("employee_id__employee_work_info__employee_type_id", "Employment Type"),
        ("employee_id__employee_work_info__company_id", "Company"),
    ]


class AttendanceActivityReGroup:
    """
    Class to keep the field name for group by option
    """

    fields = [
        ("", "Select"),
        ("employee_id", "Employee"),
        ("attendance_date", "Attendance Date"),
        ("clock_in_date", "In Date"),
        ("clock_out_date", "Out Date"),
        ("shift_day", "Shift Day"),
        ("employee_id__country", "Country"),
        ("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
        ("employee_id__employee_work_info__shift_id", "Shift"),
        ("employee_id__employee_work_info__work_type_id", "Work Type"),
        ("employee_id__employee_work_info__department_id", "Department"),
        ("employee_id__employee_work_info__job_position_id", "Job Position"),
        ("employee_id__employee_work_info__employee_type_id", "Employment Type"),
        ("employee_id__employee_work_info__company_id", "Company"),
    ]


class AttendanceRequestReGroup:
    """
    Class to keep the field name for group by option
    """

    fields = [
        ("", "Select"),
        ("employee_id", "Employee"),
        ("attendance_day", "Attendance Date"),
        ("attendance_clock_in_date", "In Date"),
        ("attendance_clock_out_date", "Out Date"),
        ("employee_id__country", "Country"),
        ("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
        ("employee_id__employee_work_info__shift_id", "Shift"),
        ("employee_id__employee_work_info__work_type_id", "Work Type"),
        ("employee_id__employee_work_info__department_id", "Department"),
        ("employee_id__employee_work_info__job_position_id", "Job Position"),
        ("employee_id__employee_work_info__employee_type_id", "Employment Type"),
        ("employee_id__employee_work_info__company_id", "Company"),
    ]


def get_working_today(queryset, _name, value):
    today = datetime.datetime.now().date()
    yesterday = today - datetime.timedelta(days=1)

    working_employees = Attendance.objects.filter(
        attendance_date__gte=yesterday,
        attendance_date__lte=today,
        attendance_clock_out_date__isnull=True,
    ).values_list("employee_id", flat=True)

    if value:
        queryset = queryset.filter(id__in=working_employees)
    else:
        queryset = queryset.exclude(id__in=working_employees)
    return queryset


og_init = EmployeeFilter.__init__


def online_init(self, *args, **kwargs):
    og_init(self, *args, **kwargs)
    custom_field = django_filters.BooleanFilter(
        label="Working", method=get_working_today
    )
    self.filters["working_today"] = custom_field
    self.form.fields["working_today"] = custom_field.field
    self.form.fields["working_today"].widget.attrs.update(
        {
            "class": "oh-select oh-select-2 w-100",
        }
    )


EmployeeFilter.__init__ = online_init
