"""
Unit tests for custom exception handler
Tests all exception types and status code mappings
"""
from django.test import TestCase, RequestFactory
from django.http import HttpRequest
from rest_framework import status
from rest_framework.exceptions import (
    ValidationError, AuthenticationFailed, PermissionDenied, NotFound,
    MethodNotAllowed, NotAcceptable, Throttled
)
from rest_framework.views import APIView
from igny8_core.api.exception_handlers import custom_exception_handler


class ExceptionHandlerTestCase(TestCase):
    """Test cases for custom exception handler"""
    
    def setUp(self):
        """Set up test fixtures"""
        self.factory = RequestFactory()
        self.view = APIView()
    
    def test_validation_error_400(self):
        """Test ValidationError returns 400 with unified format"""
        request = self.factory.post('/test/', {})
        exc = ValidationError({"field": ["This field is required"]})
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertFalse(response.data['success'])
        self.assertIn('error', response.data)
        self.assertIn('errors', response.data)
        self.assertIn('request_id', response.data)
    
    def test_authentication_failed_401(self):
        """Test AuthenticationFailed returns 401 with unified format"""
        request = self.factory.get('/test/')
        exc = AuthenticationFailed("Authentication required")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        self.assertFalse(response.data['success'])
        self.assertEqual(response.data['error'], 'Authentication required')
        self.assertIn('request_id', response.data)
    
    def test_permission_denied_403(self):
        """Test PermissionDenied returns 403 with unified format"""
        request = self.factory.get('/test/')
        exc = PermissionDenied("Permission denied")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertFalse(response.data['success'])
        self.assertEqual(response.data['error'], 'Permission denied')
        self.assertIn('request_id', response.data)
    
    def test_not_found_404(self):
        """Test NotFound returns 404 with unified format"""
        request = self.factory.get('/test/')
        exc = NotFound("Resource not found")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
        self.assertFalse(response.data['success'])
        self.assertEqual(response.data['error'], 'Resource not found')
        self.assertIn('request_id', response.data)
    
    def test_throttled_429(self):
        """Test Throttled returns 429 with unified format"""
        request = self.factory.get('/test/')
        exc = Throttled()
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
        self.assertFalse(response.data['success'])
        self.assertEqual(response.data['error'], 'Rate limit exceeded')
        self.assertIn('request_id', response.data)
    
    def test_method_not_allowed_405(self):
        """Test MethodNotAllowed returns 405 with unified format"""
        request = self.factory.post('/test/')
        exc = MethodNotAllowed("POST")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
        self.assertFalse(response.data['success'])
        self.assertIn('error', response.data)
        self.assertIn('request_id', response.data)
    
    def test_unhandled_exception_500(self):
        """Test unhandled exception returns 500 with unified format"""
        request = self.factory.get('/test/')
        exc = ValueError("Unexpected error")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
        self.assertFalse(response.data['success'])
        self.assertEqual(response.data['error'], 'Internal server error')
        self.assertIn('request_id', response.data)
    
    def test_exception_handler_includes_request_id(self):
        """Test exception handler includes request_id in response"""
        request = self.factory.get('/test/')
        request.request_id = 'test-request-id-exception'
        exc = ValidationError("Test error")
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertIn('request_id', response.data)
        self.assertEqual(response.data['request_id'], 'test-request-id-exception')
    
    def test_exception_handler_debug_mode(self):
        """Test exception handler includes debug info in DEBUG mode"""
        from django.conf import settings
        original_debug = settings.DEBUG
        
        try:
            settings.DEBUG = True
            request = self.factory.get('/test/')
            exc = ValueError("Test error")
            context = {'request': request, 'view': self.view}
            
            response = custom_exception_handler(exc, context)
            
            self.assertIn('debug', response.data)
            self.assertIn('exception_type', response.data['debug'])
            self.assertIn('exception_message', response.data['debug'])
            self.assertIn('view', response.data['debug'])
            self.assertIn('path', response.data['debug'])
            self.assertIn('method', response.data['debug'])
        finally:
            settings.DEBUG = original_debug
    
    def test_exception_handler_no_debug_mode(self):
        """Test exception handler excludes debug info when DEBUG=False"""
        from django.conf import settings
        original_debug = settings.DEBUG
        
        try:
            settings.DEBUG = False
            request = self.factory.get('/test/')
            exc = ValueError("Test error")
            context = {'request': request, 'view': self.view}
            
            response = custom_exception_handler(exc, context)
            
            self.assertNotIn('debug', response.data)
        finally:
            settings.DEBUG = original_debug
    
    def test_field_specific_validation_errors(self):
        """Test field-specific validation errors are included"""
        request = self.factory.post('/test/', {})
        exc = ValidationError({
            "email": ["Invalid email format"],
            "password": ["Password too short", "Password must contain numbers"]
        })
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertIn('errors', response.data)
        self.assertIn('email', response.data['errors'])
        self.assertIn('password', response.data['errors'])
        self.assertEqual(len(response.data['errors']['password']), 2)
    
    def test_non_field_validation_errors(self):
        """Test non-field validation errors are handled"""
        request = self.factory.post('/test/', {})
        exc = ValidationError({"non_field_errors": ["General validation error"]})
        context = {'request': request, 'view': self.view}
        
        response = custom_exception_handler(exc, context)
        
        self.assertIn('errors', response.data)
        self.assertIn('non_field_errors', response.data['errors'])

