# -*- coding: utf-8 -*-
"""
Core App Tests

This module contains test cases for the core app.
"""

from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.db import connection
from unittest.mock import patch, MagicMock
import json

User = get_user_model()


class HealthCheckTest(TestCase):
    """
    Test cases for health check endpoints.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_health_check_success(self):
        """
        Test successful health check.
        """
        response = self.client.get(reverse('core:health_check'))
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data['status'], 'healthy')
        self.assertIn('timestamp', data)
    
    @patch('django.db.connection.cursor')
    def test_health_check_database_failure(self, mock_cursor):
        """
        Test health check with database failure.
        """
        mock_cursor.side_effect = Exception('Database connection failed')
        
        response = self.client.get(reverse('core:health_check'))
        
        self.assertEqual(response.status_code, 503)
        data = json.loads(response.content)
        self.assertEqual(data['status'], 'unhealthy')
        self.assertIn('error', data)
    
    def test_detailed_health_check_success(self):
        """
        Test successful detailed health check.
        """
        with patch('redis.Redis.from_url') as mock_redis, \
             patch('celery.current_app.control.inspect') as mock_inspect, \
             patch('psutil.virtual_memory') as mock_memory, \
             patch('psutil.disk_usage') as mock_disk, \
             patch('psutil.cpu_percent') as mock_cpu:
            
            # Mock Redis
            mock_redis_instance = MagicMock()
            mock_redis.return_value = mock_redis_instance
            mock_redis_instance.ping.return_value = True
            
            # Mock Celery
            mock_inspect_instance = MagicMock()
            mock_inspect.return_value = mock_inspect_instance
            mock_inspect_instance.stats.return_value = {'worker1': {}}
            
            # Mock system resources
            mock_memory.return_value = MagicMock(percent=50)
            mock_disk.return_value = MagicMock(percent=60)
            mock_cpu.return_value = 30
            
            response = self.client.get(reverse('core:detailed_health_check'))
            
            self.assertEqual(response.status_code, 200)
            data = json.loads(response.content)
            self.assertEqual(data['status'], 'healthy')
            self.assertIn('checks', data)
            self.assertIn('database', data['checks'])
            self.assertIn('cache', data['checks'])
            self.assertIn('redis', data['checks'])
            self.assertIn('celery', data['checks'])
            self.assertIn('system', data['checks'])
    
    def test_detailed_health_check_high_resources(self):
        """
        Test detailed health check with high resource usage.
        """
        with patch('redis.Redis.from_url') as mock_redis, \
             patch('celery.current_app.control.inspect') as mock_inspect, \
             patch('psutil.virtual_memory') as mock_memory, \
             patch('psutil.disk_usage') as mock_disk, \
             patch('psutil.cpu_percent') as mock_cpu:
            
            # Mock Redis
            mock_redis_instance = MagicMock()
            mock_redis.return_value = mock_redis_instance
            mock_redis_instance.ping.return_value = True
            
            # Mock Celery
            mock_inspect_instance = MagicMock()
            mock_inspect.return_value = mock_inspect_instance
            mock_inspect_instance.stats.return_value = {'worker1': {}}
            
            # Mock high system resources
            mock_memory.return_value = MagicMock(percent=95)
            mock_disk.return_value = MagicMock(percent=85)
            mock_cpu.return_value = 92
            
            response = self.client.get(reverse('core:detailed_health_check'))
            
            self.assertEqual(response.status_code, 200)
            data = json.loads(response.content)
            self.assertEqual(data['status'], 'healthy')
            self.assertEqual(data['checks']['system']['status'], 'warning')


class SystemStatusTest(TestCase):
    """
    Test cases for system status endpoint.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_system_status_success(self):
        """
        Test successful system status check.
        """
        response = self.client.get(reverse('core:system_status'))
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data['status'], 'healthy')
        self.assertEqual(data['database'], 'connected')
        self.assertEqual(data['cache'], 'connected')
        self.assertIn('timestamp', data)
        self.assertIn('version', data)
        self.assertIn('environment', data)
    
    @patch('django.db.connection.cursor')
    def test_system_status_database_failure(self, mock_cursor):
        """
        Test system status with database failure.
        """
        mock_cursor.side_effect = Exception('Database connection failed')
        
        response = self.client.get(reverse('core:system_status'))
        
        self.assertEqual(response.status_code, 500)
        data = json.loads(response.content)
        self.assertEqual(data['status'], 'unhealthy')
        self.assertIn('error', data)


