import pytest
from unittest.mock import patch, Mock
from app import create_app, db
from app.models import User, Folder, AIRuleCache
from app.ai_service import AIService

class TestAIRuleEndpoints:
    """Test cases for AI rule generation API endpoints."""
    
    @pytest.fixture
    def app(self):
        """Create and configure a test app."""
        app = create_app('testing')
        
        with app.app_context():
            db.create_all()
            yield app
            db.drop_all()
            
    @pytest.fixture
    def client(self, app):
        """Create a test client."""
        return app.test_client()
        
    @pytest.fixture
    def user(self, app):
        """Create a test user."""
        with app.app_context():
            user = User(
                first_name='Test',
                last_name='User',
                email='test@example.com',
                password_hash='hashed_password'
            )
            db.session.add(user)
            db.session.commit()
            # Refresh the user to ensure it's attached to the session
            db.session.refresh(user)
            return user
            
    @pytest.fixture
    def authenticated_client(self, client, user):
        """Create a test client with authenticated user."""
        with client.session_transaction() as sess:
            sess['_user_id'] = str(user.id)
            sess['_fresh'] = True
        return client
        
    def test_generate_rule_success(self, authenticated_client, user):
        """Test successful rule generation."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service response
            mock_ai_service.generate_multiple_rules.return_value = (
                [{'text': "Move emails from 'boss@company.com' to this folder", 'quality_score': 85}],
                {'total_generated': 1}
            )
            
            response = authenticated_client.post('/api/folders/generate-rule', data={
                'name': 'Work',
                'folder_type': 'destination',
                'rule_type': 'multiple'
            })
            
            assert response.status_code == 200
            # Check that the response contains HTML
            assert b'generated-rule-text' in response.data
            assert b'Move emails from' in response.data
            
    def test_generate_rule_missing_folder_name(self, authenticated_client, user):
        """Test rule generation with missing folder name."""
        response = authenticated_client.post('/api/folders/generate-rule', data={
            'folder_type': 'destination',
            'rule_type': 'multiple'
        })
        
        assert response.status_code == 200
        # Should return error in HTML format
        assert b'Folder name is required' in response.data
        
    def test_generate_rule_invalid_folder_type(self, authenticated_client, user):
        """Test rule generation with invalid folder type."""
        response = authenticated_client.post('/api/folders/generate-rule', data={
            'name': 'Work',
            'folder_type': 'invalid',
            'rule_type': 'multiple'
        })
        
        assert response.status_code == 200
        # Should return error in HTML format
        assert b'Invalid folder type' in response.data
        
    def test_generate_rule_multiple_options(self, authenticated_client, user):
        """Test multiple rule options generation."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service response
            mock_ai_service.generate_multiple_rules.return_value = (
                [
                    {'text': 'Rule 1', 'quality_score': 85},
                    {'text': 'Rule 2', 'quality_score': 75}
                ],
                {'total_generated': 2}
            )
            
            response = authenticated_client.post('/api/folders/generate-rule', data={
                'name': 'Work',
                'folder_type': 'destination',
                'rule_type': 'multiple'
            })
            
            assert response.status_code == 200
            # Check that multiple rules are displayed
            assert b'Rule 1' in response.data
            assert b'Rule 2' in response.data
            
    def test_generate_rule_ai_service_failure(self, authenticated_client, user):
        """Test rule generation when AI service fails."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service failure
            mock_ai_service.generate_multiple_rules.return_value = (None, {'error': 'Service unavailable'})
            mock_ai_service.get_fallback_rule.return_value = 'Fallback rule'
            
            response = authenticated_client.post('/api/folders/generate-rule', data={
                'name': 'Work',
                'folder_type': 'destination',
                'rule_type': 'multiple'
            })
            
            assert response.status_code == 200
            # Should return fallback rule
            assert b'Fallback rule' in response.data
            assert b'AI service unavailable' in response.data
            
    def test_assess_rule_success(self, authenticated_client, user):
        """Test successful rule assessment."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service response
            mock_ai_service.assess_rule_quality.return_value = {
                'score': 85,
                'grade': 'good',
                'feedback': 'Good rule with room for improvement'
            }
            
            response = authenticated_client.post('/api/folders/assess-rule', data={
                'rule_text': 'Move emails from boss@company.com to this folder',
                'folder_name': 'Work',
                'folder_type': 'destination'
            })
            
            assert response.status_code == 200
            # Check that the response contains HTML
            assert b'generated-rule-text' in response.data
            assert b'85%' in response.data
            
    def test_assess_rule_missing_inputs(self, authenticated_client, user):
        """Test rule assessment with missing inputs."""
        response = authenticated_client.post('/api/folders/assess-rule', data={
            'folder_name': 'Work',
            'folder_type': 'destination'
        })
        
        assert response.status_code == 200
        # Should return error in HTML format
        assert b'Rule text is required' in response.data
        
    def test_cache_functionality(self, authenticated_client, user):
        """Test rule caching functionality."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service response
            mock_ai_service.generate_multiple_rules.return_value = (
                [{'text': "Cached rule", 'quality_score': 90}],
                {'total_generated': 1}
            )
            
            # First request - should generate new rule
            response1 = authenticated_client.post('/api/folders/generate-rule', data={
                'name': 'Work',
                'folder_type': 'destination',
                'rule_type': 'multiple'
            })
            
            assert response1.status_code == 200
            assert b'Cached rule' in response1.data
            
            # Verify cache entry was created
            cache_entry = AIRuleCache.query.filter_by(
                user_id=user.id,
                folder_name='Work',
                folder_type='destination'
            ).first()
            assert cache_entry is not None
            assert cache_entry.rule_text == 'Cached rule'
            
            # Second request - should use cached rule
            with patch('app.routes.folders.ai_service') as mock_ai_service:
                response2 = authenticated_client.post('/api/folders/generate-rule', data={
                    'name': 'Work',
                    'folder_type': 'destination',
                    'rule_type': 'multiple'
                })
                
                assert response2.status_code == 200
                assert b'Using cached rule' in response2.data
                
    def test_cache_expiration(self, authenticated_client, user):
        """Test cache expiration functionality."""
        with patch('app.routes.folders.ai_service') as mock_ai_service:
            # Mock AI service response
            mock_ai_service.generate_multiple_rules.return_value = (
                [{'text': "Expired rule", 'quality_score': 90}],
                {'total_generated': 1}
            )
            
            # Create expired cache entry
            from datetime import datetime, timedelta
            expired_entry = AIRuleCache(
                user_id=user.id,
                folder_name='Work',
                folder_type='destination',
                rule_text='Expired rule',
                rule_metadata={'quality_score': 90},
                cache_key='test-key',
                expires_at=datetime.utcnow() - timedelta(hours=1),
                is_active=True
            )
            db.session.add(expired_entry)
            db.session.commit()
            
            # Request should generate new rule despite cache entry
            response = authenticated_client.post('/api/folders/generate-rule', data={
                'name': 'Work',
                'folder_type': 'destination',
                'rule_type': 'multiple'
            })
            
            assert response.status_code == 200
            # Should not show cached message
            assert b'Using cached rule' not in response.data
            
    def test_unauthorized_access(self, client):
        """Test unauthorized access to AI rule endpoints."""
        response = client.post('/api/folders/generate-rule', data={
            'folder_name': 'Work',
            'folder_type': 'destination',
            'rule_type': 'multiple'
        })
        
        # Should be redirected to login
        assert response.status_code == 302
        
    def test_database_error_handling(self, authenticated_client, user):
        """Test handling of database errors."""
        with patch('app.routes.folders.db.session.commit') as mock_commit:
            # Mock database commit failure
            mock_commit.side_effect = Exception("Database error")
            
            with patch('app.routes.folders.ai_service') as mock_ai_service:
                mock_ai_service.generate_multiple_rules.return_value = (
                    [{'text': "Test rule", 'quality_score': 85}],
                    {'total_generated': 1}
                )
                
                response = authenticated_client.post('/api/folders/generate-rule', data={
                    'name': 'Work',
                    'folder_type': 'destination',
                    'rule_type': 'multiple'
                })
                
                assert response.status_code == 200
                # Should return error message
                assert b'An unexpected error occurred' in response.data