"""Test messaging system"""
import pytest
from datetime import datetime, timedelta
from app import create_app
from app.extensions import db
from app.models import User
from app.models.message import Message, Conversation
from app.services.message_service import MessageService
from app.utils.message_validators import validate_message_content, sanitize_message


@pytest.fixture
def app():
    """Create and configure test app"""
    app = create_app()
    app.config.update({
        'TESTING': True,
        'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
        'SECRET_KEY': 'test-secret-key'
    })
    
    with app.app_context():
        db.create_all()
        yield app
        db.session.remove()
        db.drop_all()


@pytest.fixture
def client(app):
    """Create test client"""
    return app.test_client()


@pytest.fixture
def sample_users(app):
    """Create sample users for testing"""
    sender = User(
        email='sender@test.com',
        first_name='Test',
        last_name='Sender',
        role='contractor',
        phone_number='0400000001',
        location='Sydney, NSW',
        abn_number='12345678901',
        is_active=True,
        privacy_consent=True,
        terms_accepted=True,
        terms_accepted_date=datetime.utcnow()
    )
    sender.set_password('password123')
    
    receiver = User(
        email='receiver@test.com',
        first_name='Test',
        last_name='Receiver',
        role='worker',
        phone_number='0400000002',
        location='Melbourne, VIC',
        abn_number='12345678902',
        is_active=True,
        privacy_consent=True,
        terms_accepted=True,
        terms_accepted_date=datetime.utcnow()
    )
    receiver.set_password('password123')
    
    db.session.add_all([sender, receiver])
    db.session.commit()
    
    return {
        'sender': sender,
        'receiver': receiver
    }


def login_user(client, email, password):
    """Helper function to log in a user"""
    return client.post('/login', data={
        'email': email,
        'password': password
    }, follow_redirects=True)