class UtilityEndpointsTest(TestCase):
    """
    Test cases for utility endpoints.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_ping_endpoint(self):
        """
        Test ping endpoint.
        """
        response = self.client.get(reverse('core:ping'))
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data['message'], 'pong')
        self.assertIn('timestamp', data)
    
    def test_version_info_endpoint(self):
        """
        Test version info endpoint.
        """
        response = self.client.get(reverse('core:version'))
        
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn('version', data)
        self.assertIn('environment', data)
        self.assertIn('python_version', data)
        self.assertIn('django_version', data)
        self.assertIn('build_date', data)
    
    def test_api_documentation_view(self):
        """
        Test API documentation view.
        """
        response = self.client.get(reverse('core:api_docs'))
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'API Documentation')


class ErrorHandlerTest(TestCase):
    """
    Test cases for error handlers.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_404_error_page(self):
        """
        Test 404 error page.
        """
        response = self.client.get(reverse('core:error_404'))
        
        self.assertEqual(response.status_code, 404)
        self.assertContains(response, 'Page Not Found', status_code=404)
        self.assertContains(response, '404', status_code=404)
    
    def test_500_error_page(self):
        """
        Test 500 error page.
        """
        response = self.client.get(reverse('core:error_500'))
        
        self.assertEqual(response.status_code, 500)
        self.assertContains(response, 'Server Error', status_code=500)
        self.assertContains(response, '500', status_code=500)


class DashboardTest(TestCase):
    """
    Test cases for dashboard view.
    """
    
    def setUp(self):
        self.client = Client()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_dashboard_requires_login(self):
        """
        Test that dashboard requires authentication.
        """
        response = self.client.get('/core/dashboard/')
        
        # Should redirect to login
        self.assertEqual(response.status_code, 302)
    
    def test_dashboard_authenticated_access(self):
        """
        Test dashboard access for authenticated user.
        """
        self.client.login(username='testuser', password='testpass123')
        response = self.client.get('/core/dashboard/')
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Dashboard')


class CacheTest(TestCase):
    """
    Test cases for cache functionality.
    """
    
    def setUp(self):
        self.client = Client()
        # Clear cache before each test
        cache.clear()
    
    def test_cache_functionality(self):
        """
        Test basic cache functionality.
        """
        # Set a cache value
        cache.set('test_key', 'test_value', 60)
        
        # Retrieve the value
        cached_value = cache.get('test_key')
        self.assertEqual(cached_value, 'test_value')
        
        # Delete the value
        cache.delete('test_key')
        cached_value = cache.get('test_key')
        self.assertIsNone(cached_value)
    
    def test_system_status_caching(self):
        """
        Test that system status endpoint uses caching.
        """
        # First request
        response1 = self.client.get(reverse('core:system_status'))
        self.assertEqual(response1.status_code, 200)
        
        # Second request should be cached
        response2 = self.client.get(reverse('core:system_status'))
        self.assertEqual(response2.status_code, 200)
        
        # Responses should be identical due to caching
        self.assertEqual(response1.content, response2.content)


class SecurityTest(TestCase):
    """
    Test cases for security features.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_health_check_only_get_method(self):
        """
        Test that health check only accepts GET requests.
        """
        # GET should work
        response = self.client.get(reverse('core:health_check'))
        self.assertEqual(response.status_code, 200)
        
        # POST should not be allowed
        response = self.client.post(reverse('core:health_check'))
        self.assertEqual(response.status_code, 405)
        
        # PUT should not be allowed
        response = self.client.put(reverse('core:health_check'))
        self.assertEqual(response.status_code, 405)
    
    def test_ping_only_get_method(self):
        """
        Test that ping endpoint only accepts GET requests.
        """
        # GET should work
        response = self.client.get(reverse('core:ping'))
        self.assertEqual(response.status_code, 200)
        
        # POST should not be allowed
        response = self.client.post(reverse('core:ping'))
        self.assertEqual(response.status_code, 405)
    
    def test_version_only_get_method(self):
        """
        Test that version endpoint only accepts GET requests.
        """
        # GET should work
        response = self.client.get(reverse('core:version'))
        self.assertEqual(response.status_code, 200)
        
        # POST should not be allowed
        response = self.client.post(reverse('core:version'))
        self.assertEqual(response.status_code, 405)


class IntegrationTest(TestCase):
    """
    Integration test cases.
    """
    
    def setUp(self):
        self.client = Client()
    
    def test_health_check_integration(self):
        """
        Test health check integration with database and cache.
        """
        # Ensure database is working
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
            result = cursor.fetchone()
            self.assertEqual(result[0], 1)
        
        # Ensure cache is working
        cache.set('integration_test', 'success', 60)
        cached_value = cache.get('integration_test')
        self.assertEqual(cached_value, 'success')
        
        # Test health check endpoint
        response = self.client.get(reverse('core:health_check'))
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.content)
        self.assertEqual(data['status'], 'healthy')
    
    def test_error_handling_integration(self):
        """
        Test error handling integration.
        """
        # Test 404 error
        response = self.client.get('/nonexistent-url/')
        self.assertEqual(response.status_code, 404)
        
        # Test that error is logged (would need to check logs in real scenario)
        # For now, just ensure the response is properly formatted
        self.assertIn('text/html', response.get('Content-Type', ''))