# Backend API Tests for Opel Lifts Attendance Management System
# Testing: Authentication, Attendance, Admin endpoints

import pytest
import requests

class TestHealthCheck:
    """Health check endpoint tests"""
    
    def test_health_endpoint(self, api_client, base_url):
        """Test health endpoint returns 200"""
        response = api_client.get(f"{base_url}/api/health")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "ok"
        assert "timestamp" in data
        print("✓ Health check passed")

class TestAuthentication:
    """Authentication endpoint tests"""
    
    def test_login_employee_success(self, api_client, base_url):
        """Test employee login with valid credentials"""
        response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP001", "password": "password123"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "token" in data
        assert "employee" in data
        assert data["employee"]["employee_id"] == "EMP001"
        assert data["employee"]["name"] == "Rajesh Kumar"
        assert data["employee"]["role"] == "employee"
        print(f"✓ Employee login successful: {data['employee']['name']}")
    
    def test_login_admin_success(self, api_client, base_url):
        """Test admin login with valid credentials"""
        response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "ADMIN001", "password": "admin123"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "token" in data
        assert data["employee"]["role"] == "admin"
        print(f"✓ Admin login successful: {data['employee']['name']}")
    
    def test_login_invalid_credentials(self, api_client, base_url):
        """Test login with invalid credentials returns 401"""
        response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP001", "password": "wrongpassword"}
        )
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        print(f"✓ Invalid login rejected: {data['detail']}")
    
    def test_login_nonexistent_user(self, api_client, base_url):
        """Test login with non-existent employee ID"""
        response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "NONEXISTENT", "password": "test123"}
        )
        assert response.status_code == 401
        print("✓ Non-existent user login rejected")
    
    def test_forgot_password_flow(self, api_client, base_url):
        """Test forgot password generates reset token"""
        response = api_client.post(
            f"{base_url}/api/auth/forgot-password",
            json={"employee_id": "EMP002"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "reset_token" in data
        assert "message" in data
        assert len(data["reset_token"]) == 32  # hex(16) = 32 chars
        print(f"✓ Forgot password token generated: {data['reset_token'][:8]}...")
        
        # Test reset password with the token
        reset_response = api_client.post(
            f"{base_url}/api/auth/reset-password",
            json={
                "employee_id": "EMP002",
                "reset_token": data["reset_token"],
                "new_password": "newpassword123"
            }
        )
        assert reset_response.status_code == 200
        print("✓ Password reset successful")
        
        # Verify can login with new password
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP002", "password": "newpassword123"}
        )
        assert login_response.status_code == 200
        print("✓ Login with new password successful")
        
        # Reset back to original password for other tests
        admin_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "ADMIN001", "password": "admin123"}
        )
        admin_token = admin_response.json()["token"]
        api_client.post(
            f"{base_url}/api/admin/change-password",
            headers={"Authorization": f"Bearer {admin_token}"},
            json={"employee_id": "EMP002", "new_password": "password123"}
        )
        print("✓ Password reset to original for other tests")

