from __future__ import annotations

import asyncio
import uuid

from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse, StreamingResponse

from app_dependencies import get_runtime
from message_pipeline import encode_sse_data
from route_helpers import read_json_request
from session_store import (
    create_session,
    delete_session,
    is_web_session_chat_id,
    list_web_sessions,
    load_session_payload,
    normalize_session_chat_id,
    rename_session,
)
from web_runtime import WebAppRuntime

router = APIRouter()


@router.get("/events")
async def stream_ui_events(request: Request, runtime: WebAppRuntime = Depends(get_runtime)):
    chat_id = normalize_session_chat_id(str(request.query_params.get("chat_id", "web") or "web"))
    if not chat_id:
        return JSONResponse({"error": "invalid chat id"}, status_code=400)

    subscription_id, queue = await runtime.event_bus.subscribe(chat_id)

    async def stream_events():
        try:
            yield ": stream-open\n\n"
            yield encode_sse_data({"type": "events.ready", "chat_id": chat_id})
            while True:
                if await request.is_disconnected():
                    break
                try:
                    payload = await asyncio.wait_for(queue.get(), timeout=20.0)
                except asyncio.TimeoutError:
                    yield ": keepalive\n\n"
                    continue
                yield encode_sse_data(payload)
        finally:
            await runtime.event_bus.unsubscribe(subscription_id)

    return StreamingResponse(
        stream_events(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )


@router.get("/sessions")
async def get_sessions() -> JSONResponse:
    return JSONResponse({"sessions": list_web_sessions()})


@router.get("/sessions/{chat_id}")
async def get_session(chat_id: str) -> JSONResponse:
    session_chat_id = normalize_session_chat_id(chat_id)
    if not session_chat_id or not is_web_session_chat_id(session_chat_id):
        return JSONResponse({"error": "invalid session id"}, status_code=400)

    payload = load_session_payload(session_chat_id)
    if payload is None:
        return JSONResponse({"error": "session not found"}, status_code=404)

    return JSONResponse(payload)


@router.post("/sessions")
async def create_session_route(
    request: Request,
    runtime: WebAppRuntime = Depends(get_runtime),
) -> JSONResponse:
    try:
        payload = await read_json_request(request)
    except ValueError as exc:
        return JSONResponse({"error": str(exc)}, status_code=400)

    title = str(payload.get("title", "")).strip()
    chat_id = f"web-{uuid.uuid4().hex[:8]}"
    summary = create_session(chat_id, title=title)
    await runtime.publish_sessions_changed()
    return JSONResponse({"session": summary}, status_code=201)


@router.patch("/sessions/{chat_id}")
async def rename_session_route(
    chat_id: str,
    request: Request,
    runtime: WebAppRuntime = Depends(get_runtime),
) -> JSONResponse:
    session_chat_id = normalize_session_chat_id(chat_id)
    if not session_chat_id or not is_web_session_chat_id(session_chat_id):
        return JSONResponse({"error": "invalid session id"}, status_code=400)

    try:
        payload = await read_json_request(request)
    except ValueError as exc:
        return JSONResponse({"error": str(exc)}, status_code=400)

    title = str(payload.get("title", "")).strip()
    summary = rename_session(session_chat_id, title)
    if summary is None:
        return JSONResponse({"error": "session not found"}, status_code=404)
    await runtime.publish_sessions_changed()
    return JSONResponse({"session": summary})


@router.delete("/sessions/{chat_id}")
async def delete_session_route(
    chat_id: str,
    runtime: WebAppRuntime = Depends(get_runtime),
) -> JSONResponse:
    session_chat_id = normalize_session_chat_id(chat_id)
    if not session_chat_id or not is_web_session_chat_id(session_chat_id):
        return JSONResponse({"error": "invalid session id"}, status_code=400)

    if not delete_session(session_chat_id, runtime.delete_cards_for_chat):
        return JSONResponse({"error": "session not found"}, status_code=404)
    await runtime.publish_sessions_changed()
    await runtime.publish_cards_changed()
    return JSONResponse({"status": "ok", "chat_id": session_chat_id})
