import asyncio
import logging
import uvicorn
import signal
import os
import sys
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import Response, StreamingResponse, FileResponse
from starlette.middleware.cors import CORSMiddleware

from mcp_logic import MCPServer
from tools import TOOL_REGISTRY
import config


# --- Configuration & Logging ---
logging.basicConfig(
    level=getattr(logging, config.LOG_LEVEL),
    filename=config.LOG_FILE,

    filemode="a",
    format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("MooseCP")

mcp_logic = MCPServer()

# --- Transport Layer (Starlette/SSE) ---

async def sse_endpoint(request):
    sid = mcp_logic.create_session()
    logger.debug(f"Session started: {sid}")
    
    async def event_generator():
        try:
            yield f"event: endpoint\ndata: /messages?session_id={sid}\n\n"
            queue = mcp_logic.sessions[sid]
            while mcp_logic.running:
                try:
                    # Use a slightly shorter timeout for faster shutdown response
                    msg = await asyncio.wait_for(queue.get(), timeout=0.5)
                    yield f"event: message\ndata: {msg}\n\n"
                    queue.task_done()
                except asyncio.TimeoutError:
                    continue
        except asyncio.CancelledError:
            logger.debug(f"Session cancelled: {sid}")
        finally:
            mcp_logic.sessions.pop(sid, None)
            logger.debug(f"Session closed: {sid}")

    return StreamingResponse(event_generator(), media_type="text/event-stream")

async def messages_endpoint(request):
    sid = request.query_params.get("session_id")
    if not sid or sid not in mcp_logic.sessions:
        return Response("Session not found", status_code=404)

    # Capture the host header to ensure image links work across the network
    host_header = request.headers.get("host", f"{config.HOST}:{config.PORT}")
    config.request_host.set(host_header)

    try:
        body = await request.json()
    except Exception as e:
        return Response(f"Invalid JSON: {e}", status_code=400)

    method = body.get("method")
    params = body.get("params", {})
    req_id = body.get("id")
    
    logger.debug(f"Request: {method} (ID: {req_id})")
    
    result = await mcp_logic.handle_request(method, params, req_id, TOOL_REGISTRY)
    
    if isinstance(result, Response):
        return result
    
    await mcp_logic.send_to_session(sid, {"jsonrpc": "2.0", "id": req_id, "result": result})
    return Response(status_code=202)

async def file_endpoint(request):
    """
    Proxy endpoint to serve files from disk with CORP/CORS headers to bypass browser restrictions.
    """
    path = request.path_params.get("path")
    if not path:
        return Response("Path not provided", status_code=400)
    
    # Ensure the path is absolute (SDFiles are usually in /tmp/gradio/...)
    # If the path doesn't start with /, we assume it's relative to root for simplicity in this context
    full_path = path if path.startswith("/") else f"/{path}"
    
    if not os.path.exists(full_path):
        return Response(f"File not found: {full_path}", status_code=404)
    
    return FileResponse(
        full_path, 
        headers={
            "Cross-Origin-Resource-Policy": "cross-origin",
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET"
        }
    )

app = Starlette(
    routes=[
        Route("/sse", endpoint=sse_endpoint),
        Route("/messages", endpoint=messages_endpoint, methods=["POST"]),
        Route("/file/{path:path}", endpoint=file_endpoint),
    ]
)

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

if __name__ == "__main__":
    # Use a separate variable for uvicorn config to avoid shadowing the config module
    uvicorn_config = uvicorn.Config(
        app=app, 
        host=config.HOST, 
        port=config.PORT, 
        log_level="info",
        timeout_graceful_shutdown=2 # Reduced from 5 to 2 seconds
    )

    server = uvicorn.Server(uvicorn_config)
    
    def signal_handler(sig, frame):
        logger.info("Shutdown signal received")
        mcp_logic.running = False
        # Explicitly tell Uvicorn to stop the server
        server.should_exit = True
    
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    try:
        asyncio.run(server.serve())
    except KeyboardInterrupt:
        pass
    finally:
        logger.info("Server process exiting.")
        sys.exit(0)
