from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.utils import timezone
from django.http import HttpResponseForbidden, JsonResponse
from django.urls import reverse
from django.db.models import Q, Sum, F, Count, Avg
from django.core.exceptions import ValidationError
from datetime import datetime
from decimal import Decimal
from accounts.models import Employee
from django.views.decorators.http import require_POST
from .models import OvertimeRequest, OvertimeRequestHistory
from .forms import OvertimeRequestForm
from notifications.utils import (
    notify_overtime_submission, notify_overtime_hr_approval, notify_overtime_hr_rejection,
    notify_overtime_registrar_approval, notify_overtime_registrar_rejection,
    notify_overtime_payment_processed
)

# Create your views here.

@login_required
def overtime_history(request):
    """View for finance to see processed overtime payments history"""
    if request.user.employee.role != 'FIN':
        messages.error(request, 'You do not have permission to access this page.')
        return redirect('home')
    
    # Get all processed overtime requests with employee stats
    processed_requests = OvertimeRequest.objects.filter(
        status='PROCESSED',
        payment_status='PAID'
    ).select_related(
        'employee__user',
        'finance_processed_by__user'
    ).order_by('-finance_processed_at')
    
    # Get employee statistics
    employee_stats = OvertimeRequest.objects.filter(
        status='PROCESSED',
        payment_status='PAID'
    ).values('employee').annotate(
        total_requests=Count('id'),
        total_amount=Sum('amount')
    )
    
    # Calculate overall statistics
    stats = {
        'total_payments': processed_requests.count(),
        'total_amount': processed_requests.aggregate(Sum('amount'))['amount__sum'] or 0,
        'total_hours': sum(request.total_hours for request in processed_requests),
        'avg_amount': processed_requests.aggregate(Avg=Sum('amount')/Count('id'))['Avg'] or 0
    }
    
    context = {
        'title': 'Overtime Payment History',
        'processed_requests': processed_requests,
        'stats': stats
    }
    return render(request, 'overtime/overtime_history.html', context)

@login_required
def finance_overtime(request):
    """View for finance to process overtime payments"""
    if request.user.employee.role != 'FIN':
        messages.error(request, 'You do not have permission to access this page.')
        return redirect('home')
    
    employee_id = request.GET.get('employee_id')
    month = int(request.GET.get('month', timezone.now().month))
    year = int(request.GET.get('year', timezone.now().year))
    
    # Base query for approved overtime requests that haven't been processed
    base_query = OvertimeRequest.objects.filter(
        status='REG_APPROVED',
        payment_status='PENDING'
    ).select_related('employee__user')
    
    # Filter by month and year
    filtered_requests = base_query.filter(
        date__month=month,
        date__year=year
    )
    
    if employee_id:
        # If employee_id is provided, show details for that employee
        employee = get_object_or_404(Employee, id=employee_id)
        overtime_requests = filtered_requests.filter(employee_id=employee_id)
        
        # Calculate total amount for this employee
        employee_total = overtime_requests.aggregate(
            total=Sum('amount')
        )['total'] or 0
        
        context = {
            'title': f'Overtime Details for {employee.user.get_full_name()}',
            'employee': employee,
            'overtime_requests': overtime_requests,
            'total_amount': employee_total,
            'selected_month': month,
            'selected_year': year,
            'show_employee_details': True
        }
    else:
        # Group overtime requests by employee
        employees_with_overtime = {}
        
        for overtime_req in filtered_requests:
            employee = overtime_req.employee
            if employee.id not in employees_with_overtime:
                employees_with_overtime[employee.id] = {
                    'employee': employee,
                    'request_count': 0,
                    'total_hours': Decimal('0.00'),
                    'total_amount': Decimal('0.00')
                }
            
            employees_with_overtime[employee.id]['request_count'] += 1
            employees_with_overtime[employee.id]['total_hours'] += Decimal(str(overtime_req.total_hours))
            employees_with_overtime[employee.id]['total_amount'] += overtime_req.amount or Decimal('0.00')
        
        # Calculate overall total amount
        total_amount = filtered_requests.aggregate(
            total=Sum('amount')
        )['total'] or 0
        
        context = {
            'title': 'Approved Overtime Reports',
            'employees_with_overtime': employees_with_overtime.values(),
            'total_amount': total_amount,
            'selected_month': month,
            'selected_year': year,
            'show_employee_details': False
        }
    
    return render(request, 'overtime/finance_overtime.html', context)

