from django import forms
from django.core.exceptions import ValidationError
from django.utils import timezone
from datetime import datetime, timedelta
from .models import OvertimeRequest

class OvertimeRequestForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.employee = kwargs.pop('employee', None)
        super().__init__(*args, **kwargs)
        if self.employee:
            self.instance.employee = self.employee
        
        # Get current month's first and last day
        today = timezone.now().date()
        current_month_first = today.replace(day=1)
        
        # Allow submissions for current month and previous month
        # Calculate previous month's first day
        if today.month == 1:
            prev_month_first = today.replace(year=today.year - 1, month=12, day=1)
        else:
            prev_month_first = today.replace(month=today.month - 1, day=1)
        
        # Set date field widget attributes - allow previous month to current date
        self.fields['date'].widget = forms.DateInput(
            attrs={
                'type': 'date',
                'class': 'form-control',
                'min': prev_month_first.isoformat(),
                'max': today.isoformat()
            }
        )

    date = forms.DateField(
        help_text="Select a date from the current or previous month"
    )
    start_time = forms.TimeField(
        widget=forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
        help_text="Enter the time you started work (after 5:00 PM)"
    )
    end_time = forms.TimeField(
        widget=forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
        help_text="Enter the time you finished work (can be next day for overnight work)"
    )
    reason = forms.CharField(
        widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
        help_text="Provide a reason for the overtime request",
        required=False
    )

    class Meta:
        model = OvertimeRequest
        fields = ['date', 'start_time', 'end_time', 'reason', 'supporting_document']
        help_texts = {
            'date': 'Select the date of overtime',
            'start_time': 'Enter the time you started work',
            'end_time': 'Enter the time you finished work',
            'reason': 'Provide a reason for the overtime request',
            'supporting_document': 'Upload any supporting documentation (PDF, images)',
        }

    def clean(self):
        cleaned_data = super().clean()
        date = cleaned_data.get('date')
        start_time = cleaned_data.get('start_time')
        end_time = cleaned_data.get('end_time')

        if not self.employee:
            raise ValidationError("Employee is required")

        if not all([date, start_time, end_time]):
            return cleaned_data  # Let the field validators handle required fields
            


        # All required fields are present, proceed with validation
        current_date = timezone.now().date()
        
        # Check if date is not in the future
        if date > current_date:
            raise ValidationError({"date": "Cannot request overtime for future dates"})
        
        # Allow current month and previous month, but warn for previous month
        current_month_first = current_date.replace(day=1)
        if date.month == current_date.month and date.year == current_date.year:
            # Current month - no additional validation needed
            pass
        elif date < current_month_first:
            # Previous month or earlier - check if it's within allowed range
            if current_date.month == 1:
                prev_month_first = current_date.replace(year=current_date.year - 1, month=12, day=1)
            else:
                prev_month_first = current_date.replace(month=current_date.month - 1, day=1)
            
            if date < prev_month_first:
                raise ValidationError({"date": "Overtime can only be requested for the current month or previous month"})
            
            # Add a warning for previous month submissions (but don't block them)
            # This will be handled by HR/Registrar validation
        else:
            # Future month
            raise ValidationError({"date": "Cannot request overtime for future dates"})

        # Handle overnight work - if end time is before start time, assume it's next day
        # Only validate that they're not equal (which would be 0 hours)
        if end_time == start_time:
            raise ValidationError({"end_time": "End time cannot be the same as start time"})
        
        # For overnight work, end_time < start_time is allowed (next day)
        # The model will handle the calculation correctly

        # Check if start time is after regular work hours (5:00 PM) only for regular weekdays (not holidays)
        # First check if the date is a holiday regardless of weekday
        from overtime.models import Holiday
        is_holiday = Holiday.is_holiday(date, self.employee.company)
        
        # If it's a holiday, skip the weekday validation entirely
        if is_holiday:
            return cleaned_data
            
        # Only apply the 5:00 PM rule for regular weekdays that are not holidays
        if date.weekday() < 5:  # Monday-Friday
            # For regular weekdays, require start time after 5 PM
            if start_time.hour < 17:
                raise ValidationError({"start_time": "Start time must be after regular work hours (5:00 PM)"})
    
        # Check for overlapping requests
        overlapping = OvertimeRequest.objects.filter(
            employee=self.employee,
            date=date,
            status__in=['PENDING', 'HR_APPROVED', 'REG_APPROVED']
        ).exclude(pk=self.instance.pk if self.instance else None)

        if overlapping.exists():
            raise ValidationError("You already have an active overtime request for this date")

        return cleaned_data

    def save(self, commit=True):
        instance = super().save(commit=False)
        if self.employee:
            instance.employee = self.employee
        if commit:
            instance.save()
        return instance

