from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from sqlalchemy import or_, and_, func from typing import List from app.db.base import get_db from app.models.user import User, Connection from app.models.message import ( Conversation, Message, GroupChat, GroupChatMember, GroupMessage, ) from app.schemas.message import ( MessageCreate, MessageResponse, ConversationResponse, GroupChatCreate, GroupChatResponse, GroupMessageCreate, GroupMessageResponse, ) from app.api.auth import get_current_user router = APIRouter() # Direct Messages @router.get("/conversations", response_model=List[ConversationResponse]) def get_conversations( db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get user's conversations""" conversations = ( db.query(Conversation) .filter( or_( Conversation.participant_1_id == current_user.id, Conversation.participant_2_id == current_user.id, ) ) .order_by(Conversation.updated_at.desc()) .all() ) result = [] for conv in conversations: # Determine other participant other_participant = ( conv.participant_2 if conv.participant_1_id == current_user.id else conv.participant_1 ) # Get last message last_message = ( db.query(Message) .filter(Message.conversation_id == conv.id) .order_by(Message.created_at.desc()) .first() ) # Get unread count unread_count = ( db.query(Message) .filter( Message.conversation_id == conv.id, Message.receiver_id == current_user.id, Message.is_read is False, ) .count() ) conv_response = ConversationResponse( id=conv.id, participant_1_id=conv.participant_1_id, participant_2_id=conv.participant_2_id, created_at=conv.created_at, updated_at=conv.updated_at, other_participant_name=f"{other_participant.first_name} {other_participant.last_name}", other_participant_id=other_participant.id, last_message=last_message.content if last_message else None, unread_count=unread_count, ) result.append(conv_response) return result @router.get("/conversations/{user_id}/messages", response_model=List[MessageResponse]) def get_conversation_messages( user_id: int, skip: int = 0, limit: int = 50, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get messages in a conversation with specific user""" # Check if users are connected connection = ( db.query(Connection) .filter( or_( and_( Connection.sender_id == current_user.id, Connection.receiver_id == user_id, ), and_( Connection.sender_id == user_id, Connection.receiver_id == current_user.id, ), ), Connection.status == "accepted", ) .first() ) if not connection: raise HTTPException(status_code=403, detail="Can only message connections") # Get or create conversation conversation = ( db.query(Conversation) .filter( or_( and_( Conversation.participant_1_id == current_user.id, Conversation.participant_2_id == user_id, ), and_( Conversation.participant_1_id == user_id, Conversation.participant_2_id == current_user.id, ), ) ) .first() ) if not conversation: conversation = Conversation( participant_1_id=min(current_user.id, user_id), participant_2_id=max(current_user.id, user_id), ) db.add(conversation) db.commit() db.refresh(conversation) # Get messages messages = ( db.query(Message) .filter(Message.conversation_id == conversation.id) .order_by(Message.created_at.desc()) .offset(skip) .limit(limit) .all() ) # Mark messages as read db.query(Message).filter( Message.conversation_id == conversation.id, Message.receiver_id == current_user.id, Message.is_read is False, ).update({"is_read": True, "read_at": func.now()}) db.commit() # Add sender names for message in messages: message.sender_name = f"{message.sender.first_name} {message.sender.last_name}" return messages @router.post("/send", response_model=MessageResponse) def send_message( message: MessageCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Send a direct message""" # Check if users are connected connection = ( db.query(Connection) .filter( or_( and_( Connection.sender_id == current_user.id, Connection.receiver_id == message.receiver_id, ), and_( Connection.sender_id == message.receiver_id, Connection.receiver_id == current_user.id, ), ), Connection.status == "accepted", ) .first() ) if not connection: raise HTTPException(status_code=403, detail="Can only message connections") # Get or create conversation conversation = ( db.query(Conversation) .filter( or_( and_( Conversation.participant_1_id == current_user.id, Conversation.participant_2_id == message.receiver_id, ), and_( Conversation.participant_1_id == message.receiver_id, Conversation.participant_2_id == current_user.id, ), ) ) .first() ) if not conversation: conversation = Conversation( participant_1_id=min(current_user.id, message.receiver_id), participant_2_id=max(current_user.id, message.receiver_id), ) db.add(conversation) db.commit() db.refresh(conversation) # Create message db_message = Message( conversation_id=conversation.id, sender_id=current_user.id, receiver_id=message.receiver_id, content=message.content, message_type=message.message_type, file_url=message.file_url, ) db.add(db_message) # Update conversation timestamp conversation.updated_at = func.now() db.commit() db.refresh(db_message) db_message.sender_name = f"{current_user.first_name} {current_user.last_name}" return db_message # Group Chats @router.post("/groups", response_model=GroupChatResponse) def create_group_chat( group_data: GroupChatCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a group chat""" # Create group chat group_chat = GroupChat( name=group_data.name, description=group_data.description, created_by=current_user.id, ) db.add(group_chat) db.commit() db.refresh(group_chat) # Add creator as admin member creator_member = GroupChatMember( group_chat_id=group_chat.id, user_id=current_user.id, is_admin=True ) db.add(creator_member) # Add other members for member_id in group_data.member_ids: if member_id != current_user.id: # Don't add creator twice member = GroupChatMember(group_chat_id=group_chat.id, user_id=member_id) db.add(member) db.commit() group_chat.creator_name = f"{current_user.first_name} {current_user.last_name}" group_chat.member_count = len(group_data.member_ids) + 1 # Include creator return group_chat @router.get("/groups", response_model=List[GroupChatResponse]) def get_my_group_chats( db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """Get user's group chats""" # Get groups where user is a member memberships = ( db.query(GroupChatMember) .filter(GroupChatMember.user_id == current_user.id) .all() ) groups = [] for membership in memberships: group = membership.group_chat if group.is_active: # Get member count member_count = ( db.query(GroupChatMember) .filter(GroupChatMember.group_chat_id == group.id) .count() ) # Get last message last_message = ( db.query(GroupMessage) .filter(GroupMessage.group_chat_id == group.id) .order_by(GroupMessage.created_at.desc()) .first() ) group.creator_name = f"{group.creator.first_name} {group.creator.last_name}" group.member_count = member_count group.last_message = last_message.content if last_message else None groups.append(group) return groups @router.get("/groups/{group_id}/messages", response_model=List[GroupMessageResponse]) def get_group_messages( group_id: int, skip: int = 0, limit: int = 50, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get group chat messages""" # Check if user is a member membership = ( db.query(GroupChatMember) .filter( GroupChatMember.group_chat_id == group_id, GroupChatMember.user_id == current_user.id, ) .first() ) if not membership: raise HTTPException(status_code=403, detail="Not a member of this group") # Get messages messages = ( db.query(GroupMessage) .filter(GroupMessage.group_chat_id == group_id) .order_by(GroupMessage.created_at.desc()) .offset(skip) .limit(limit) .all() ) # Add sender names for message in messages: message.sender_name = f"{message.sender.first_name} {message.sender.last_name}" return messages @router.post("/groups/{group_id}/send", response_model=GroupMessageResponse) def send_group_message( group_id: int, message: GroupMessageCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Send a group message""" # Check if user is a member membership = ( db.query(GroupChatMember) .filter( GroupChatMember.group_chat_id == group_id, GroupChatMember.user_id == current_user.id, ) .first() ) if not membership: raise HTTPException(status_code=403, detail="Not a member of this group") # Create message db_message = GroupMessage( group_chat_id=group_id, sender_id=current_user.id, content=message.content, message_type=message.message_type, file_url=message.file_url, ) db.add(db_message) db.commit() db.refresh(db_message) db_message.sender_name = f"{current_user.first_name} {current_user.last_name}" return db_message