@login_required
@require_POST
def process_payment(request):
    """Process overtime payment"""
    if request.user.employee.role != 'FIN':
        return JsonResponse({'success': False, 'message': 'Permission denied'})
    
    request_id = request.POST.get('request_id')
    payment_date = request.POST.get('payment_date')
    comment = request.POST.get('comment')
    
    try:
        overtime_request = OvertimeRequest.objects.get(id=request_id)
        
        # Update payment status
        overtime_request.status = 'PROCESSED'
        overtime_request.payment_status = 'PAID'
        overtime_request.payment_date = payment_date
        overtime_request.finance_processed_by = request.user.employee
        overtime_request.finance_processed_at = timezone.now()
        overtime_request.finance_comment = comment
        overtime_request.save()
        
        # Send payment processed notification
        notify_overtime_payment_processed(overtime_request, request=request)
        
        # Create history entry
        OvertimeRequestHistory.objects.create(
            request=overtime_request,
            status='PAYMENT_PROCESSED',
            comment=comment,
            actor=request.user.employee
        )
        
        return JsonResponse({'success': True})
    except OvertimeRequest.DoesNotExist:
        return JsonResponse({'success': False, 'message': 'Request not found'})
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)})


def process_bulk_payments(request):
    """Process multiple overtime payments at once"""
    if request.user.employee.role != 'FIN':
        return JsonResponse({'success': False, 'message': 'Permission denied'})
    
    request_ids = request.POST.getlist('request_ids[]')
    payment_date = request.POST.get('payment_date')
    comment = request.POST.get('comment')
    
    if not request_ids:
        return JsonResponse({'success': False, 'message': 'No requests selected'})
    
    try:
        processed_count = 0
        for request_id in request_ids:
            try:
                overtime_request = OvertimeRequest.objects.get(id=request_id)
                
                # Skip already processed requests
                if overtime_request.payment_status == 'PAID':
                    continue
                
                # Update payment status
                overtime_request.status = 'PROCESSED'
                overtime_request.payment_status = 'PAID'
                overtime_request.payment_date = payment_date
                overtime_request.finance_processed_by = request.user.employee
                overtime_request.finance_processed_at = timezone.now()
                overtime_request.finance_comment = comment
                overtime_request.save()
                
                # Create history entry
                OvertimeRequestHistory.objects.create(
                    request=overtime_request,
                    status='PAYMENT_PROCESSED',
                    comment=comment,
                    actor=request.user.employee
                )
                
                processed_count += 1
            except OvertimeRequest.DoesNotExist:
                continue  # Skip if request doesn't exist
        
        return JsonResponse({
            'success': True, 
            'message': f'Successfully processed {processed_count} payment(s)'
        })
    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)})

import logging

logger = logging.getLogger(__name__)

