from django.test import TestCase, TransactionTestCase, Client
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.urls import reverse
from django.utils import timezone
from datetime import date, time, datetime, timedelta
from decimal import Decimal
from accounts.models import Employee
from .models import OvertimeRequest
from .forms import OvertimeRequestForm

class OvertimeRequestFormTests(TestCase):
    def setUp(self):
        # Create employee user (Employee will be auto-created)
        self.emp_user = User.objects.create_user(
            username='employee1',
            password='testpass123',
            first_name='John',
            last_name='Doe'
        )
        
        # Update the auto-created employee
        self.employee = self.emp_user.employee
        self.employee.basic_pay = Decimal('5000.00')
        self.employee.save()
        
        # Create HR user
        self.hr_user = User.objects.create_user(
            username='hr1',
            password='testpass123',
            first_name='HR',
            last_name='User'
        )
        self.hr_employee = self.hr_user.employee
        self.hr_employee.role = 'HR'
        self.hr_employee.basic_pay = Decimal('6000.00')
        self.hr_employee.save()
        
        # Create client
        self.client = Client()

    def test_form_validation(self):
        """Test form validation"""
        # Find a weekday in the current month
        today = timezone.now().date()
        test_date = today
        while test_date.weekday() >= 5:  # Skip weekends
            test_date = test_date - timedelta(days=1)

        # Test valid form data for current month weekday
        form_data = {
            'date': test_date.strftime('%Y-%m-%d'),
            'start_time': '17:00',  # 5:00 PM
            'end_time': '20:00',    # 8:00 PM
            'reason': 'Test overtime'
        }
        form = OvertimeRequestForm(data=form_data, employee=self.employee)
        self.assertTrue(form.is_valid())
        
        # Test invalid date (next month)
        next_month = today.replace(day=1) + timedelta(days=32)
        form_data['date'] = next_month.strftime('%Y-%m-%d')
        form = OvertimeRequestForm(data=form_data, employee=self.employee)
        self.assertFalse(form.is_valid())
        self.assertIn('Overtime can only be requested for the current month', str(form.errors['date']))

        # Test invalid start time (before 5 PM on weekday)
        form_data['date'] = test_date.strftime('%Y-%m-%d')
        form_data['start_time'] = '16:00'  # 4:00 PM
        form = OvertimeRequestForm(data=form_data, employee=self.employee)
        self.assertFalse(form.is_valid())
        self.assertIn('Start time must be after regular work hours (5:00 PM)', str(form.errors['start_time']))
    
    def test_view_as_hr(self):
        """Test overtime request view as HR"""
        self.client.login(username='hr1', password='testpass123')
        
        # Test GET request
        response = self.client.get(reverse('overtime:request'))
        self.assertEqual(response.status_code, 200)
        
        # Test POST request
        today = datetime.now().date()
        form_data = {
            'date': today.strftime('%Y-%m-%d'),
            'start_time': '17:00',
            'end_time': '20:00',
            'reason': 'Test overtime'
        }
        response = self.client.post(reverse('overtime:request'), form_data)
        self.assertEqual(response.status_code, 302)  # Should redirect on success
        
        # Verify request was created and auto-approved
        overtime = OvertimeRequest.objects.first()
        self.assertIsNotNone(overtime)
        self.assertEqual(overtime.employee, self.hr_employee)
        self.assertEqual(overtime.status, 'HR_APPROVED')
    
    def test_view_as_employee(self):
        """Test overtime request view as regular employee"""
        self.client.login(username='employee1', password='testpass123')
        
        # Test POST request
        today = datetime.now().date()
        form_data = {
            'date': today.strftime('%Y-%m-%d'),
            'start_time': '17:00',
            'end_time': '20:00',
            'reason': 'Test overtime'
        }
        response = self.client.post(reverse('overtime:request'), form_data)
        self.assertEqual(response.status_code, 302)  # Should redirect on success
        
        # Verify request was created with PENDING status
        overtime = OvertimeRequest.objects.first()
        self.assertIsNotNone(overtime)
        self.assertEqual(overtime.employee, self.employee)
        self.assertEqual(overtime.status, 'PENDING')
        """Test weekday overtime validation rules"""
        # Test invalid start time (before 5 PM)
        with self.assertRaises(ValidationError):
            overtime = OvertimeRequest.objects.create(
                employee=self.employee,
                date=date(2025, 3, 21),  # Friday
                start_time=time(16, 0),  # 4:00 PM
                end_time=time(20, 0),    # 8:00 PM
                reason='Invalid start time'
            )

        # Test valid weekday overtime
        overtime = OvertimeRequest.objects.create(
            employee=self.employee,
            date=date(2025, 3, 21),  # Friday
            start_time=time(17, 0),  # 5:00 PM
            end_time=time(20, 0),    # 8:00 PM
            reason='Valid weekday overtime'
        )
        # Should be 3 hours (no rate multiplier)
        self.assertEqual(overtime.calculate_overtime_hours(), Decimal('3.0'))
        self.assertEqual(overtime.calculate_rate(), Decimal('1.5'))

    def test_saturday_overtime_validation(self):
        """Test Saturday overtime rules"""
        # Test Saturday overtime (all hours count)
        overtime = OvertimeRequest.objects.create(
            employee=self.employee,
            date=date(2025, 3, 22),  # Today
            start_time=time(9, 0),   # 9:00 AM
            end_time=time(15, 0),    # 3:00 PM
            reason='Saturday work'
        )
        # Should be 6 hours (no rate multiplier)
        self.assertEqual(overtime.calculate_overtime_hours(), Decimal('6.0'))
        self.assertEqual(overtime.calculate_rate(), Decimal('1.5'))

    def test_sunday_overtime_validation(self):
        """Test Sunday overtime rules"""
        overtime = OvertimeRequest.objects.create(
            employee=self.employee,
            date=date(2025, 3, 23),  # Sunday
            start_time=time(10, 0),  # 10:00 AM
            end_time=time(14, 0),    # 2:00 PM
            reason='Sunday work'
        )
        # Should be 4 hours (no rate multiplier)
        self.assertEqual(overtime.calculate_overtime_hours(), Decimal('4.0'))
        self.assertEqual(overtime.calculate_rate(), Decimal('2.0'))

    def test_overtime_amount_calculation(self):
        """Test overtime payment calculation"""
        overtime = OvertimeRequest.objects.create(
            employee=self.employee,
            date=date(2025, 3, 21),  # Friday
            start_time=time(17, 0),  # 5:00 PM
            end_time=time(20, 0),    # 8:00 PM
            reason='Testing amount calculation'
        )

        # For a pending request, amount should be 0
        self.assertEqual(overtime.amount, Decimal('0'))

        # Calculate expected amount
        # Basic pay: 5000
        # Daily rate: 5000 / 22 ≈ 227.27
        # Hourly rate: 227.27 / 8 ≈ 28.41
        # Hours: 3
        # Rate: 1.5 (weekday)
        # Expected: 28.41 * 3 * 1.5 ≈ 127.84

        overtime.status = 'REG_APPROVED'
        overtime.save()
        self.assertAlmostEqual(float(overtime.amount), 127.84, places=2)

    def test_submission_time_limit(self):
        """Test that overtime requests can only be submitted for the current month"""
        # Try to submit for next month
        next_month = timezone.now().date().replace(day=1) + timedelta(days=32)
        form_data = {
            'date': next_month.strftime('%Y-%m-%d'),
            'start_time': '17:00',
            'end_time': '20:00',
            'reason': 'Future month submission'
        }
        form = OvertimeRequestForm(data=form_data, employee=self.employee)
        self.assertFalse(form.is_valid())
        self.assertIn('Overtime can only be requested for the current month', str(form.errors['date']))

    def test_end_time_validation(self):
        """Test that end time must be after start time"""
        with self.assertRaises(ValidationError):
            overtime = OvertimeRequest.objects.create(
                employee=self.employee,
                date=date(2025, 3, 21),  # Friday
                start_time=time(17, 0),
                end_time=time(16, 0),  # Before start time
                reason='Invalid end time'
            )
