import pytest
import imaplib
from unittest.mock import patch, Mock
from app.imap_service import IMAPService
from app.models import User, db
from app import create_app

class TestIMAPService:
    def test_init_with_user(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        user.imap_config = {'server': 'test.com', 'port': 993}
        imap_service = IMAPService(user)
        assert imap_service.user == user
        assert imap_service.config == {'server': 'test.com', 'port': 993}
    
    def test_init_without_config(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        imap_service = IMAPService(user)
        assert imap_service.user == user
        assert imap_service.config == {}
    
    def test_test_connection_success(self,  app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        user.imap_config = {'server': 'localhost', 'port': 5143, 'username': 'user1@example.com', 'password': 'password1', 'use_ssl': True}
        imap_service = IMAPService(user)
        
        # Mock the IMAP connection
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.login.return_value = None
            mock_connection.select.return_value = ('OK', [b'1'])
            mock_connection.close.return_value = None
            mock_connection.logout.return_value = None
            
            success, message = imap_service.test_connection()
            
            assert message == "Connection successful"
            assert success is True
    
    def test_test_connection_failure(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        user.imap_config = {'server': 'test.com', 'port': 993, 'username': 'user', 'password': 'pass'}
        imap_service = IMAPService(user)
        
        # Mock the IMAP connection to raise a generic exception
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.login.side_effect = Exception("Connection failed")
            
            success, message = imap_service.test_connection()
            
            assert success is False
            assert "Connection error: Connection failed" in message
    
    def test_test_connection_imap_error(self, app):
        # Mock the IMAP connection to raise an IMAP error
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.login.side_effect = imaplib.IMAP4.error("IMAP login failed")
            
            user = User(email='test@example.com', first_name='Test', last_name='User')
            user.imap_config = {'server': 'test.com', 'port': 993, 'username': 'user', 'password': 'pass'}
            imap_service = IMAPService(user)
            
            success, message = imap_service.test_connection()
            
            assert success is False
            assert "IMAP connection error: IMAP login failed" in message
    
    def test_test_connection_no_config(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        imap_service = IMAPService(user)
        
        success, message = imap_service.test_connection()
        
        assert success is False
        assert message == "No IMAP configuration found"
    
    def test_get_folders_success(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        user.imap_config = {'server': 'test.com', 'port': 993, 'username': 'user', 'password': 'pass'}
        imap_service = IMAPService(user)
        
        # Mock the IMAP connection
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.list.return_value = ('OK', [
                b'(\\HasNoChildren) "INBOX"',
                b'(\\HasNoChildren) "Sent"',
                b'(\\HasNoChildren) "Drafts"'
            ])
            
            folders = imap_service.get_folders()
            
            assert len(folders) == 3
            assert folders[0]['name'] == 'INBOX'
            assert folders[1]['name'] == 'Sent'
            assert folders[2]['name'] == 'Drafts'
    
    def test_get_folders_empty(self, app):
        # Mock the IMAP connection to return an empty list of folders
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.list.return_value = ('OK', [])
            mock_connection.login.return_value = None
            mock_connection.select.return_value = ('OK', [b'1'])
            mock_connection.close.return_value = None
            mock_connection.logout.return_value = None
            
            user = User(email='test@example.com', first_name='Test', last_name='User')
            user.imap_config = {'server': 'test.com', 'port': 993, 'username': 'user', 'password': 'pass'}
            imap_service = IMAPService(user)
            
            folders = imap_service.get_folders()
            
            assert len(folders) == 0
    
    def test_get_folders_error(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        user.imap_config = {'server': 'test.com', 'port': 993, 'username': 'user', 'password': 'pass'}
        imap_service = IMAPService(user)
        
        # Mock the IMAP connection to raise an exception
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.login.return_value = None
            mock_connection.list.side_effect = Exception("IMAP error")
            
            folders = imap_service.get_folders()
            
            assert len(folders) == 0
    
    def test_get_folders_no_config(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        imap_service = IMAPService(user)
        
        folders = imap_service.get_folders()
        
        assert len(folders) == 0
    
    def test_sync_folders_success(self, app):
        with app.app_context():
            user = User(email='test@example.com', first_name='Test', last_name='User')
            user.set_password('testpassword')
            db.session.add(user)
            db.session.commit()
            user.imap_config = {'server': 'localhost', 'port': 5143, 'username': 'user1@example.com', 'password': 'password1'}
            imap_service = IMAPService(user)
        
        # Mock the IMAP connection
        with patch('app.imap_service.imaplib.IMAP4_SSL') as mock_imap_ssl:
            mock_connection = mock_imap_ssl.return_value
            mock_connection.list.return_value = ('OK', [
                b'(\\HasNoChildren) "CustomFolder1"',
                b'(\\HasNoChildren) "CustomFolder2"'
            ])
            
            # Mock the get_folder_email_count method
            with patch.object(imap_service, 'get_folder_email_count') as mock_count:
                mock_count.return_value = 5
                
                # Mock the get_recent_emails method
                with patch.object(imap_service, 'get_recent_emails') as mock_recent:
                    mock_recent.return_value = [
                        {'subject': 'Test Email 1', 'date': '2023-01-01T12:00:00'},
                        {'subject': 'Test Email 2', 'date': '2023-01-02T12:00:00'}
                    ]
                    
                    # Get fresh user from database and create new IMAPService to fix DetachedInstanceError
                    fresh_user = User.query.filter_by(email='test@example.com').first()
                    fresh_user.imap_config = {'server': 'localhost', 'port': 5143, 'username': 'user1@example.com', 'password': 'password1'}
                    fresh_imap_service = IMAPService(fresh_user)
                    
                    # Mock the get_folder_email_count method on the new service
                    with patch.object(fresh_imap_service, 'get_folder_email_count') as mock_count:
                        mock_count.return_value = 5
                        
                        # Mock the get_recent_emails method on the new service
                        with patch.object(fresh_imap_service, 'get_recent_emails') as mock_recent:
                            mock_recent.return_value = [
                                {'subject': 'Test Email 1', 'date': '2023-01-01T12:00:00'},
                                {'subject': 'Test Email 2', 'date': '2023-01-02T12:00:00'}
                            ]
                            
                            success, message = fresh_imap_service.sync_folders()
                            
                            assert success is True
                            assert "Successfully synced 2 folders" in message
    
    def test_sync_folders_no_config(self, app):
        user = User(email='test@example.com', first_name='Test', last_name='User')
        imap_service = IMAPService(user)
        
        success, message = imap_service.sync_folders()
        
        assert success is False
        assert message == "No IMAP configuration found"
    
    def test_sync_folders_no_folders(self, app):
        with app.app_context():
            user = User(email='test@example.com', first_name='Test', last_name='User')
            user.set_password('testpassword')
            db.session.add(user)
            db.session.commit()
            user.imap_config = {'server': 'localhost', 'port': 5143, 'username': 'user1@example.com', 'password': 'password1', 'use_ssl': False}
            imap_service = IMAPService(user)
            
            # Mock the get_folders method to return an empty list
            with patch.object(imap_service, 'get_folders', return_value=[]):
                success, message = imap_service.sync_folders()
                
                assert success is False
                assert message == "No folders found on IMAP server"
    
    def test_sync_folders_exception(self, app):
        with app.app_context():
             
            user = User(email='test@example.com', first_name='Test', last_name='User')
            user.set_password('testpassword')
            db.session.add(user)
            db.session.commit()
            user.imap_config = {'server': 'localhost', 'port': 5143, 'username': 'user1@example.com', 'password': 'password1', 'use_ssl': False }
            imap_service = IMAPService(user)
            
            # Mock the get_folders method to raise an exception
            with patch.object(imap_service, 'get_folders', side_effect=Exception("IMAP server error")):
                success, message = imap_service.sync_folders()
                
                assert success is False
                assert "Sync error: IMAP server error" in message