@login_required
def request_overtime(request):
    """Handle overtime request creation for employees."""
    logger.info(f'Overtime request received. Method: {request.method}, Is AJAX: {request.headers.get("X-Requested-With") == "XMLHttpRequest"}')
    
    # Only Registrar cannot submit overtime requests
    if request.user.employee.role == 'REG':
        if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
            return JsonResponse({'error': 'Registrar cannot submit overtime requests.'}, status=403)
        messages.error(request, 'Registrar cannot submit overtime requests.')
        return redirect('home')

    if request.method == 'POST':
        form = OvertimeRequestForm(request.POST, request.FILES, employee=request.user.employee)
        
        # Add more descriptive error messages for mobile validation
        if 'date' in form.data and form.data['date']:
            try:
                date_value = datetime.strptime(form.data['date'], '%Y-%m-%d').date()
                current_date = timezone.now().date()
                
                # Only check if date is in the future - allow current and previous month
                if date_value > current_date:
                    form.add_error('date', 'Cannot request overtime for future dates')
            except (ValueError, TypeError):
                # Let the form's built-in validation handle invalid date formats
                pass
        
        # Debug form validation
        logger.info(f'Form data received: {form.data}')
        logger.info(f'Form is valid: {form.is_valid()}')
        if not form.is_valid():
            logger.error(f'Form errors: {form.errors}')
            logger.error(f'Form non-field errors: {form.non_field_errors()}')
        
        if form.is_valid():
            try:
                # Check for duplicate requests
                date = form.cleaned_data['date']
                start_time = form.cleaned_data['start_time']
                end_time = form.cleaned_data['end_time']
                existing_request = OvertimeRequest.objects.filter(
                    employee=request.user.employee,
                    date=date,
                    start_time=start_time,
                    end_time=end_time
                ).exists()
                
                if existing_request:
                    error_msg = 'An overtime request for this date and time range already exists.'
                    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                        return JsonResponse({
                            'success': False,
                            'error': error_msg,
                        }, status=400)
                    form.add_error(None, error_msg)
                    return render(request, 'overtime/request_form.html', {'form': form})
                
                overtime = form.save(commit=False)
                overtime.employee = request.user.employee
                
                # If HR, auto-approve their own request
                if request.user.employee.role == 'HR':
                    overtime.status = 'HR_APPROVED'
                    overtime.hr_approved_by = request.user.employee
                    overtime.hr_approved_at = timezone.now()
                    overtime.hr_comment = 'Auto-approved (HR request)'
                
                # Check if it's a weekend
                is_weekend = overtime.date.weekday() >= 5
                logger.info(f'Request date: {overtime.date}, Is weekend: {is_weekend}')
                
                overtime.full_clean()  # Run model validation
                overtime.save()  # Save only once at the end
                logger.info(f'Overtime request saved with ID: {overtime.id}')
                
                # Send notification email
                notify_overtime_submission(overtime, request=request)
                
                if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                    response_data = {
                        'success': True,
                        'message': 'Overtime request submitted successfully.',
                        'view_url': reverse('overtime:view_request', args=[overtime.id])
                    }
                    logger.info(f'Sending success response: {response_data}')
                    return JsonResponse(response_data)
                
                messages.success(request, 'Overtime request submitted successfully.')
                return redirect('home')
            except ValidationError as e:
                error_msg = str(e)
                if isinstance(e.message_dict.get('__all__', [None])[0], str):
                    error_msg = e.message_dict['__all__'][0]
                
                logger.error(f'Validation error: {error_msg}')
                if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                    return JsonResponse({
                        'success': False,
                        'error': error_msg,
                    }, status=400)
                form.add_error(None, error_msg)

        
        if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
            errors = {}
            for field, error_list in form.errors.items():
                errors[field] = [str(error) for error in error_list]
            
            # Add a more descriptive general error message for mobile users
            if 'date' in errors and any('current month or previous month' in err for err in errors['date']):
                errors['__all__'] = ['Please select a date within the current month or previous month. Overtime cannot be requested for dates older than the previous month.']
            elif 'date' in errors and any('future' in err for err in errors['date']):
                errors['__all__'] = ['Please select a date up to today. Overtime cannot be requested for future dates.']
                
            logger.info(f'Sending validation errors: {errors}')
            return JsonResponse({
                'success': False,
                'errors': errors,
            }, status=400)
    else:
        form = OvertimeRequestForm(employee=request.user.employee)

    return render(request, 'overtime/request_form.html', {'form': form})

