import asyncio
from datetime import datetime
from fastapi import FastAPI, WebSocket
from fastapi.responses import JSONResponse
from fastapi.websockets import WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from config import Config
from services.log_utils import Log

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/", response_class=JSONResponse)
async def index_page():
    return {"message": "Mcube Call Service Server is running!"}

@app.get("/health")
async def health_check():
    """Basic health check endpoint."""  # noqa: E501
    return {
        "status": "healthy",
        "timestamp": datetime.now().isoformat(),
        "service": "Mcube Call Service",
        "version": "1.0.0"
    }

@app.get("/health/performance")
async def performance_health_check():
    """Performance health check endpoint."""
    import psutil
    try:
        # Get system metrics
        cpu_percent = psutil.cpu_percent(interval=1)
        memory = psutil.virtual_memory()
        disk = psutil.disk_usage('/')
        
        return {
            "status": "healthy" if cpu_percent < 80 and memory.percent < 80 else "warning",
            "timestamp": datetime.now().isoformat(),
            "system": {
                "cpu_percent": cpu_percent,
                "memory_percent": memory.percent,
                "memory_available_gb": round(memory.available / (1024**3), 2),
                "disk_percent": disk.percent,
                "disk_free_gb": round(disk.free / (1024**3), 2)
            },
            "service": "Mcube Call Service"
        }
    except Exception as e:
        return {
            "status": "error",
            "timestamp": datetime.now().isoformat(),
            "error": str(e)
        }

@app.websocket("/ws/{session_id}")
async def handle_mcube_call(websocket: WebSocket, session_id: str):
    """Handle Mcube WebSocket call connection with proper isolation."""
    Log.header(f"Mcube call connected - Session ID: {session_id}")
    await websocket.accept()
    from services.call_manager import call_manager
    
    session = await call_manager.create_call_session(session_id, websocket)
    if not session:
        Log.error(f"❌ Failed to create call session for {session_id}")
        await websocket.close(code=1013, reason="Failed to create call session")
        return
    
    try:
        connection_manager = session.connection_manager
        audio_service = session.audio_service
        async def handle_media_event(data: dict) -> None:
            """Handle incoming media data from Mcube."""
            await session.handle_media_event(data)

        async def handle_call_start(data: dict) -> None:
            """Handle call start event with full WebSocket data."""
            await session.handle_call_start(data)

        async def handle_played_stream(name: str) -> None:
            """Handle playedStream event from Mcube."""
            audio_service.handle_played_stream_event(name)
            await session.handle_played_stream_event(name)
        mcube_task = asyncio.create_task(
            connection_manager.receive_from_mcube(handle_media_event, handle_call_start, handle_played_stream)
        )
        
        try:
            tasks_to_wait = [mcube_task]
            
            done, pending = await asyncio.wait(
                tasks_to_wait,
                return_when=asyncio.FIRST_COMPLETED
            )
            
            for task in pending:
                task.cancel()
                try:
                    await task
                except asyncio.CancelledError:
                    pass
            
            for task in done:
                if task.exception():
                    exception = task.exception()
                    if isinstance(exception, WebSocketDisconnect):
                        Log.info(f"🔌 WebSocket disconnected detected in task for session {session_id}")
                        await call_manager.remove_call_session(session_id)
                        return
                    else:
                        Log.error(f"Task exception for session {session_id}: {exception}")
                        await call_manager.remove_call_session(session_id)
                        return        
            Log.info(f"All tasks completed normally for session {session_id}")
            await call_manager.remove_call_session(session_id)
            
        except WebSocketDisconnect:
            Log.info(f"WebSocket disconnected for session {session_id}")
            await call_manager.remove_call_session(session_id)
        except Exception as e:
            Log.error(f"Error in media stream handler: {e}")
            import traceback
            traceback.print_exc()
            await call_manager.remove_call_session(session_id)
        finally:
            if session_id in call_manager.active_sessions:
                await call_manager.remove_call_session(session_id)
    
    except Exception as e:
        Log.error(f"❌ Unexpected error in WebSocket handler for session {session_id}: {e}")
        import traceback
        traceback.print_exc()
        try:
            await call_manager.remove_call_session(session_id)
        except Exception as cleanup_error:
            Log.error(f"❌ Error during cleanup: {cleanup_error}")

if __name__ == "__main__":
    import uvicorn
    import atexit
    from services.log_utils import Log
    atexit.register(Log.close_log_file)
    uvicorn.run(app, host="0.0.0.0", port=Config.PORT)
