""" Mail API — unified inbox, send, and email detail endpoints. """ import logging from typing import Optional, List from fastapi import APIRouter, HTTPException, Query from .models import ( EmailComposeRequest, EmailSendResult, ) from .mail_db import ( get_unified_inbox, get_email, mark_email_read, mark_email_starred, log_mail_audit, ) from .aggregator import get_mail_aggregator logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/mail", tags=["Mail"]) @router.get("/inbox", response_model=List[dict]) async def get_inbox( user_id: str = Query(..., description="User ID"), account_ids: Optional[str] = Query(None, description="Comma-separated account IDs"), categories: Optional[str] = Query(None, description="Comma-separated categories"), is_read: Optional[bool] = Query(None), is_starred: Optional[bool] = Query(None), limit: int = Query(50, ge=1, le=200), offset: int = Query(0, ge=0), ): """Get unified inbox with all accounts aggregated.""" # Parse comma-separated values account_id_list = account_ids.split(",") if account_ids else None category_list = categories.split(",") if categories else None emails = await get_unified_inbox( user_id=user_id, account_ids=account_id_list, categories=category_list, is_read=is_read, is_starred=is_starred, limit=limit, offset=offset, ) return emails @router.get("/inbox/{email_id}", response_model=dict) async def get_email_detail( email_id: str, user_id: str = Query(..., description="User ID"), ): """Get a single email with full details.""" email_data = await get_email(email_id, user_id) if not email_data: raise HTTPException(status_code=404, detail="Email not found") # Mark as read await mark_email_read(email_id, user_id, is_read=True) return email_data @router.post("/inbox/{email_id}/read") async def mark_read( email_id: str, user_id: str = Query(..., description="User ID"), is_read: bool = Query(True), ): """Mark email as read/unread.""" success = await mark_email_read(email_id, user_id, is_read) if not success: raise HTTPException(status_code=500, detail="Failed to update email") return {"status": "updated", "is_read": is_read} @router.post("/inbox/{email_id}/star") async def mark_starred( email_id: str, user_id: str = Query(..., description="User ID"), is_starred: bool = Query(True), ): """Mark email as starred/unstarred.""" success = await mark_email_starred(email_id, user_id, is_starred) if not success: raise HTTPException(status_code=500, detail="Failed to update email") return {"status": "updated", "is_starred": is_starred} @router.post("/send", response_model=EmailSendResult) async def send_email( request: EmailComposeRequest, user_id: str = Query(..., description="User ID"), ): """Send an email.""" aggregator = get_mail_aggregator() result = await aggregator.send_email( account_id=request.account_id, user_id=user_id, request=request, ) if result.success: await log_mail_audit( user_id=user_id, action="email_sent", entity_type="email", details={ "account_id": request.account_id, "to": request.to, "subject": request.subject, }, ) return result