@login_required
def approve_overtime(request):
    """Handle overtime approvals for HR and Registrar."""
    employee = request.user.employee
    
    # Only HR and Registrar can approve requests
    if employee.role not in ['HR', 'REG']:
        messages.error(request, 'You do not have permission to approve requests.')
        return redirect('home')

    if employee.role == 'HR':
        # HR sees pending requests
        status_filter = 'PENDING'
    else:
        # Registrar sees HR approved requests
        status_filter = 'HR_APPROVED'
        
    # Get all requests with the appropriate status
    all_requests = OvertimeRequest.objects.filter(status=status_filter)
    
    # Get counts of requests per employee
    from django.db.models import Count
    employees_with_counts = Employee.objects.filter(
        overtimerequest__status=status_filter
    ).annotate(request_count=Count('overtimerequest'))
    
    # Create a dictionary of employee IDs to request counts
    employee_counts = {e.id: e.request_count for e in employees_with_counts}
    
    # Group requests by employee and get the most recent request for each employee
    employee_requests = {}
    for req in all_requests:
        employee_id = req.employee.id
        if employee_id not in employee_requests or req.date > employee_requests[employee_id].date:
            employee_requests[employee_id] = req
            # Add the count to the request object
            req.request_count = employee_counts.get(employee_id, 0)
    
    # Convert to list and sort by date (most recent first)
    requests = sorted(employee_requests.values(), key=lambda x: x.date, reverse=True)

    if request.method == 'POST':
        request_id = request.POST.get('request_id')
        action = request.POST.get('action')
        comment = request.POST.get('comment', '')
        
        if request_id and action:
            overtime_request = get_object_or_404(OvertimeRequest, id=request_id)
            
            new_status = None
            now = timezone.now()
            if employee.role == 'HR':
                if action == 'approve':
                    new_status = 'HR_APPROVED'
                    overtime_request.status = new_status
                    overtime_request.hr_approved_by = employee
                    overtime_request.hr_approved_at = now
                    overtime_request.hr_comment = comment
                    messages.success(request, 'Request approved and sent to Registrar.')
                    # Send notification email
                    notify_overtime_hr_approval(overtime_request, request=request)
                else:
                    new_status = 'HR_REJECTED'
                    overtime_request.status = new_status
                    overtime_request.hr_approved_by = employee
                    overtime_request.hr_approved_at = now
                    overtime_request.hr_comment = comment
                    messages.warning(request, 'Request rejected.')
                    # Send notification email
                    notify_overtime_hr_rejection(overtime_request, request=request)
            
            elif employee.role == 'REG':
                if action == 'approve':
                    new_status = 'REG_APPROVED'
                    overtime_request.status = new_status
                    overtime_request.registrar_approved_by = employee
                    overtime_request.registrar_approved_at = now
                    overtime_request.registrar_comment = comment
                    messages.success(request, 'Request approved for payment processing.')
                    # Send notification email
                    notify_overtime_registrar_approval(overtime_request, request=request)
                else:
                    new_status = 'REG_REJECTED'
                    overtime_request.status = new_status
                    overtime_request.registrar_approved_by = employee
                    overtime_request.registrar_approved_at = now
                    overtime_request.registrar_comment = comment
                    messages.warning(request, 'Request rejected.')
                    # Send notification email
                    notify_overtime_registrar_rejection(overtime_request, request=request)
            
            try:
                overtime_request.save()
                
                # Create history record
                OvertimeRequestHistory.objects.create(
                    request=overtime_request,
                    status=new_status,
                    comment=comment,
                    actor=employee
                )
                return redirect(reverse('overtime:approve'))
            except ValidationError as e:
                # Handle validation errors gracefully (e.g., for past month requests)
                error_msg = str(e)
                if hasattr(e, 'message_dict') and e.message_dict:
                    error_msg = '; '.join([f"{field}: {', '.join(errors)}" for field, errors in e.message_dict.items()])
                messages.error(request, f'Error processing request: {error_msg}. Please review the overtime details and try again.')
                return redirect(reverse('overtime:approve'))
            except Exception as e:
                # Handle any other unexpected errors
                messages.error(request, f'An unexpected error occurred: {str(e)}. Please contact system administrator.')
                return redirect(reverse('overtime:approve'))

    context = {
        'requests': requests,
        'role': employee.role,
        'title': 'Pending Overtime Requests' if employee.role == 'HR' else 'HR Approved Requests'
    }
    return render(request, 'overtime/approvals.html', context)

