"""Message / contact service.""" from __future__ import annotations from datetime import datetime, timezone from typing import Optional from app.core.config import get_settings from app.core.exceptions import ForbiddenException, NotFoundException from app.core.supabase import get_supabase_admin from app.services.email_service import EmailService class MessageService: def __init__(self): self.db = get_supabase_admin() def send_message(self, data: dict) -> dict: # Resolve agency_id and listing title listing_row = ( self.db.table("listings") .select("agency_id, title") .eq("id", data["listing_id"]) .execute() ) if not listing_row.data: raise NotFoundException("Listing not found") agency_id = listing_row.data[0]["agency_id"] listing_title = listing_row.data[0].get("title", "") now = datetime.now(timezone.utc).isoformat() msg_data = { **data, "agency_id": agency_id, "read": False, "created_at": now, } result = self.db.table("messages").insert(msg_data).execute() if not result.data: raise Exception("Failed to send message") # Notify agency via email (non-blocking) try: agency_row = ( self.db.table("agencies") .select("name, email") .eq("id", agency_id) .execute() ) if agency_row.data: agency = agency_row.data[0] settings = get_settings() dashboard_url = f"{settings.FRONTEND_URL}/agency/dashboard" EmailService().send_new_message_notification( to_email=agency["email"], agency_name=agency["name"], sender_name=data.get("name", "Someone"), listing_title=listing_title, dashboard_url=dashboard_url, ) except Exception: pass return result.data[0] def list_messages( self, agency_id: str, user_id: str, user_role: str, read_filter: Optional[bool] = None, page: int = 1, page_size: int = 20, ) -> dict: # Verify ownership if user_role != "admin": agency = ( self.db.table("agencies") .select("user_id") .eq("id", agency_id) .execute() ) if not agency.data or agency.data[0]["user_id"] != user_id: raise ForbiddenException("Not authorized") query = ( self.db.table("messages") .select("*, listings(title)", count="exact") .eq("agency_id", agency_id) ) if read_filter is not None: query = query.eq("read", read_filter) offset = (page - 1) * page_size result = ( query.order("created_at", desc=True) .range(offset, offset + page_size - 1) .execute() ) messages = [] for m in result.data: listings_data = m.pop("listings", None) if listings_data and isinstance(listings_data, dict): m["listing_title"] = listings_data.get("title") messages.append(m) return { "messages": messages, "total": result.count or 0, "page": page, "page_size": page_size, } def mark_read(self, message_id: str, user_id: str, user_role: str, read: bool = True) -> dict: msg = self.db.table("messages").select("*, agencies(user_id)").eq("id", message_id).execute() if not msg.data: raise NotFoundException("Message not found") message = msg.data[0] agencies_data = message.get("agencies") if user_role != "admin": if not agencies_data or agencies_data.get("user_id") != user_id: raise ForbiddenException("Not authorized") result = ( self.db.table("messages") .update({"read": read}) .eq("id", message_id) .execute() ) if not result.data: raise NotFoundException("Message not found") return result.data[0] def get_unread_count(self, agency_id: str) -> int: result = ( self.db.table("messages") .select("id", count="exact") .eq("agency_id", agency_id) .eq("read", False) .execute() ) return result.count or 0 def delete_message(self, message_id: str, user_id: str, user_role: str) -> dict: msg = self.db.table("messages").select("*, agencies(user_id)").eq("id", message_id).execute() if not msg.data: raise NotFoundException("Message not found") message = msg.data[0] agencies_data = message.get("agencies") if user_role != "admin": if not agencies_data or agencies_data.get("user_id") != user_id: raise ForbiddenException("Not authorized") self.db.table("messages").delete().eq("id", message_id).execute() return {"message": "Message deleted"}