class TestAttendance:
    """Attendance endpoint tests"""
    
    def test_attendance_status_endpoint(self, api_client, base_url, employee_token):
        """Test GET /api/attendance/status returns correct state"""
        response = api_client.get(
            f"{base_url}/api/attendance/status",
            headers={"Authorization": f"Bearer {employee_token}"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "date" in data
        assert "has_login" in data
        assert "has_logout" in data
        assert "can_login" in data
        assert "can_logout" in data
        print(f"✓ Attendance status: Login={data['has_login']}, Logout={data['has_logout']}")
    
    def test_mark_login_attendance(self, api_client, base_url):
        """Test POST /api/attendance/login marks attendance"""
        # Login as EMP003 (clean state)
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP003", "password": "password123"}
        )
        token = login_response.json()["token"]
        
        # Check if already logged in today
        status_response = api_client.get(
            f"{base_url}/api/attendance/status",
            headers={"Authorization": f"Bearer {token}"}
        )
        status_data = status_response.json()
        
        if not status_data["can_login"]:
            print("⚠ EMP003 already logged in today, cannot test login")
            pytest.skip("Employee already logged in today")
        
        # Mark login
        response = api_client.post(
            f"{base_url}/api/attendance/login",
            headers={"Authorization": f"Bearer {token}"},
            json={"latitude": 28.6139, "longitude": 77.2090}  # Delhi coordinates
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        assert "time" in data
        assert "location_url" in data
        print(f"✓ Login marked at {data['time']}")
        
        # Verify status updated
        verify_response = api_client.get(
            f"{base_url}/api/attendance/status",
            headers={"Authorization": f"Bearer {token}"}
        )
        verify_data = verify_response.json()
        assert verify_data["has_login"] == True
        assert verify_data["can_login"] == False
        assert verify_data["can_logout"] == True
        print("✓ Attendance status updated correctly after login")
    
    def test_mark_logout_attendance(self, api_client, base_url):
        """Test POST /api/attendance/logout marks logout"""
        # Login as EMP003
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP003", "password": "password123"}
        )
        token = login_response.json()["token"]
        
        # Check if can logout
        status_response = api_client.get(
            f"{base_url}/api/attendance/status",
            headers={"Authorization": f"Bearer {token}"}
        )
        status_data = status_response.json()
        
        if not status_data["can_logout"]:
            print("⚠ EMP003 cannot logout (not logged in or already logged out)")
            pytest.skip("Employee cannot logout")
        
        # Mark logout
        response = api_client.post(
            f"{base_url}/api/attendance/logout",
            headers={"Authorization": f"Bearer {token}"},
            json={"latitude": 28.6139, "longitude": 77.2090}
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        assert "time" in data
        print(f"✓ Logout marked at {data['time']}")
        
        # Verify status updated
        verify_response = api_client.get(
            f"{base_url}/api/attendance/status",
            headers={"Authorization": f"Bearer {token}"}
        )
        verify_data = verify_response.json()
        assert verify_data["has_logout"] == True
        assert verify_data["can_logout"] == False
        print("✓ Attendance status updated correctly after logout")
    
    def test_cannot_login_twice(self, api_client, base_url):
        """Test business rule: cannot login twice in one day"""
        # Login as EMP001 (already has login for today per agent note)
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": "EMP001", "password": "password123"}
        )
        token = login_response.json()["token"]
        
        # Try to mark login again
        response = api_client.post(
            f"{base_url}/api/attendance/login",
            headers={"Authorization": f"Bearer {token}"},
            json={"latitude": 28.6139, "longitude": 77.2090}
        )
        
        if response.status_code == 400:
            data = response.json()
            assert "Already logged in" in data["detail"]
            print(f"✓ Duplicate login prevented: {data['detail']}")
        else:
            # If 200, it means EMP001 didn't have login today (test setup issue)
            print("⚠ EMP001 didn't have existing login, test inconclusive")
    
    def test_auto_logout_endpoint(self, api_client, base_url, admin_token):
        """Test POST /api/attendance/auto-logout runs auto logout"""
        response = api_client.post(
            f"{base_url}/api/attendance/auto-logout",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        print(f"✓ Auto logout completed: {data['message']}")

class TestAdminEndpoints:
    """Admin endpoint tests"""
    
    def test_get_employees_list(self, api_client, base_url, admin_token):
        """Test GET /api/admin/employees returns employee list"""
        response = api_client.get(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "employees" in data
        assert len(data["employees"]) >= 4  # At least 4 seeded employees
        
        # Verify structure
        emp = data["employees"][0]
        assert "employee_id" in emp
        assert "name" in emp
        assert "role" in emp
        assert "is_active" in emp
        assert "password_hash" not in emp  # Should be excluded
        print(f"✓ Got {len(data['employees'])} employees")
    
    def test_create_employee(self, api_client, base_url, admin_token):
        """Test POST /api/admin/employees creates new employee"""
        new_emp_id = "TEST_EMP999"
        
        # Delete if exists from previous test run
        api_client.delete(
            f"{base_url}/api/admin/employees/{new_emp_id}",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        
        response = api_client.post(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {admin_token}"},
            json={
                "employee_id": new_emp_id,
                "name": "Test Employee",
                "password": "testpass123",
                "role": "employee"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["employee_id"] == new_emp_id
        print(f"✓ Employee created: {new_emp_id}")
        
        # Verify can login with new employee
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": new_emp_id, "password": "testpass123"}
        )
        assert login_response.status_code == 200
        print("✓ New employee can login")
        
        # Verify appears in employee list
        list_response = api_client.get(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        employees = list_response.json()["employees"]
        assert any(e["employee_id"] == new_emp_id for e in employees)
        print("✓ New employee appears in list")
    
    def test_delete_employee(self, api_client, base_url, admin_token):
        """Test DELETE /api/admin/employees/EMP_ID deletes employee"""
        # Create test employee first
        test_emp_id = "TEST_EMP888"
        api_client.post(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {admin_token}"},
            json={
                "employee_id": test_emp_id,
                "name": "Delete Test",
                "password": "test123",
                "role": "employee"
            }
        )
        
        # Delete the employee
        response = api_client.delete(
            f"{base_url}/api/admin/employees/{test_emp_id}",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        assert response.status_code == 200
        print(f"✓ Employee deleted: {test_emp_id}")
        
        # Verify cannot login anymore
        login_response = api_client.post(
            f"{base_url}/api/auth/login",
            json={"employee_id": test_emp_id, "password": "test123"}
        )
        assert login_response.status_code == 401
        print("✓ Deleted employee cannot login")
        
        # Verify not in employee list
        list_response = api_client.get(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        employees = list_response.json()["employees"]
        assert not any(e["employee_id"] == test_emp_id for e in employees)
        print("✓ Deleted employee removed from list")
    
    def test_cannot_delete_primary_admin(self, api_client, base_url, admin_token):
        """Test business rule: cannot delete ADMIN001"""
        response = api_client.delete(
            f"{base_url}/api/admin/employees/ADMIN001",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        assert response.status_code == 400
        data = response.json()
        assert "Cannot delete primary admin" in data["detail"]
        print(f"✓ Primary admin deletion prevented: {data['detail']}")
    
    def test_admin_get_attendance(self, api_client, base_url, admin_token):
        """Test GET /api/admin/attendance returns attendance records"""
        response = api_client.get(
            f"{base_url}/api/admin/attendance",
            headers={"Authorization": f"Bearer {admin_token}"}
        )
        assert response.status_code == 200
        data = response.json()
        assert "records" in data
        assert "date" in data
        print(f"✓ Got {len(data['records'])} attendance records for {data['date']}")
        
        if len(data["records"]) > 0:
            record = data["records"][0]
            assert "employee_id" in record
            assert "name" in record
            assert "action" in record
            assert "time" in record
            print(f"✓ Record structure valid: {record['name']} - {record['action']}")
    
    def test_employee_cannot_access_admin_endpoints(self, api_client, base_url, employee_token):
        """Test that regular employees cannot access admin endpoints"""
        response = api_client.get(
            f"{base_url}/api/admin/employees",
            headers={"Authorization": f"Bearer {employee_token}"}
        )
        assert response.status_code == 403
        data = response.json()
        assert "Admin access required" in data["detail"]
        print(f"✓ Employee access to admin endpoints blocked: {data['detail']}")