@login_required
def view_request(request, request_id):
    overtime_request = get_object_or_404(OvertimeRequest, id=request_id)

    # Check if the user has permission to view this request
    if request.user.employee != overtime_request.employee and not any([
        request.user.employee.role == 'HR',
        request.user.employee.role == 'REG',
        request.user.employee.role == 'FIN'
    ]):
        messages.error(request, "You don't have permission to view this request.")
        return redirect('home')
    
    context = {
        'request': overtime_request,
        'title': f'Overtime Request - {overtime_request.date}',
    }
    return render(request, 'overtime/view_request.html', context)

@login_required
def edit_request(request, request_id):
    """Allow employees to edit their rejected overtime requests."""
    overtime_request = get_object_or_404(OvertimeRequest, id=request_id)
    
    # Check if user owns this request and it's rejected
    if request.user.employee != overtime_request.employee:
        messages.error(request, "You can only edit your own requests.")
        return redirect('home')
    
    if overtime_request.status not in ['HR_REJECTED', 'REG_REJECTED']:
        messages.error(request, "You can only edit rejected requests.")
        return redirect('home')
    
    if request.method == 'POST':
        form = OvertimeRequestForm(request.POST, request.FILES, instance=overtime_request, employee=request.user.employee)
        if form.is_valid():
            overtime_request = form.save(commit=False)
            overtime_request.employee = request.user.employee  # Ensure employee is set
            overtime_request.status = 'PENDING'  # Reset to pending
            overtime_request.hr_approved_at = None
            overtime_request.hr_approved_by = None
            overtime_request.hr_comment = ''  # Set empty string instead of None
            overtime_request.registrar_approved_at = None
            overtime_request.registrar_approved_by = None
            overtime_request.registrar_comment = ''  # Set empty string instead of None
            overtime_request.save()
            
            # Send notification email for resubmission if it was rejected before
            if form.initial.get('status') in ['HR_REJECTED', 'REG_REJECTED']:
                from notifications.utils import notify_overtime_submission
                notify_overtime_submission(overtime_request, request=request)
                messages.success(request, 'Overtime request updated successfully and resubmitted.')
            else:
                messages.success(request, 'Overtime request updated successfully.')
            return redirect('overtime:view_request', request_id=overtime_request.id)
    else:
        form = OvertimeRequestForm(instance=overtime_request, employee=request.user.employee)
    
    context = {
        'form': form,
        'title': 'Edit Overtime Request',
        'overtime_request': overtime_request
    }
    return render(request, 'overtime/request_form.html', context)

@login_required
def delete_request(request, request_id):
    overtime_request = get_object_or_404(OvertimeRequest, id=request_id)
    
    # Only allow deletion of own requests that are pending or rejected
    if request.user.employee != overtime_request.employee:
        messages.error(request, "You don't have permission to delete this request.")
        return redirect('home')
    
    if overtime_request.status not in ['PENDING', 'HR_REJECTED', 'REG_REJECTED']:
        messages.error(request, "Only pending or rejected requests can be deleted.")
        return redirect('overtime:view_request', request_id=overtime_request.id)
    
    overtime_request.delete()
    messages.success(request, 'Overtime request deleted successfully.')
    return redirect('overtime:my_requests')

@login_required
def my_requests(request):
    """View for users to see their own overtime requests"""
    # Get all overtime requests for the current user
    requests = OvertimeRequest.objects.filter(
        employee=request.user.employee
    ).select_related(
        'employee__user',
        'hr_approved_by__user',
        'registrar_approved_by__user',
        'finance_processed_by__user'
    ).order_by('-date', '-created_at')
    
    # Calculate statistics
    stats = {
        'total_requests': requests.count(),
        'pending_requests': requests.filter(status='PENDING').count(),
        'approved_requests': requests.filter(status='REG_APPROVED').count(),
        'rejected_requests': requests.filter(status__in=['HR_REJECTED', 'REG_REJECTED']).count(),
        'processed_requests': requests.filter(status='PROCESSED').count(),
        'total_amount': requests.filter(status='PROCESSED').aggregate(Sum('amount'))['amount__sum'] or 0,
        'total_hours': sum(request.total_hours for request in requests.filter(status='PROCESSED'))
    }
    
    context = {
        'title': 'My Overtime Requests',
        'requests': requests,
        'stats': stats
    }
    return render(request, 'overtime/my_requests.html', context)