class TestMessagingSystem:
    """Test messaging system functionality"""
    
    def test_send_message_basic(self, app, sample_users):
        """Test basic message sending"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send message
            message = service.send_message(
                sender_id=sender.id,
                receiver_id=receiver.id,
                content='Hello, this is a test message',
                message_type='text'
            )
            
            assert message is not None
            assert message.sender_id == sender.id
            assert message.receiver_id == receiver.id
            assert message.content == 'Hello, this is a test message'
            assert message.message_type == 'text'
            assert message.status == 'sent'
    
    def test_send_message_via_api(self, client, sample_users):
        """Test sending message via REST API"""
        # Login as sender
        login_user(client, 'sender@test.com', 'password123')
        
        # Send message via API
        response = client.post('/api/messages/', json={
            'receiver_id': sample_users['receiver'].id,
            'content': 'API message test',
            'message_type': 'text'
        })
        
        assert response.status_code == 201
        
        data = response.get_json()
        assert data['success'] is True
        assert 'message_id' in data
        
        # Verify message was created
        message = Message.query.get(data['message_id'])
        assert message is not None
        assert message.content == 'API message test'
    
    def test_get_user_conversations(self, app, sample_users):
        """Test retrieving user conversations"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send a few messages to create conversation
            service.send_message(sender.id, receiver.id, 'First message')
            service.send_message(receiver.id, sender.id, 'Reply message')
            service.send_message(sender.id, receiver.id, 'Another message')
            
            # Get conversations for sender
            conversations = service.get_user_conversations(sender.id)
            
            assert len(conversations) == 1
            
            conversation = conversations[0]
            assert conversation['other_user_id'] == receiver.id
            assert conversation['other_user_name'] == f'{receiver.first_name} {receiver.last_name}'
            assert conversation['last_message_content'] == 'Another message'
            assert conversation['unread_count'] >= 0
    
    def test_get_conversation_messages(self, app, sample_users):
        """Test retrieving messages from a conversation"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send messages
            messages_sent = [
                'First message',
                'Second message', 
                'Third message'
            ]
            
            for content in messages_sent:
                service.send_message(sender.id, receiver.id, content)
            
            # Get conversation messages
            messages = service.get_conversation_messages(sender.id, receiver.id)
            
            assert len(messages) == 3
            
            # Should be ordered by timestamp (oldest first for conversation)
            assert messages[0].content == 'First message'
            assert messages[1].content == 'Second message'
            assert messages[2].content == 'Third message'
    
    def test_mark_message_as_read(self, app, sample_users):
        """Test marking messages as read"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send message
            message = service.send_message(sender.id, receiver.id, 'Test read status')
            
            # Initially should not be read
            assert message.is_read is False
            assert message.read_at is None
            
            # Mark as read
            success = service.mark_as_read(message.id, receiver.id)
            
            assert success is True
            
            # Check status updated
            db.session.refresh(message)
            assert message.is_read is True
            assert message.read_at is not None
    
    def test_mark_conversation_as_read(self, app, sample_users):
        """Test marking entire conversation as read"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send multiple messages
            messages = []
            for i in range(3):
                msg = service.send_message(sender.id, receiver.id, f'Message {i+1}')
                messages.append(msg)
            
            # Mark conversation as read by receiver
            success = service.mark_conversation_as_read(sender.id, receiver.id, receiver.id)
            
            assert success is True
            
            # All messages should now be marked as read
            for message in messages:
                db.session.refresh(message)
                assert message.is_read is True
    
    def test_message_search(self, app, sample_users):
        """Test searching messages"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send messages with searchable content
            messages = [
                'Looking for plumbing work in Sydney',
                'Available for electrical jobs',
                'Sydney construction project available',
                'Melbourne building work needed'
            ]
            
            for content in messages:
                service.send_message(sender.id, receiver.id, content)
            
            # Search for messages containing "Sydney"
            results = service.search_messages(receiver.id, 'Sydney')
            
            assert len(results) == 2
            
            # Check that results contain Sydney
            for result in results:
                assert 'Sydney' in result.content
    
    def test_get_unread_message_count(self, app, sample_users):
        """Test getting unread message count"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send some messages
            service.send_message(sender.id, receiver.id, 'Message 1')
            service.send_message(sender.id, receiver.id, 'Message 2')
            service.send_message(sender.id, receiver.id, 'Message 3')
            
            # Get unread count for receiver
            unread_count = service.get_unread_count(receiver.id)
            
            assert unread_count == 3
            
            # Mark one as read
            message = Message.query.filter_by(receiver_id=receiver.id).first()
            service.mark_as_read(message.id, receiver.id)
            
            # Count should decrease
            unread_count = service.get_unread_count(receiver.id)
            assert unread_count == 2
    
    def test_message_validation(self):
        """Test message content validation"""
        # Valid messages
        assert validate_message_content('Hello, how are you?') is True
        assert validate_message_content('Looking for construction work') is True
        
        # Invalid messages
        assert validate_message_content('') is False  # Empty
        assert validate_message_content('   ') is False  # Only whitespace
        assert validate_message_content('a' * 5001) is False  # Too long
        
        # Messages with profanity or inappropriate content should be flagged
        # (Implementation depends on your validation rules)
    
    def test_message_sanitization(self):
        """Test message content sanitization"""
        # Test HTML sanitization
        dirty_html = '<script>alert("xss")</script>Hello <b>world</b>'
        clean_content = sanitize_message(dirty_html)
        
        assert '<script>' not in clean_content
        assert 'alert(' not in clean_content
        
        # Test that safe HTML is preserved or converted appropriately
        safe_html = 'Hello <b>world</b>'
        clean_safe = sanitize_message(safe_html)
        
        # Depending on implementation, might strip all HTML or allow safe tags
        assert 'Hello' in clean_safe
        assert 'world' in clean_safe
    
    def test_block_user_messaging(self, app, sample_users):
        """Test blocking users from messaging"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Block sender from messaging receiver
            success = service.block_user(receiver.id, sender.id)
            assert success is True
            
            # Try to send message (should fail)
            message = service.send_message(sender.id, receiver.id, 'Blocked message')
            
            assert message is None  # Should not create message when blocked
    
    def test_conversation_threading(self, app, sample_users):
        """Test conversation threading and grouping"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send messages back and forth
            msg1 = service.send_message(sender.id, receiver.id, 'Hello')
            msg2 = service.send_message(receiver.id, sender.id, 'Hi there')
            msg3 = service.send_message(sender.id, receiver.id, 'How are you?')
            
            # Get messages in conversation order
            messages = service.get_conversation_messages(sender.id, receiver.id)
            
            # Should include all messages between these users
            assert len(messages) == 3
            
            # Verify correct ordering (chronological)
            timestamps = [msg.created_at for msg in messages]
            assert timestamps == sorted(timestamps)
    
    def test_message_status_updates(self, app, sample_users):
        """Test message status progression"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send message
            message = service.send_message(sender.id, receiver.id, 'Status test')
            
            # Initially should be 'sent'
            assert message.status == 'sent'
            
            # Mark as delivered (when receiver comes online)
            service.mark_as_delivered(message.id)
            db.session.refresh(message)
            assert message.status == 'delivered'
            
            # Mark as read
            service.mark_as_read(message.id, receiver.id)
            db.session.refresh(message)
            assert message.status == 'read'
            assert message.is_read is True
    
    def test_message_polling_api(self, client, sample_users):
        """Test message polling API endpoint"""
        # Login as receiver
        login_user(client, 'receiver@test.com', 'password123')
        
        # Send a message as sender first
        with client.application.app_context():
            service = MessageService()
            service.send_message(
                sample_users['sender'].id, 
                sample_users['receiver'].id, 
                'Poll test message'
            )
        
        # Poll for new messages
        response = client.get('/api/messages/poll')
        
        assert response.status_code == 200
        
        data = response.get_json()
        assert 'messages' in data
        assert len(data['messages']) >= 1
        
        # Check message content
        message = data['messages'][0]
        assert message['content'] == 'Poll test message'
        assert message['sender_id'] == sample_users['sender'].id
    
    def test_conversation_list_api(self, client, sample_users):
        """Test conversation list API endpoint"""
        # Create some conversations first
        with client.application.app_context():
            service = MessageService()
            service.send_message(
                sample_users['sender'].id,
                sample_users['receiver'].id,
                'Conversation test'
            )
        
        # Login and get conversations
        login_user(client, 'receiver@test.com', 'password123')
        
        response = client.get('/api/messages/conversations')
        
        assert response.status_code == 200
        
        data = response.get_json()
        assert 'conversations' in data
        assert len(data['conversations']) >= 1
        
        conversation = data['conversations'][0]
        assert 'other_user_id' in conversation
        assert 'last_message_content' in conversation
        assert 'unread_count' in conversation
    
    def test_send_message_to_nonexistent_user(self, app, sample_users):
        """Test sending message to user that doesn't exist"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            
            # Try to send message to non-existent user
            message = service.send_message(
                sender_id=sender.id,
                receiver_id=99999,  # Non-existent user
                content='This should fail'
            )
            
            assert message is None
    
    def test_message_timestamps(self, app, sample_users):
        """Test message timestamp handling"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            before_time = datetime.utcnow()
            
            # Send message
            message = service.send_message(sender.id, receiver.id, 'Timestamp test')
            
            after_time = datetime.utcnow()
            
            assert message.created_at is not None
            assert before_time <= message.created_at <= after_time
            
            # Test read timestamp
            service.mark_as_read(message.id, receiver.id)
            db.session.refresh(message)
            
            assert message.read_at is not None
            assert message.read_at >= message.created_at
    
    def test_message_length_limits(self, app, sample_users):
        """Test message length validation"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Test maximum length message
            max_length_content = 'a' * 5000  # Assuming 5000 char limit
            message = service.send_message(sender.id, receiver.id, max_length_content)
            
            assert message is not None
            assert len(message.content) <= 5000
            
            # Test overly long message
            too_long_content = 'a' * 6000
            message = service.send_message(sender.id, receiver.id, too_long_content)
            
            # Should either be rejected or truncated
            assert message is None or len(message.content) <= 5000
    
    def test_bulk_mark_as_read(self, app, sample_users):
        """Test marking multiple messages as read"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send multiple messages
            message_ids = []
            for i in range(5):
                msg = service.send_message(sender.id, receiver.id, f'Bulk test {i+1}')
                message_ids.append(msg.id)
            
            # Mark all as read
            success = service.bulk_mark_as_read(message_ids, receiver.id)
            
            assert success is True
            
            # Verify all are marked as read
            for msg_id in message_ids:
                message = Message.query.get(msg_id)
                assert message.is_read is True
    
    def test_message_soft_delete(self, app, sample_users):
        """Test soft deletion of messages"""
        with app.app_context():
            service = MessageService()
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Send message
            message = service.send_message(sender.id, receiver.id, 'Delete test')
            
            # Soft delete by sender
            success = service.delete_message(message.id, sender.id)
            
            assert success is True
            
            # Message should still exist but be marked as deleted for sender
            db.session.refresh(message)
            assert message.deleted_by_sender is True
            assert message.deleted_by_receiver is False
            
            # Delete by receiver
            success = service.delete_message(message.id, receiver.id)
            
            assert success is True
            
            db.session.refresh(message)
            assert message.deleted_by_receiver is True
    
    def test_conversation_helper_methods(self, app, sample_users):
        """Test Conversation helper class methods"""
        with app.app_context():
            sender = sample_users['sender']
            receiver = sample_users['receiver']
            
            # Test getting conversation ID
            conv_id = Conversation.get_conversation_id(sender.id, receiver.id)
            
            # Should be consistent regardless of order
            conv_id_reverse = Conversation.get_conversation_id(receiver.id, sender.id)
            
            assert conv_id == conv_id_reverse
            
            # Test participants
            participants = Conversation.get_participants(conv_id)
            
            assert len(participants) == 2
            assert sender.id in participants
            assert receiver.id in participants
