"""
Tests for activities utilities and helper functions.
"""
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from unittest.mock import patch, MagicMock

from apps.activities.models import Activity, ActivityCategory
from apps.activities.utils import log_activity, log_user_activity
from apps.activities.managers import ActivityManager

User = get_user_model()


class ActivityUtilsTest(TestCase):
    """Test activity utility functions."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            email='test@example.com',
            username='testuser',
            password='testpass123'
        )
        
        # Create test categories
        self.auth_category = ActivityCategory.objects.create(
            name='Authentication',
            code='authentication',
            description='User authentication activities',
            color='#28a745',
            icon='fas fa-sign-in-alt',
            is_system=True
        )
        
        self.general_category = ActivityCategory.objects.create(
            name='General',
            code='general',
            description='General system activities',
            color='#6c757d',
            icon='fas fa-cog',
            is_system=True
        )
    
    def test_log_activity_basic(self):
        """Test basic activity logging."""
        activity = log_activity(
            user=self.user,
            action='TEST_ACTION',
            description='Test action performed',
            category=self.general_category
        )
        
        self.assertIsInstance(activity, Activity)
        self.assertEqual(activity.user, self.user)
        self.assertEqual(activity.action, 'TEST_ACTION')
        self.assertEqual(activity.description, 'Test action performed')
        self.assertEqual(activity.category, self.general_category)
    
    def test_log_activity_with_metadata(self):
        """Test activity logging with metadata."""
        metadata = {
            'method': 'email',
            'success': True,
            'details': {'foo': 'bar'}
        }
        
        activity = log_activity(
            user=self.user,
            action='LOGIN',
            metadata=metadata,
            category=self.auth_category
        )
        
        self.assertEqual(activity.metadata, metadata)
    
    def test_log_activity_with_content_object(self):
        """Test activity logging with content object."""
        activity = log_activity(
            user=self.user,
            action='USER_UPDATE',
            content_object=self.user,
            category=self.general_category
        )
        
        self.assertEqual(activity.content_object, self.user)
        self.assertEqual(activity.content_type, ContentType.objects.get_for_model(User))
        self.assertEqual(activity.object_id, self.user.id)
    
    def test_log_activity_with_ip_and_user_agent(self):
        """Test activity logging with IP and user agent."""
        activity = log_activity(
            user=self.user,
            action='LOGIN',
            ip_address='192.168.1.100',
            user_agent='Mozilla/5.0 Test Browser',
            category=self.auth_category
        )
        
        self.assertEqual(activity.ip_address, '192.168.1.100')
        self.assertEqual(activity.user_agent, 'Mozilla/5.0 Test Browser')
    
    def test_log_activity_auto_category(self):
        """Test automatic category assignment."""
        activity = log_activity(
            user=self.user,
            action='LOGIN'
        )
        
        # Should automatically assign authentication category
        self.assertEqual(activity.category, self.auth_category)
    
    def test_log_activity_fallback_category(self):
        """Test fallback to general category."""
        activity = log_activity(
            user=self.user,
            action='UNKNOWN_ACTION'
        )
        
        # Should fall back to general category
        self.assertEqual(activity.category, self.general_category)
    
    def test_log_user_activity_backward_compatibility(self):
        """Test backward compatibility function."""
        activity = log_user_activity(
            user=self.user,
            action='login',
            object_type='User',
            object_id=self.user.id,
            object_repr=str(self.user),
            details={'method': 'email'},
            ip_address='127.0.0.1',
            user_agent='Test Browser'
        )
        
        self.assertIsInstance(activity, Activity)
        self.assertEqual(activity.user, self.user)
        self.assertEqual(activity.action, 'LOGIN')
        self.assertEqual(activity.ip_address, '127.0.0.1')
        self.assertEqual(activity.user_agent, 'Test Browser')
        self.assertEqual(activity.metadata, {'method': 'email'})
        self.assertEqual(activity.category, self.auth_category)
    
    def test_log_user_activity_with_content_type(self):
        """Test backward compatibility with content type resolution."""
        user_content_type = ContentType.objects.get_for_model(User)
        object_type = f'{user_content_type.app_label}.{user_content_type.model}'
        
        activity = log_user_activity(
            user=self.user,
            action='update',
            object_type=object_type,
            object_id=self.user.id,
            object_repr=str(self.user)
        )
        
        self.assertEqual(activity.content_type, user_content_type)
        self.assertEqual(activity.object_id, self.user.id)
    
    def test_log_user_activity_invalid_content_type(self):
        """Test backward compatibility with invalid content type."""
        activity = log_user_activity(
            user=self.user,
            action='update',
            object_type='invalid.model',
            object_id=999,
            object_repr='Invalid Object'
        )
        
        self.assertIsNone(activity.content_type)
        self.assertIsNone(activity.object_id)
        self.assertIn('original_object_type', activity.metadata)
        self.assertEqual(activity.metadata['original_object_type'], 'invalid.model')
    
    def test_log_user_activity_action_mapping(self):
        """Test action mapping in backward compatibility function."""
        # Test various action mappings
        test_cases = [
            ('login', 'LOGIN'),
            ('logout', 'LOGOUT'),
            ('register', 'REGISTER'),
            ('update', 'UPDATE'),
            ('delete', 'DELETE'),
            ('create', 'CREATE'),
            ('custom_action', 'CUSTOM_ACTION')
        ]
        
        for old_action, expected_action in test_cases:
            activity = log_user_activity(
                user=self.user,
                action=old_action
            )
            self.assertEqual(activity.action, expected_action)
    
    @patch('apps.activities.utils.logger')
    def test_log_activity_error_handling(self, mock_logger):
        """Test error handling in activity logging."""
        # Test with invalid user
        activity = log_activity(
            user=None,
            action='TEST_ACTION',
            category=self.general_category
        )
        
        self.assertIsNone(activity)
        mock_logger.error.assert_called_once()
    
    @patch('apps.activities.utils.logger')
    def test_log_user_activity_error_handling(self, mock_logger):
        """Test error handling in backward compatibility function."""
        # Test with invalid user
        activity = log_user_activity(
            user=None,
            action='login'
        )
        
        self.assertIsNone(activity)
        mock_logger.error.assert_called_once()


class ActivityManagerTest(TestCase):
    """Test Activity manager methods."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            email='test@example.com',
            username='testuser',
            password='testpass123'
        )
        
        # Create test categories
        self.auth_category = ActivityCategory.objects.create(
            name='Authentication',
            code='authentication',
            description='User authentication activities',
            color='#28a745',
            icon='fas fa-sign-in-alt',
            is_system=True
        )
        
        self.general_category = ActivityCategory.objects.create(
            name='General',
            code='general',
            description='General system activities',
            color='#6c757d',
            icon='fas fa-cog',
            is_system=True
        )
    
    def test_log_activity_manager_method(self):
        """Test the log_activity manager method."""
        activity = Activity.objects.log_activity(
            user=self.user,
            action='LOGIN',
            category=self.auth_category,
            ip_address='127.0.0.1',
            user_agent='Test Browser'
        )
        
        self.assertIsInstance(activity, Activity)
        self.assertEqual(activity.user, self.user)
        self.assertEqual(activity.action, 'LOGIN')
        self.assertEqual(activity.category, self.auth_category)
        self.assertEqual(activity.ip_address, '127.0.0.1')
        self.assertEqual(activity.user_agent, 'Test Browser')
    
    def test_for_user_manager_method(self):
        """Test the for_user manager method."""
        # Create activities for different users
        other_user = User.objects.create_user(
            email='other@example.com',
            username='otheruser',
            password='testpass123'
        )
        
        Activity.objects.log_activity(
            user=self.user,
            action='LOGIN',
            category=self.auth_category
        )
        
        Activity.objects.log_activity(
            user=other_user,
            action='LOGIN',
            category=self.auth_category
        )
        
        # Test filtering by user
        user_activities = Activity.objects.for_user(self.user)
        self.assertEqual(user_activities.count(), 1)
        self.assertEqual(user_activities.first().user, self.user)
    
    def test_by_category_manager_method(self):
        """Test the by_category manager method."""
        # Create activities in different categories
        Activity.objects.log_activity(
            user=self.user,
            action='LOGIN',
            category=self.auth_category
        )
        
        Activity.objects.log_activity(
            user=self.user,
            action='UPDATE',
            category=self.general_category
        )
        
        # Test filtering by category
        auth_activities = Activity.objects.by_category(self.auth_category)
        self.assertEqual(auth_activities.count(), 1)
        self.assertEqual(auth_activities.first().category, self.auth_category)
    
    def test_recent_manager_method(self):
        """Test the recent manager method."""
        # Create activities with different timestamps
        old_time = timezone.now() - timezone.timedelta(hours=2)
        
        old_activity = Activity.objects.log_activity(
            user=self.user,
            action='OLD_ACTION',
            category=self.general_category
        )
        old_activity.created_at = old_time
        old_activity.save()
        
        recent_activity = Activity.objects.log_activity(
            user=self.user,
            action='RECENT_ACTION',
            category=self.general_category
        )
        
        # Test recent activities (last hour)
        recent_activities = Activity.objects.recent(hours=1)
        self.assertEqual(recent_activities.count(), 1)
        self.assertEqual(recent_activities.first().action, 'RECENT_ACTION')
    
    def test_with_metadata_manager_method(self):
        """Test the with_metadata manager method."""
        # Create activities with and without metadata
        Activity.objects.log_activity(
            user=self.user,
            action='WITH_METADATA',
            category=self.general_category,
            metadata={'key': 'value'}
        )
        
        Activity.objects.log_activity(
            user=self.user,
            action='WITHOUT_METADATA',
            category=self.general_category
        )
        
        # Test filtering activities with metadata
        activities_with_metadata = Activity.objects.with_metadata()
        self.assertEqual(activities_with_metadata.count(), 1)
        self.assertEqual(activities_with_metadata.first().action, 'WITH_METADATA')