@login_required
def overtime_calculator(request):
    """Simple overtime calculator for finance"""
    # Allow all authenticated users to access the calculator
    # No role restriction for this standalone tool
    
    context = {
        'title': 'Overtime Calculator',
        'current_date': timezone.now().date().isoformat()
    }
    return render(request, 'overtime/calculator.html', context)


@login_required
def employee_overtime_requests(request, employee_id):
    """View and approve/reject all overtime requests from a specific employee."""
    current_employee = request.user.employee
    
    # Only HR and Registrar can access this view
    if current_employee.role not in ['HR', 'REG']:
        messages.error(request, 'You do not have permission to access this page.')
        return redirect('home')
    
    # Get the employee whose requests we're viewing
    employee = get_object_or_404(Employee, id=employee_id)
    
    # Get the appropriate requests based on role
    if current_employee.role == 'HR':
        # HR sees pending requests
        requests = OvertimeRequest.objects.filter(
            employee=employee,
            status='PENDING'
        ).order_by('-date')
    else:
        # Registrar sees HR approved requests
        requests = OvertimeRequest.objects.filter(
            employee=employee,
            status='HR_APPROVED'
        ).order_by('-date')
    
    # Handle bulk approval/rejection
    if request.method == 'POST':
        action = request.POST.get('action')
        request_ids = request.POST.getlist('request_ids')
        comment = request.POST.get('comment', '')
        
        if action and request_ids:
            # Determine the new status based on role and action
            new_status = None
            if current_employee.role == 'HR':
                if action == 'approve':
                    new_status = 'HR_APPROVED'
                elif action == 'reject':
                    new_status = 'HR_REJECTED'
            else:  # Registrar
                if action == 'approve':
                    new_status = 'REG_APPROVED'
                elif action == 'reject':
                    new_status = 'REG_REJECTED'
            
            if new_status:
                now = timezone.now()
                updated_count = 0
                
                for request_id in request_ids:
                    try:
                        overtime_request = OvertimeRequest.objects.get(id=request_id)
                        
                        # Update the request status
                        overtime_request.status = new_status
                        
                        # Update approval fields based on role
                        if current_employee.role == 'HR':
                            overtime_request.hr_approved_by = current_employee
                            overtime_request.hr_approved_at = now
                            overtime_request.hr_comment = comment
                        else:  # Registrar
                            overtime_request.registrar_approved_by = current_employee
                            overtime_request.registrar_approved_at = now
                            overtime_request.registrar_comment = comment
                            
                            # Calculate payment amount when Registrar approves
                            if new_status == 'REG_APPROVED':
                                overtime_request.calculate_amount()
                        
                        overtime_request.save()
                        
                        # Create history record
                        OvertimeRequestHistory.objects.create(
                            request=overtime_request,
                            status=new_status,
                            comment=comment,
                            actor=current_employee
                        )
                        
                        updated_count += 1
                    except OvertimeRequest.DoesNotExist:
                        continue
                
                action_text = 'approved' if 'APPROVED' in new_status else 'rejected'
                messages.success(
                    request, 
                    f'Successfully {action_text} {updated_count} overtime requests for {employee.user.get_full_name()}'
                )
                return redirect('overtime:approve')
    
    return render(request, 'overtime/employee_requests.html', {
        'page_title': f'Overtime Requests - {employee.user.get_full_name()}',
        'employee': employee,
        'requests': requests,
        'role': current_employee.role,
    })