"""
Test framework for permission system validation and migration testing
"""
from django.test import TestCase, TransactionTestCase
from django.core.management import call_command
from django.db import transaction
from django.utils import timezone
from users.models import CustomUser, RolePermission, DefaultRolePermission, UserPermission, Branch
from users.enhanced_permissions_models import PagePermission, RolePermissionTemplate, UserPagePermission
from users.permission_validation import PermissionDataValidator
from users.feature_flags import feature_flags
from users.permission_compatibility import permission_compatibility
import tempfile
import json
import os


class PermissionMigrationTestCase(TransactionTestCase):
    """
    Test case for permission migration functionality
    """
    
    def setUp(self):
        """Set up test data"""
        # Create test branch
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TEST",
            is_main_branch=True
        )
        
        # Create test users
        self.admin_user = CustomUser.objects.create_user(
            username='admin_test',
            email='admin@test.com',
            password='testpass123',
            role='admin',
            branch=self.branch,
            phone_number='+254700000001'
        )
        
        self.loan_officer = CustomUser.objects.create_user(
            username='officer_test',
            email='officer@test.com',
            password='testpass123',
            role='loan_officer',
            branch=self.branch,
            phone_number='+254700000002'
        )
        
        # Create test legacy permissions
        self.create_test_legacy_permissions()
    
    def create_test_legacy_permissions(self):
        """Create test legacy permissions"""
        # Role permissions
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='create',
            is_allowed=True
        )
        
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='edit',
            is_allowed=True
        )
        
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='delete',
            is_allowed=False
        )
        
        # Default role permissions
        DefaultRolePermission.objects.create(
            role='loan_officer',
            module='clients',
            action='create',
            is_allowed=True,
            description='Allow loan officers to create clients'
        )
        
        # User permissions
        UserPermission.objects.create(
            user=self.loan_officer,
            module='loans',
            action='delete',
            is_allowed=True,
            granted_by=self.admin_user,
            reason='Special permission for testing'
        )
    
    def test_migration_command_dry_run(self):
        """Test migration command in dry-run mode"""
        # Run migration in dry-run mode
        call_command('migrate_permissions_to_granular', '--dry-run')
        
        # Verify no granular permissions were created
        self.assertEqual(PagePermission.objects.count(), 0)
        self.assertEqual(RolePermissionTemplate.objects.count(), 0)
        self.assertEqual(UserPagePermission.objects.count(), 0)
        
        # Verify legacy permissions still exist
        self.assertTrue(RolePermission.objects.exists())
        self.assertTrue(DefaultRolePermission.objects.exists())
        self.assertTrue(UserPermission.objects.exists())
    
    def test_migration_command_with_backup(self):
        """Test migration command with backup creation"""
        with tempfile.TemporaryDirectory() as temp_dir:
            os.chdir(temp_dir)
            
            # Run migration with backup
            call_command('migrate_permissions_to_granular', '--backup', '--dry-run')
            
            # Check if backup file was created
            backup_files = [f for f in os.listdir('.') if f.startswith('permission_backup_')]
            self.assertTrue(len(backup_files) > 0)
            
            # Verify backup content
            with open(backup_files[0], 'r') as f:
                backup_data = json.load(f)
            
            self.assertIn('role_permissions', backup_data)
            self.assertIn('default_role_permissions', backup_data)
            self.assertIn('user_permissions', backup_data)
            self.assertTrue(len(backup_data['role_permissions']) > 0)
    
    def test_full_migration(self):
        """Test full migration from legacy to granular"""
        # Count original permissions
        original_role_perms = RolePermission.objects.count()
        original_default_perms = DefaultRolePermission.objects.count()
        original_user_perms = UserPermission.objects.count()
        
        # Run migration
        call_command('migrate_permissions_to_granular', '--force')
        
        # Verify granular permissions were created
        self.assertTrue(PagePermission.objects.exists())
        self.assertTrue(RolePermissionTemplate.objects.exists())
        self.assertTrue(UserPagePermission.objects.exists())
        
        # Verify migration completeness
        expected_page_perms = len(set(
            (perm.module, perm.action) for perm in RolePermission.objects.all()
        ).union(set(
            (perm.module, perm.action) for perm in DefaultRolePermission.objects.all()
        )).union(set(
            (perm.module, perm.action) for perm in UserPermission.objects.all()
        )))
        
        self.assertEqual(PagePermission.objects.count(), expected_page_perms)
    
    def test_rollback_migration(self):
        """Test rollback functionality"""
        # First, run migration
        call_command('migrate_permissions_to_granular', '--force')
        
        # Verify granular permissions exist
        self.assertTrue(PagePermission.objects.exists())
        
        # Create backup for rollback
        with tempfile.TemporaryDirectory() as temp_dir:
            os.chdir(temp_dir)
            call_command('migrate_permissions_to_granular', '--backup', '--dry-run')
            backup_files = [f for f in os.listdir('.') if f.startswith('permission_backup_')]
            
            # Run rollback
            call_command('rollback_permission_migration', 
                        f'--backup-file={backup_files[0]}', '--confirm')
            
            # Verify granular permissions were cleared
            self.assertEqual(PagePermission.objects.count(), 0)
            self.assertEqual(RolePermissionTemplate.objects.count(), 0)
            self.assertEqual(UserPagePermission.objects.count(), 0)
            
            # Verify legacy permissions were restored
            self.assertTrue(RolePermission.objects.exists())
            self.assertTrue(DefaultRolePermission.objects.exists())
            self.assertTrue(UserPermission.objects.exists())
    
    def test_permission_validation(self):
        """Test permission validation functionality"""
        validator = PermissionDataValidator()
        
        # Validate legacy permissions
        legacy_results = validator.validate_legacy_permissions()
        self.assertIn('system', legacy_results)
        self.assertEqual(legacy_results['system'], 'legacy')
        self.assertIn('is_valid', legacy_results)
        
        # Run migration
        call_command('migrate_permissions_to_granular', '--force')
        
        # Validate granular permissions
        granular_results = validator.validate_granular_permissions()
        self.assertEqual(granular_results['system'], 'granular')
        
        # Validate migration integrity
        migration_results = validator.validate_migration_integrity()
        self.assertEqual(migration_results['system'], 'migration')
    
    def test_compatibility_layer(self):
        """Test compatibility layer functionality"""
        # Test with legacy system
        feature_flags.set_flag('use_granular_permissions', False)
        
        # Test permission checking
        has_perm = permission_compatibility.has_permission(
            self.loan_officer, 'loans', 'create'
        )
        self.assertTrue(has_perm)
        
        # Run migration
        call_command('migrate_permissions_to_granular', '--force')
        
        # Test with granular system
        feature_flags.set_flag('use_granular_permissions', True)
        
        # Test page permission checking
        has_page_perm = permission_compatibility.has_page_permission(
            self.loan_officer, 'loans', 'loans_create'
        )
        self.assertTrue(has_page_perm)
        
        # Test getting user permissions
        user_perms = permission_compatibility.get_user_permissions(self.loan_officer)
        self.assertIn('permissions', user_perms)
        self.assertIn('system', user_perms)
    
    def test_feature_flags(self):
        """Test feature flag functionality"""
        # Test default values
        self.assertFalse(feature_flags.is_enabled('use_granular_permissions'))
        self.assertTrue(feature_flags.is_enabled('fallback_to_legacy'))
        
        # Test setting flags
        feature_flags.set_flag('use_granular_permissions', True)
        self.assertTrue(feature_flags.is_enabled('use_granular_permissions'))
        
        # Test user-specific flags
        feature_flags.enable_granular_permissions_for_user(self.loan_officer)
        self.assertTrue(feature_flags.is_enabled('use_granular_permissions', self.loan_officer))
        
        # Test migration modes
        feature_flags.set_migration_mode('testing')
        self.assertEqual(feature_flags.get_migration_mode(), 'testing')
        self.assertTrue(feature_flags.is_enabled('use_granular_permissions'))
        self.assertTrue(feature_flags.is_enabled('debug_permissions'))
    
    def test_enhanced_user_methods(self):
        """Test enhanced user methods"""
        # Run migration first
        call_command('migrate_permissions_to_granular', '--force')
        
        # Enable granular permissions
        feature_flags.set_flag('use_granular_permissions', True)
        
        # Test has_page_permission method
        has_perm = self.loan_officer.has_page_permission('loans', 'loans_create')
        self.assertTrue(has_perm)
        
        # Test get_page_permissions method
        page_perms = self.loan_officer.get_page_permissions('loans')
        self.assertIsInstance(page_perms, dict)
        
        # Test get_effective_permissions_enhanced method
        enhanced_perms = self.loan_officer.get_effective_permissions_enhanced()
        self.assertIn('permissions', enhanced_perms)
        self.assertIn('system', enhanced_perms)
        self.assertEqual(enhanced_perms['system'], 'granular')
        
        # Test get_available_page_actions method
        available_actions = self.loan_officer.get_available_page_actions('loans')
        self.assertIsInstance(available_actions, list)
    
    def tearDown(self):
        """Clean up after tests"""
        # Clear feature flags
        feature_flags.clear_all_flags()
        
        # Clear all permissions
        PagePermission.objects.all().delete()
        RolePermissionTemplate.objects.all().delete()
        UserPagePermission.objects.all().delete()
        RolePermission.objects.all().delete()
        DefaultRolePermission.objects.all().delete()
        UserPermission.objects.all().delete()


