"""
Tests for the migrate_useractivity_to_activity management command.
"""
from django.test import TestCase, TransactionTestCase
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.db import connection
from io import StringIO

from apps.activities.models import Activity, ActivityCategory

User = get_user_model()


class MigrateUserActivityCommandTest(TransactionTestCase):
    """
    Test the migrate_useractivity_to_activity management command.
    """
    
    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
        )
        
        # Create the UserActivity table manually for testing
        self.create_useractivity_table()
    
    def create_useractivity_table(self):
        """Create a temporary UserActivity table for testing."""
        with connection.cursor() as cursor:
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS accounts_useractivity (
                    id SERIAL PRIMARY KEY,
                    user_id INTEGER REFERENCES auth_user(id),
                    action VARCHAR(50) NOT NULL,
                    object_type VARCHAR(100),
                    object_id INTEGER,
                    object_repr VARCHAR(255),
                    details JSONB,
                    ip_address INET,
                    user_agent TEXT,
                    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
                    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
                )
            """)
    
    def drop_useractivity_table(self):
        """Drop the temporary UserActivity table."""
        with connection.cursor() as cursor:
            cursor.execute("DROP TABLE IF EXISTS accounts_useractivity")
    
    def tearDown(self):
        """Clean up after tests."""
        self.drop_useractivity_table()
    
    def insert_useractivity_data(self, data):
        """Insert test data into the UserActivity table."""
        with connection.cursor() as cursor:
            placeholders = ', '.join(['%s'] * len(data))
            columns = ', '.join(data.keys())
            cursor.execute(
                f"INSERT INTO accounts_useractivity ({columns}) VALUES ({placeholders})",
                list(data.values())
            )
    
    def test_migration_command_basic(self):
        """Test basic migration functionality."""
        # Insert test data
        self.insert_useractivity_data({
            'user_id': self.user.id,
            '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',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command
        out = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out)
        
        # Check that the activity was created
        self.assertEqual(Activity.objects.count(), 1)
        activity = Activity.objects.first()
        
        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.code, 'authentication')
        
        # Check command output
        output = out.getvalue()
        self.assertIn('Successfully migrated 1 UserActivity records', output)
    
    def test_migration_command_dry_run(self):
        """Test dry run mode."""
        # Insert test data
        self.insert_useractivity_data({
            'user_id': self.user.id,
            'action': 'login',
            'details': '{}',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command in dry run mode
        out = StringIO()
        call_command('migrate_useractivity_to_activity', dry_run=True, stdout=out)
        
        # Check that no activities were created
        self.assertEqual(Activity.objects.count(), 0)
        
        # Check command output
        output = out.getvalue()
        self.assertIn('DRY RUN: Would migrate 1 UserActivity records', output)
    
    def test_migration_command_batch_processing(self):
        """Test batch processing with multiple records."""
        # Insert multiple test records
        for i in range(5):
            self.insert_useractivity_data({
                'user_id': self.user.id,
                'action': 'login',
                'details': f'{{"attempt": {i}}}',
                'created_at': timezone.now(),
                'updated_at': timezone.now()
            })
        
        # Run the migration command with small batch size
        out = StringIO()
        call_command('migrate_useractivity_to_activity', batch_size=2, stdout=out)
        
        # Check that all activities were created
        self.assertEqual(Activity.objects.count(), 5)
        
        # Check command output
        output = out.getvalue()
        self.assertIn('Successfully migrated 5 UserActivity records', output)
        self.assertIn('Batch 1/3', output)
        self.assertIn('Batch 2/3', output)
        self.assertIn('Batch 3/3', output)
    
    def test_migration_command_error_handling(self):
        """Test error handling during migration."""
        # Insert test data with invalid user_id
        self.insert_useractivity_data({
            'user_id': 99999,  # Non-existent user
            'action': 'login',
            'details': '{}',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command
        out = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out)
        
        # Check that no activities were created
        self.assertEqual(Activity.objects.count(), 0)
        
        # Check command output
        output = out.getvalue()
        self.assertIn('Error migrating record', output)
        self.assertIn('Successfully migrated 0 UserActivity records', output)
    
    def test_migration_command_content_type_handling(self):
        """Test content type handling during migration."""
        # Insert test data with valid content type
        user_content_type = ContentType.objects.get_for_model(User)
        self.insert_useractivity_data({
            'user_id': self.user.id,
            'action': 'update',
            'object_type': f'{user_content_type.app_label}.{user_content_type.model}',
            'object_id': self.user.id,
            'object_repr': str(self.user),
            'details': '{}',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Insert test data with invalid content type
        self.insert_useractivity_data({
            'user_id': self.user.id,
            'action': 'update',
            'object_type': 'invalid.model',
            'object_id': 999,
            'object_repr': 'Invalid Object',
            'details': '{}',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command
        out = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out)
        
        # Check that both activities were created
        self.assertEqual(Activity.objects.count(), 2)
        
        # Check the valid content type activity
        valid_activity = Activity.objects.filter(content_type__isnull=False).first()
        self.assertEqual(valid_activity.content_type, user_content_type)
        self.assertEqual(valid_activity.object_id, self.user.id)
        
        # Check the invalid content type activity
        invalid_activity = Activity.objects.filter(content_type__isnull=True).first()
        self.assertIsNone(invalid_activity.content_type)
        self.assertIsNone(invalid_activity.object_id)
        self.assertIn('original_object_type', invalid_activity.metadata)
        self.assertEqual(invalid_activity.metadata['original_object_type'], 'invalid.model')
    
    def test_migration_command_skip_existing(self):
        """Test that the command doesn't duplicate existing activities."""
        # Insert test data
        self.insert_useractivity_data({
            'user_id': self.user.id,
            'action': 'login',
            'details': '{}',
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command twice
        out1 = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out1)
        
        out2 = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out2)
        
        # Check that only one activity was created
        self.assertEqual(Activity.objects.count(), 1)
        
        # Check second command output
        output2 = out2.getvalue()
        self.assertIn('Successfully migrated 0 UserActivity records', output2)
    
    def test_migration_command_json_details(self):
        """Test handling of JSON details field."""
        # Insert test data with complex JSON details
        complex_details = {
            'method': 'email',
            'success': True,
            'metadata': {
                'browser': 'Chrome',
                'version': '91.0'
            },
            'list_data': [1, 2, 3]
        }
        
        self.insert_useractivity_data({
            'user_id': self.user.id,
            'action': 'login',
            'details': str(complex_details).replace("'", '"'),
            'created_at': timezone.now(),
            'updated_at': timezone.now()
        })
        
        # Run the migration command
        out = StringIO()
        call_command('migrate_useractivity_to_activity', stdout=out)
        
        # Check that the activity was created with correct metadata
        self.assertEqual(Activity.objects.count(), 1)
        activity = Activity.objects.first()
        
        self.assertEqual(activity.metadata['method'], 'email')
        self.assertEqual(activity.metadata['success'], True)
        self.assertEqual(activity.metadata['metadata']['browser'], 'Chrome')
        self.assertEqual(activity.metadata['list_data'], [1, 2, 3])
