import unittest
import ssl
from web_interface import app
from security.audit import SecureAudit
from security.rbac_engine import RBACEngine

class TestWebInterfaceSecurity(unittest.TestCase):
    def setUp(self):
        self.client = app.test_client()
        self.audit = SecureAudit(self.rbac)
        self.rbac = RBACEngine()
        
    def test_tls_configuration(self):
        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_3)
        self.assertIn('AES256-GCM', context.get_ciphers()[0]['name'])
        
    def test_security_headers(self):
        response = self.client.get('/permissions/validate')
        self.assertEqual(response.headers['X-Frame-Options'], 'SAMEORIGIN')
        self.assertEqual(response.headers['X-Content-Type-Options'], 'nosniff')
        
    def test_rate_limiting(self):
        for _ in range(10):
            response = self.client.post('/tasks')
            self.assertEqual(response.status_code, 200)
            
        response = self.client.post('/tasks')
        self.assertEqual(response.status_code, 429)
        
    def test_audit_logging(self):
        with self.assertLogs('audit', level='INFO') as cm:
            self.client.post('/tasks')
            self.assertIn('task_add', cm.output[0])
            
    def test_rbac_integration(self):
        response = self.client.post('/tasks')
        self.assertIn(response.status_code, [401, 403])
        
    def test_batch_integrity_verification(self):
        """Verify cryptographic integrity of batched entries"""
        # Generate test operations
        test_ops = [
            {'operation': 'task_add', 'data': 'test1', 'user': 'user1', 'status': 'completed'},
            {'operation': 'task_add', 'data': 'test2', 'user': 'user2', 'status': 'failed'}
        ]
        
        # Process batch
        with self.assertLogs('audit', level='INFO') as cm:
            for op in test_ops:
                self.audit.log_operation(**op)
                
        # Verify integrity
        logs = [record.getMessage() for record in cm.records]
        self.assertEqual(len(logs), 2)  # Should be 2 entries
        self.assertIn('integrity_hash=', logs[0])  # First entry has hash
        self.assertIn('prev_hash=', logs[1])  # Second entry references first
        
    def test_timer_flush_security(self):
        """Verify timer-based flush maintains security properties"""
        # Setup
        test_op = {'operation': 'task_add', 'data': 'test', 'user': 'user1', 'status': 'completed'}
        
        # Log operation and force timer flush
        with self.assertLogs('audit', level='INFO') as cm:
            self.audit.log_operation(**test_op)
            self.audit._flush_batch()  # Force flush
            
        # Verify
        self.assertIn('Batch flushed', cm.output[0])
        self.assertIn('integrity_hash=', cm.output[0])

    def test_size_flush_security(self):
        """Verify size-based flush maintains security properties"""
        # Setup
        test_ops = [
            {'operation': 'task_add', 'data': f'test{i}', 'user': f'user{i}', 'status': 'completed'}
            for i in range(10)  # Batch size limit
        ]
        
        # Log operations (should trigger size-based flush)
        with self.assertLogs('audit', level='INFO') as cm:
            for op in test_ops:
                self.audit.log_operation(**op)
                
        # Verify flush occurred and has integrity
        self.assertIn('Batch size limit reached', cm.output[-1])
        self.assertIn('integrity_hash=', cm.output[-1])

    def test_rbac_batch_notifications(self):
        """Verify RBAC notifications for batched operations"""
        # Setup test user with permissions
        self.rbac.grant_permission('user1', 'batch_operations')
        
        # Test operation
        test_op = {'operation': 'task_add', 'data': 'test', 'user': 'user1', 'status': 'completed'}
        
        # Log operation and verify notification
        with self.assertLogs('rbac', level='INFO') as cm:
            self.audit.log_operation(**test_op)
            self.audit._flush_batch()
            
        # Verify notification
        self.assertIn('Batch operation notification', cm.output[0])
        self.assertIn('user1', cm.output[0])

    def test_thread_safety(self):
        """Verify thread-safe operation during concurrent access"""
        import threading
        
        # Shared test data
        test_ops = [
            {'operation': 'task_add', 'data': f'test{i}', 'user': f'user{i}', 'status': 'completed'}
            for i in range(20)
        ]
        results = []
        
        # Worker function
        def worker(op):
            try:
                self.audit.log_operation(**op)
                results.append(True)
            except Exception:
                results.append(False)
        
        # Create and start threads
        threads = []
        for op in test_ops:
            t = threading.Thread(target=worker, args=(op,))
            threads.append(t)
            t.start()
            
        # Wait for completion
        for t in threads:
            t.join()
            
        # Verify all operations succeeded
        self.assertTrue(all(results))
        self.assertEqual(len(results), len(test_ops))

if __name__ == '__main__':
    unittest.main()