"""Message service for RateRight Australian Construction Marketplace"""
import logging
from typing import List, Optional, Dict, Any
from datetime import datetime

from ..extensions import db
from ..models.message import Message, MessageStatus, Conversation
from ..models import User
from ..utils.notification_helpers import NotificationTriggers

logger = logging.getLogger(__name__)


class MessageService:
    """Service for handling messaging operations"""
    
    @staticmethod
    def send_message(
        sender_id: int,
        receiver_id: int,
        text: str = None,
        content: str = None,
        message_type: str = None,
        contract_id: Optional[int] = None,
        job_id: Optional[int] = None
    ) -> Optional[Message]:
        """
        Send a message from one user to another
        
        Args:
            sender_id: ID of the user sending the message
            receiver_id: ID of the user receiving the message
            text: Message content
            contract_id: Optional contract ID for context
            job_id: Optional job ID for context
            
        Returns:
            Created message or None if failed
        """
        try:
            # Validate users exist
            sender = User.query.get(sender_id)
            receiver = User.query.get(receiver_id)
            
            if not sender:
                logger.error(f"Sender not found: {sender_id}")
                return None
                
            if not receiver:
                logger.error(f"Receiver not found: {receiver_id}")
                return None
            
            # Check if either user is deleted
            if sender.is_deleted:
                logger.error(f"Sender account is deleted: {sender_id}")
                return None
            
            if receiver.is_deleted:
                logger.error(f"Receiver account is deleted: {receiver_id}")
                return None
            
            # Don't allow sending messages to self
            if sender_id == receiver_id:
                logger.error(f"User {sender_id} tried to send message to themselves")
                return None
            
            # Use content if provided, otherwise use text
            message_text = content or text
            if not message_text:
                logger.error("No message content provided")
                return None
            
            # Capture sender name for snapshot
            sender_name = f"{sender.first_name} {sender.last_name}"
            
            # Create message
            message = Message(
                sender_id=sender_id,
                receiver_id=receiver_id,
                sender_name=sender_name,  # Store name snapshot
                text=message_text.strip()[:2000],  # Limit message length
                status=MessageStatus.SENT,
                contract_id=contract_id,
                job_id=job_id
            )
            
            db.session.add(message)
            db.session.commit()
            
            # Don't auto-mark as delivered - let tests control the status
            # message.mark_as_delivered()
            
            # Send notification to receiver
            try:
                sender_name = f"{sender.first_name} {sender.last_name}"
                message_preview = text[:50] + "..." if len(text) > 50 else text
                
                NotificationTriggers.message_received(
                    user_id=receiver_id,
                    sender_name=sender_name,
                    message_preview=message_preview,
                    conversation_id=message.id  # Using message ID as conversation reference
                )
                
                logger.info(f"Message notification sent to user {receiver_id}")
                
            except Exception as notification_error:
                # Don't fail message sending if notification fails
                logger.error(f"Failed to send message notification: {notification_error}")
            
            logger.info(f"Message sent from user {sender_id} to user {receiver_id}")
            return message
            
        except Exception as e:
            logger.error(f"Failed to send message: {e}")
            db.session.rollback()
            return None
    
    @staticmethod
    def get_user_conversations(user_id: int, limit: int = 20) -> List[Dict[str, Any]]:
        """
        Get list of conversations for a user
        
        Args:
            user_id: ID of the user
            limit: Maximum number of conversations to return
            
        Returns:
            List of conversation summaries
        """
        try:
            # Get all messages where user is sender or receiver
            user_messages = Message.query.filter(
                db.or_(Message.sender_id == user_id, Message.receiver_id == user_id)
            ).order_by(Message.created_at.desc()).all()
            
            conversations = {}
            current_user = User.query.get(user_id)

            for message in user_messages:
                partner_id = message.receiver_id if message.sender_id == user_id else message.sender_id
                partner = User.query.get(partner_id)

                if not partner:
                    continue

                # Get partner name - use snapshot if available, otherwise get from user
                if message.sender_id == user_id:
                    # Partner is receiver, no direct snapshot, get from user
                    partner_name = f"{partner.first_name} {partner.last_name}"
                else:
                    # Partner is sender, use sender_name snapshot if available
                    partner_name = message.get_sender_name()

                # Determine which rating to show (same logic you already have)
                if partner.role == "worker":
                    rating_to_show = float(partner.average_rating or 0.0)
                else:
                    rating_to_show = float(partner.average_rating or 0.0)

                # If we've already seen this partner, compare timestamps and keep the latest message only
                if partner_id in conversations:
                    if message.created_at > conversations[partner_id]['created_at']:
                        conversations[partner_id]['last_message'] = message.to_dict(include_sender_info=True)
                        conversations[partner_id]['created_at'] = message.created_at
                        # Update partner name in case it changed (deleted user)
                        if message.sender_id == partner_id:
                            conversations[partner_id]['partner']['name'] = message.get_sender_name()
                else:
                    # First time seeing this partner
                    conversations[partner_id] = {
                        'partner': {
                            'id': partner.id,
                            'name': partner_name,
                            'role': partner.role,
                            'rating': rating_to_show,
                            'profile_picture': partner.profile_picture,
                            'is_deleted': partner.is_deleted if hasattr(partner, 'is_deleted') else False
                        },
                        'last_message': message.to_dict(include_sender_info=True),
                        'unread_count': 0,
                        'created_at': message.created_at
                    }
            
            # Calculate unread counts
            for partner_id, conv in conversations.items():
                conversation = Conversation(user_id, partner_id)
                conv['unread_count'] = conversation.get_unread_count(user_id)
            
            # Sort by most recent message and limit
            sorted_conversations = sorted(
                conversations.values(),
                key=lambda x: x['created_at'],
                reverse=True
            )
            
            return sorted_conversations[:limit]
            
        except Exception as e:
            logger.error(f"Failed to get user conversations: {e}")
            return []
    
    @staticmethod
    def get_conversation_messages(
        user_id: int,
        partner_id: int,
        limit: int = 50,
        offset: int = 0
    ) -> Dict[str, Any]:
        """
        Get messages in a conversation between two users
        """
        try:
            # Verify both users exist
            user = User.query.get(user_id)
            partner = User.query.get(partner_id)
            
            if not user or not partner:
                return {'error': 'User not found', 'messages': []}
            
            conversation = Conversation(user_id, partner_id)
            messages = conversation.get_messages(limit, offset)
            
            # Convert messages to dict format
            message_list = []
            for message in reversed(messages):  # chronological order
                message_dict = message.to_dict(include_sender_info=True)
                message_list.append(message_dict)
            
            # Mark messages as read for current user
            unread_count = conversation.mark_all_as_read(user_id)
            
            return {
                'success': True,
                'messages': message_list,
                'partner': {
                    'id': partner.id,
                    'name': f"{partner.first_name} {partner.last_name}",
                    'role': partner.role,
                    'rating': float(partner.average_rating or 0.0),
                    'profile_picture': partner.profile_picture
                },
                'has_more': len(messages) == limit,
                'marked_as_read': unread_count,
                'total_messages': len(message_list)
            }

        except Exception as e:
            logger.error(f"Failed to get conversation messages: {e}")
            return {'success': False, 'error': str(e), 'messages': []}

    
    @staticmethod
    def get_unread_message_count(user_id: int) -> int:
        """
        Get total unread message count for user
        
        Args:
            user_id: ID of the user
            
        Returns:
            Count of unread messages
        """
        try:
            return Message.query.filter(
                Message.receiver_id == user_id,
                Message.status != MessageStatus.READ
            ).count()
            
        except Exception as e:
            logger.error(f"Failed to get unread message count: {e}")
            return 0
    
    @staticmethod
    def mark_message_as_read(message_id: int, user_id: int) -> bool:
        """
        Mark specific message as read
        
        Args:
            message_id: ID of the message
            user_id: ID of the user (must be receiver)
            
        Returns:
            True if successful, False otherwise
        """
        try:
            message = Message.query.filter(
                Message.id == message_id,
                Message.receiver_id == user_id
            ).first()
            
            if message:
                message.mark_as_read()
                return True
            
            return False
            
        except Exception as e:
            logger.error(f"Failed to mark message as read: {e}")
            return False
    
    @staticmethod
    def delete_message(message_id: int, user_id: int) -> bool:
        """
        Delete a message (only sender can delete)
        
        Args:
            message_id: ID of the message
            user_id: ID of the user (must be sender)
            
        Returns:
            True if successful, False otherwise
        """
        try:
            message = Message.query.filter(
                Message.id == message_id,
                Message.sender_id == user_id
            ).first()
            
            if message:
                db.session.delete(message)
                db.session.commit()
                logger.info(f"Message {message_id} deleted by user {user_id}")
                return True
            
            return False
            
        except Exception as e:
            logger.error(f"Failed to delete message: {e}")
            db.session.rollback()
            return False
    
    @staticmethod
    def search_messages(user_id: int, query: str, limit: int = 20) -> List[Dict[str, Any]]:
        """
        Search messages for a user
        
        Args:
            user_id: ID of the user
            query: Search query
            limit: Maximum number of results
            
        Returns:
            List of matching messages
        """
        try:
            messages = Message.query.filter(
                db.or_(Message.sender_id == user_id, Message.receiver_id == user_id),
                Message.text.ilike(f'%{query}%')
            ).order_by(Message.created_at.desc()).limit(limit).all()
            
            return [message.to_dict(include_sender_info=True, include_receiver_info=True) 
                   for message in messages]
            
        except Exception as e:
            logger.error(f"Failed to search messages: {e}")
            return []
    
    @staticmethod
    def mark_as_read(message_id: int, user_id: int) -> bool:
        """
        Mark a message as read (alias for mark_message_as_read)
        
        Args:
            message_id: ID of the message
            user_id: ID of the user (must be receiver)
            
        Returns:
            True if successful, False otherwise
        """
        return MessageService.mark_message_as_read(message_id, user_id)
    
    @staticmethod
    def mark_conversation_as_read(user_id: int, partner_id: int) -> bool:
        """
        Mark all messages in a conversation as read
        
        Args:
            user_id: ID of the current user
            partner_id: ID of the conversation partner
            
        Returns:
            True if successful, False otherwise
        """
        try:
            conversation = Conversation(user_id, partner_id)
            marked_count = conversation.mark_all_as_read(user_id)
            logger.info(f"Marked {marked_count} messages as read for user {user_id}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to mark conversation as read: {e}")
            return False
    
    @staticmethod
    def get_unread_count(user_id: int) -> int:
        """
        Get unread message count (alias for get_unread_message_count)
        
        Args:
            user_id: ID of the user
            
        Returns:
            Count of unread messages
        """
        return MessageService.get_unread_message_count(user_id)
    
    @staticmethod
    def block_user(user_id: int, blocked_user_id: int) -> bool:
        """
        Block a user from sending messages (placeholder implementation)
        
        Args:
            user_id: ID of the user doing the blocking
            blocked_user_id: ID of the user being blocked
            
        Returns:
            True if successful, False otherwise
        """
        # This is a placeholder - in a full implementation you'd maintain a blocked users table
        logger.warning(f"Block user functionality not fully implemented: {user_id} blocking {blocked_user_id}")
        return False
    
    @staticmethod
    def bulk_mark_as_read(user_id: int, message_ids: List[int]) -> int:
        """
        Mark multiple messages as read
        
        Args:
            user_id: ID of the user (must be receiver)
            message_ids: List of message IDs to mark as read
            
        Returns:
            Number of messages marked as read
        """
        try:
            messages = Message.query.filter(
                Message.id.in_(message_ids),
                Message.receiver_id == user_id,
                Message.status != MessageStatus.READ
            ).all()
            
            marked_count = 0
            for message in messages:
                message.mark_as_read()
                marked_count += 1
            
            logger.info(f"Bulk marked {marked_count} messages as read for user {user_id}")
            return marked_count
            
        except Exception as e:
            logger.error(f"Failed to bulk mark messages as read: {e}")
            return 0


# Singleton instance
message_service = MessageService()
