from decimal import Decimal

from django import forms
from django.core.exceptions import ValidationError
from .models import LeaveRequest, LeaveType


class LeaveApplicationForm(forms.Form):
    leave_type = forms.ModelChoiceField(queryset=LeaveType.objects.none())
    start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
    end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
    half_day = forms.BooleanField(required=False, help_text="Only for a single-day leave.")
    reason = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": 3}))

    def __init__(self, *args, employee=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.employee = employee
        if employee:
            self.fields["leave_type"].queryset = LeaveType.objects.filter(company=employee.company, is_active=True).order_by("name")

    def clean(self):
        cleaned = super().clean()
        if not self.employee:
            raise ValidationError("Employee profile is required.")
        leave_type = cleaned.get("leave_type")
        start = cleaned.get("start_date")
        end = cleaned.get("end_date")
        half_day = cleaned.get("half_day")
        if not start or not end or not leave_type:
            return cleaned
        if end < start:
            raise ValidationError("End date cannot be earlier than start date.")
        if start.year != end.year:
            raise ValidationError("Cross-year leave must be submitted as separate applications.")
        from apps.attendance.services import employee_in_service_on
        if not employee_in_service_on(self.employee, start) or not employee_in_service_on(self.employee, end):
            raise ValidationError("Leave dates must fall within the employee's employment service period.")
        if half_day:
            if start != end:
                raise ValidationError("Half-day leave is allowed only when start and end date are the same.")
            if not leave_type.half_day_allowed:
                raise ValidationError("The selected leave type does not allow half-day leave.")

        from .services import count_leave_days
        days = count_leave_days(self.employee, start, end, half_day=half_day)
        if days <= 0:
            raise ValidationError("The selected date range has no payable working day according to the current weekly holiday/holiday setup.")
        cleaned["calculated_days"] = days

        overlap = LeaveRequest.objects.filter(
            employee=self.employee,
            status__in=["PENDING", "UNDER_REVIEW", "APPROVED"],
            start_date__lte=end,
            end_date__gte=start,
        )
        if overlap.exists():
            raise ValidationError("Another pending/approved leave request overlaps this date range.")
        return cleaned


class LeaveCancellationForm(forms.Form):
    reason = forms.CharField(widget=forms.Textarea(attrs={"rows": 3}), min_length=5)


class LeaveYearInitializeForm(forms.Form):
    company = forms.ModelChoiceField(queryset=None)
    year = forms.IntegerField(min_value=2020, max_value=2100)

    def __init__(self, *args, company_queryset=None, **kwargs):
        super().__init__(*args, **kwargs)
        from apps.organization.models import Company
        self.fields["company"].queryset = company_queryset if company_queryset is not None else Company.objects.none()


class ManagedLeaveApplicationForm(forms.Form):
    employee = forms.ModelChoiceField(queryset=None)
    leave_type = forms.ModelChoiceField(queryset=LeaveType.objects.none())
    start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
    end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
    half_day = forms.BooleanField(required=False, help_text="Only for a single-day leave.")
    reason = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": 3}))

    def __init__(self, *args, employee_queryset=None, **kwargs):
        super().__init__(*args, **kwargs)
        from apps.employees.models import Employee
        self.fields["employee"].queryset = employee_queryset if employee_queryset is not None else Employee.objects.none()
        employee_id = self.data.get("employee") if self.is_bound else None
        if employee_id:
            employee = self.fields["employee"].queryset.filter(pk=employee_id).first()
            if employee:
                self.fields["leave_type"].queryset = LeaveType.objects.filter(company=employee.company, is_active=True).order_by("name")
        else:
            company_ids = self.fields["employee"].queryset.values_list("company_id", flat=True).distinct()
            self.fields["leave_type"].queryset = LeaveType.objects.filter(company_id__in=company_ids, is_active=True).order_by("company_id", "name")

    def clean(self):
        cleaned = super().clean()
        employee = cleaned.get("employee")
        leave_type = cleaned.get("leave_type")
        start = cleaned.get("start_date")
        end = cleaned.get("end_date")
        half_day = cleaned.get("half_day")
        if not employee or not leave_type or not start or not end:
            return cleaned
        if leave_type.company_id != employee.company_id:
            self.add_error("leave_type", "Selected leave type does not belong to the employee company.")
            return cleaned
        if end < start:
            raise ValidationError("End date cannot be earlier than start date.")
        if start.year != end.year:
            raise ValidationError("Cross-year leave must be submitted as separate applications.")
        from apps.attendance.services import employee_in_service_on
        if not employee_in_service_on(employee, start) or not employee_in_service_on(employee, end):
            raise ValidationError("Leave dates must fall within the employee's employment service period.")
        if half_day:
            if start != end:
                raise ValidationError("Half-day leave is allowed only when start and end date are the same.")
            if not leave_type.half_day_allowed:
                raise ValidationError("The selected leave type does not allow half-day leave.")
        from .services import count_leave_days
        days = count_leave_days(employee, start, end, half_day=half_day)
        if days <= 0:
            raise ValidationError("The selected date range has no payable working day according to the current weekly holiday/holiday setup.")
        overlap = LeaveRequest.objects.filter(employee=employee, status__in=["PENDING", "UNDER_REVIEW", "APPROVED"], start_date__lte=end, end_date__gte=start)
        if overlap.exists():
            raise ValidationError("Another pending/approved leave request overlaps this date range.")
        cleaned["calculated_days"] = days
        return cleaned