class PermissionValidationTestCase(TestCase):
    """
    Test case for permission validation functionality
    """
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TEST",
            is_main_branch=True
        )
        
        self.user = CustomUser.objects.create_user(
            username='test_user',
            email='test@test.com',
            password='testpass123',
            role='loan_officer',
            branch=self.branch,
            phone_number='+254700000003'
        )
    
    def test_legacy_permission_validation(self):
        """Test legacy permission validation"""
        # Create some test permissions with issues
        RolePermission.objects.create(
            role='invalid_role',  # Invalid role
            module='loans',
            action='create',
            is_allowed=True
        )
        
        # Create duplicate permission
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='create',
            is_allowed=True
        )
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='create',
            is_allowed=False
        )
        
        validator = PermissionDataValidator()
        results = validator.validate_legacy_permissions()
        
        self.assertFalse(results['is_valid'])
        self.assertTrue(len(results['issues']) > 0)
    
    def test_granular_permission_validation(self):
        """Test granular permission validation"""
        # Create test page permission
        page_perm = PagePermission.objects.create(
            page_name='loans',
            action_code='loans_create',
            action_name='Create Loan',
            description='Allow creating loans',
            category='create'
        )
        
        # Create duplicate role template
        RolePermissionTemplate.objects.create(
            role='loan_officer',
            page_permission=page_perm,
            is_allowed=True
        )
        RolePermissionTemplate.objects.create(
            role='loan_officer',
            page_permission=page_perm,
            is_allowed=False
        )
        
        validator = PermissionDataValidator()
        results = validator.validate_granular_permissions()
        
        self.assertFalse(results['is_valid'])
        self.assertTrue(len(results['issues']) > 0)
    
    def test_migration_validation(self):
        """Test migration validation"""
        # Create legacy permissions
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='create',
            is_allowed=True
        )
        
        # Create incomplete granular permissions (missing some mappings)
        PagePermission.objects.create(
            page_name='loans',
            action_code='loans_edit',  # Different action than legacy
            action_name='Edit Loan',
            description='Allow editing loans',
            category='edit'
        )
        
        validator = PermissionDataValidator()
        results = validator.validate_migration_integrity()
        
        # Should find issues with incomplete migration
        self.assertTrue(len(results['issues']) > 0)


