from datetime import datetime, timedelta from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import func from sqlalchemy.orm import Session from app.core.database import get_db from app.core.security import get_current_user from app.models.queue import Queue from app.models.user import User, UserRole, UserStatus from app.models.whatsapp import Conversation, ConversationStatus, Message router = APIRouter(prefix="/api/supervisor", tags=["supervisor"]) def require_supervisor(current_user: User = Depends(get_current_user)) -> User: if current_user.role not in [UserRole.ADMIN, UserRole.SUPERVISOR]: raise HTTPException(status_code=403, detail="Supervisor or Admin required") return current_user @router.get("/dashboard") def get_dashboard( db: Session = Depends(get_db), current_user: User = Depends(require_supervisor), ) -> dict: """Get supervisor dashboard data""" now = datetime.utcnow() today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) status_counts = {} for status in ConversationStatus: count = db.query(Conversation).filter(Conversation.status == status).count() status_counts[status.value] = count waiting_conversations = db.query(Conversation).filter( Conversation.status == ConversationStatus.WAITING ).count() active_agents = db.query(User).filter( User.status == UserStatus.ONLINE, User.is_active == True, ).count() total_agents = db.query(User).filter( User.role.in_([UserRole.AGENT, UserRole.SUPERVISOR]), User.is_active == True, ).count() resolved_today = db.query(Conversation).filter( Conversation.resolved_at >= today_start ).count() messages_today = db.query(Message).filter( Message.created_at >= today_start ).count() csat_result = db.query(func.avg(Conversation.csat_score)).filter( Conversation.csat_score != None, Conversation.resolved_at >= today_start, ).scalar() avg_csat = round(float(csat_result), 2) if csat_result else None return { "conversations": { "by_status": status_counts, "waiting": waiting_conversations, "resolved_today": resolved_today, }, "agents": { "online": active_agents, "total": total_agents, }, "messages_today": messages_today, "avg_csat": avg_csat, } @router.get("/agents") def get_agents_status( db: Session = Depends(get_db), current_user: User = Depends(require_supervisor), ) -> list[dict]: """Get detailed agent status for supervisor view""" agents = db.query(User).filter( User.role.in_([UserRole.AGENT, UserRole.SUPERVISOR]), User.is_active == True, ).all() today_start = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) result = [] for agent in agents: active = db.query(Conversation).filter( Conversation.assigned_to == agent.id, Conversation.status == ConversationStatus.ACTIVE, ).count() waiting = db.query(Conversation).filter( Conversation.assigned_to == agent.id, Conversation.status == ConversationStatus.WAITING, ).count() resolved_today = db.query(Conversation).filter( Conversation.assigned_to == agent.id, Conversation.resolved_at >= today_start, ).count() result.append({ "id": str(agent.id), "name": agent.name, "email": agent.email, "role": agent.role.value, "status": agent.status.value, "active_conversations": active, "waiting_conversations": waiting, "resolved_today": resolved_today, }) return result @router.get("/queues") def get_queues_status( db: Session = Depends(get_db), current_user: User = Depends(require_supervisor), ) -> list[dict]: """Get queue status for supervisor view""" queues = db.query(Queue).filter(Queue.is_active == True).all() result = [] for queue in queues: waiting = db.query(Conversation).filter( Conversation.queue_id == queue.id, Conversation.status == ConversationStatus.WAITING, ).count() active = db.query(Conversation).filter( Conversation.queue_id == queue.id, Conversation.status == ConversationStatus.ACTIVE, ).count() online_agents = sum( 1 for qa in queue.agents if qa.user and qa.user.status == UserStatus.ONLINE ) result.append({ "id": str(queue.id), "name": queue.name, "waiting_conversations": waiting, "active_conversations": active, "online_agents": online_agents, "total_agents": len(queue.agents), "sla_first_response": queue.sla_first_response, }) return result @router.get("/conversations/critical") def get_critical_conversations( db: Session = Depends(get_db), current_user: User = Depends(require_supervisor), ) -> list[dict]: """Get conversations that need attention""" now = datetime.utcnow() long_wait = db.query(Conversation).filter( Conversation.status == ConversationStatus.WAITING, Conversation.last_message_at < now - timedelta(minutes=5), ).all() high_priority = db.query(Conversation).filter( Conversation.priority.in_(["high", "urgent"]), Conversation.status.in_([ConversationStatus.WAITING, ConversationStatus.ACTIVE]), ).all() long_wait_ids = {conv.id for conv in long_wait} result = [] seen_ids = set() for conv in long_wait + high_priority: if conv.id in seen_ids: continue seen_ids.add(conv.id) reason = "long_wait" if conv.id in long_wait_ids else "high_priority" result.append({ "id": str(conv.id), "contact_name": conv.contact.name if conv.contact else "Unknown", "contact_phone": conv.contact.phone_number if conv.contact else "", "status": conv.status.value, "priority": conv.priority, "assigned_to": str(conv.assigned_to) if conv.assigned_to else None, "last_message_at": conv.last_message_at.isoformat() if conv.last_message_at else None, "reason": reason, }) return result