""" AnyDesk Remote Support Router REST API endpoints for managing remote support sessions """ import logging from typing import Optional from fastapi import APIRouter, HTTPException from fastapi.responses import JSONResponse from app.models.schemas import ( AnyDeskSessionCreate, AnyDeskSession, AnyDeskSessionDetail, AnyDeskSessionHistory, AnyDeskSessionWithWorklog ) from app.services.anydesk import AnyDeskService from app.core.database import execute_query logger = logging.getLogger(__name__) router = APIRouter() anydesk_service = AnyDeskService() # ===================================================== # Session Management Endpoints # ===================================================== @router.post("/anydesk/start-session", response_model=AnyDeskSession, tags=["Remote Support"]) async def start_remote_session(session_data: AnyDeskSessionCreate): """ Start a new AnyDesk remote support session - **customer_id**: Required - Customer to provide support to - **contact_id**: Optional - Specific contact person - **sag_id**: Optional - Link to case/ticket for time tracking - **description**: Optional - Purpose of session - **created_by_user_id**: Optional - User initiating session Returns session details with access link for sharing with customer """ try: logger.info(f"🔗 Starting AnyDesk session for customer {session_data.customer_id}") # Verify customer exists cust_query = "SELECT id FROM customers WHERE id = %s" customer = execute_query(cust_query, (session_data.customer_id,)) if not customer: raise HTTPException(status_code=404, detail="Customer not found") # Verify contact exists if provided if session_data.contact_id: contact_query = "SELECT id FROM contacts WHERE id = %s" contact = execute_query(contact_query, (session_data.contact_id,)) if not contact: raise HTTPException(status_code=404, detail="Contact not found") # Verify sag exists if provided if session_data.sag_id: sag_query = "SELECT id FROM sag_sager WHERE id = %s" sag = execute_query(sag_query, (session_data.sag_id,)) if not sag: raise HTTPException(status_code=404, detail="Case not found") # Create session via AnyDesk service result = await anydesk_service.create_session( customer_id=session_data.customer_id, contact_id=session_data.contact_id, sag_id=session_data.sag_id, description=session_data.description, created_by_user_id=session_data.created_by_user_id ) if "error" in result: raise HTTPException(status_code=400, detail=result["error"]) return result except HTTPException: raise except Exception as e: logger.error(f"Error starting session: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/anydesk/sessions/{session_id}", response_model=AnyDeskSessionDetail, tags=["Remote Support"]) async def get_session_details(session_id: int): """ Get details of a specific AnyDesk session Includes current status, duration, and linked entities (contact, customer, case) """ try: query = """ SELECT s.id, s.anydesk_session_id, s.customer_id, s.contact_id, s.sag_id, s.session_link, s.status, s.started_at, s.ended_at, s.duration_minutes, s.created_by_user_id, s.created_at, s.updated_at, c.first_name || ' ' || c.last_name as contact_name, cust.name as customer_name, sag.title as sag_title, u.full_name as created_by_user_name, s.device_info, s.metadata FROM anydesk_sessions s LEFT JOIN contacts c ON s.contact_id = c.id LEFT JOIN customers cust ON s.customer_id = cust.id LEFT JOIN sag_sager sag ON s.sag_id = sag.id LEFT JOIN users u ON s.created_by_user_id = u.id WHERE s.id = %s """ result = execute_query(query, (session_id,)) if not result: raise HTTPException(status_code=404, detail="Session not found") session = result[0] return AnyDeskSessionDetail(**session) except HTTPException: raise except Exception as e: logger.error(f"Error fetching session: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/anydesk/sessions/{session_id}/end", tags=["Remote Support"]) async def end_remote_session(session_id: int): """ End a remote support session and calculate duration Returns completed session with duration in minutes and hours """ try: logger.info(f"🛑 Ending AnyDesk session {session_id}") result = await anydesk_service.end_session(session_id) if "error" in result: raise HTTPException(status_code=400, detail=result["error"]) return JSONResponse(content=result) except HTTPException: raise except Exception as e: logger.error(f"Error ending session: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/anydesk/sessions", response_model=AnyDeskSessionHistory, tags=["Remote Support"]) async def get_session_history( contact_id: Optional[int] = None, customer_id: Optional[int] = None, sag_id: Optional[int] = None, limit: int = 50, offset: int = 0 ): """ Get session history filtered by contact, customer, or case At least one filter parameter should be provided. Results are paginated and sorted by date (newest first). - **contact_id**: Get all sessions for a specific contact - **customer_id**: Get all sessions for a company - **sag_id**: Get sessions linked to a specific case - **limit**: Number of sessions per page (default 50, max 100) - **offset**: Pagination offset """ try: if not any([contact_id, customer_id, sag_id]): raise HTTPException( status_code=400, detail="At least one filter (contact_id, customer_id, or sag_id) is required" ) # Validate limit if limit > 100: limit = 100 result = await anydesk_service.get_session_history( contact_id=contact_id, customer_id=customer_id, sag_id=sag_id, limit=limit, offset=offset ) if "error" in result: raise HTTPException(status_code=400, detail=result["error"]) # Enrich session data with contact/customer/user names enriched_sessions = [] for session in result.get("sessions", []): enriched_sessions.append(AnyDeskSessionDetail(**session)) return AnyDeskSessionHistory( sessions=enriched_sessions, total=result["total"], limit=limit, offset=offset ) except HTTPException: raise except Exception as e: logger.error(f"Error fetching session history: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) # ===================================================== # Worklog Integration Endpoints # ===================================================== @router.get("/anydesk/sessions/{session_id}/worklog-suggestion", tags=["Remote Support", "Time Tracking"]) async def suggest_worklog_from_session(session_id: int): """ Get suggested worklog entry from a completed session Calculates billable hours from session duration and provides a pre-filled worklog suggestion for review/approval. The worklog still needs to be created separately via the timetracking/worklog endpoints after user approval. """ try: # Get session query = """ SELECT id, duration_minutes, customer_id, sag_id, contact_id, started_at, ended_at, status FROM anydesk_sessions WHERE id = %s """ result = execute_query(query, (session_id,)) if not result: raise HTTPException(status_code=404, detail="Session not found") session = result[0] if session["status"] != "completed": raise HTTPException( status_code=400, detail=f"Cannot suggest worklog for non-completed session (status: {session['status']})" ) if not session["duration_minutes"]: raise HTTPException( status_code=400, detail="Session duration not calculated yet" ) # Build worklog suggestion duration_hours = round(session["duration_minutes"] / 60, 2) suggestion = { "session_id": session_id, "duration_minutes": session["duration_minutes"], "duration_hours": duration_hours, "start_time": str(session["started_at"]), "end_time": str(session["ended_at"]), "description": f"Remote support session via AnyDesk", "work_type": "remote_support", "billable": True, "linked_to": { "customer_id": session["customer_id"], "contact_id": session["contact_id"], "sag_id": session["sag_id"] } } logger.info(f"Generated worklog suggestion for session {session_id}: {duration_hours}h") return JSONResponse(content=suggestion) except HTTPException: raise except Exception as e: logger.error(f"Error generating worklog suggestion: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) # ===================================================== # Status & Analytics Endpoints # ===================================================== @router.get("/anydesk/stats", tags=["Remote Support", "Analytics"]) async def get_anydesk_stats(): """ Get AnyDesk integration statistics - Total sessions today/this week/this month - Active sessions count - Average session duration - Most supported customers """ try: stats = { "sessions_today": 0, "sessions_this_week": 0, "sessions_this_month": 0, "active_sessions": 0, "average_duration_minutes": 0, "total_support_hours": 0 } # Get today's sessions query = """ SELECT COUNT(*) as count FROM anydesk_sessions WHERE DATE(started_at) = CURRENT_DATE """ result = execute_query(query) stats["sessions_today"] = result[0]["count"] if result else 0 # Get this week's sessions query = """ SELECT COUNT(*) as count FROM anydesk_sessions WHERE started_at >= CURRENT_DATE - INTERVAL '7 days' """ result = execute_query(query) stats["sessions_this_week"] = result[0]["count"] if result else 0 # Get this month's sessions query = """ SELECT COUNT(*) as count FROM anydesk_sessions WHERE started_at >= DATE_TRUNC('month', CURRENT_DATE) """ result = execute_query(query) stats["sessions_this_month"] = result[0]["count"] if result else 0 # Get active sessions query = """ SELECT COUNT(*) as count FROM anydesk_sessions WHERE status = 'active' """ result = execute_query(query) stats["active_sessions"] = result[0]["count"] if result else 0 # Get average duration query = """ SELECT AVG(duration_minutes) as avg_duration FROM anydesk_sessions WHERE status = 'completed' AND duration_minutes IS NOT NULL """ result = execute_query(query) stats["average_duration_minutes"] = round(result[0]["avg_duration"], 1) if result and result[0]["avg_duration"] else 0 # Get total support hours this month query = """ SELECT SUM(duration_minutes) as total_minutes FROM anydesk_sessions WHERE status = 'completed' AND started_at >= DATE_TRUNC('month', CURRENT_DATE) """ result = execute_query(query) total_minutes = result[0]["total_minutes"] if result and result[0]["total_minutes"] else 0 stats["total_support_hours"] = round(total_minutes / 60, 2) return JSONResponse(content=stats) except Exception as e: logger.error(f"Error calculating stats: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/anydesk/health", tags=["Remote Support", "Health"]) async def anydesk_health_check(): """ Health check for AnyDesk integration Returns configuration status, API connectivity, and last sync time """ return JSONResponse(content={ "service": "AnyDesk Remote Support", "status": "operational", "configured": bool(anydesk_service.api_token and anydesk_service.license_id), "dry_run_mode": anydesk_service.dry_run, "read_only_mode": anydesk_service.read_only, "auto_start_enabled": anydesk_service.auto_start })