class PermissionCompatibilityTestCase(TestCase):
    """
    Test case for permission compatibility layer
    """
    
    def setUp(self):
        """Set up test data"""
        self.branch = Branch.objects.create(
            name="Test Branch",
            code="TEST",
            is_main_branch=True
        )
        
        self.user = CustomUser.objects.create_user(
            username='test_user',
            email='test@test.com',
            password='testpass123',
            role='loan_officer',
            branch=self.branch,
            phone_number='+254700000004'
        )
        
        # Create legacy permission
        RolePermission.objects.create(
            role='loan_officer',
            module='loans',
            action='create',
            is_allowed=True
        )
    
    def test_legacy_permission_checking(self):
        """Test permission checking with legacy system"""
        feature_flags.set_flag('use_granular_permissions', False)
        
        has_perm = permission_compatibility.has_permission(self.user, 'loans', 'create')
        self.assertTrue(has_perm)
        
        has_perm = permission_compatibility.has_permission(self.user, 'loans', 'delete')
        self.assertFalse(has_perm)
    
    def test_granular_permission_checking(self):
        """Test permission checking with granular system"""
        # Create granular permissions
        page_perm = PagePermission.objects.create(
            page_name='loans',
            action_code='loans_create',
            action_name='Create Loan',
            description='Allow creating loans',
            category='create'
        )
        
        RolePermissionTemplate.objects.create(
            role='loan_officer',
            page_permission=page_perm,
            is_allowed=True
        )
        
        feature_flags.set_flag('use_granular_permissions', True)
        
        has_perm = permission_compatibility.has_page_permission(
            self.user, 'loans', 'loans_create'
        )
        self.assertTrue(has_perm)
    
    def test_fallback_behavior(self):
        """Test fallback from granular to legacy system"""
        feature_flags.set_flag('use_granular_permissions', True)
        feature_flags.set_flag('fallback_to_legacy', True)
        
        # No granular permissions exist, should fall back to legacy
        has_perm = permission_compatibility.has_permission(self.user, 'loans', 'create')
        self.assertTrue(has_perm)  # Should use legacy permission
    
    def tearDown(self):
        """Clean up after tests"""
        feature_flags.clear_all_flags()