from __future__ import annotations

import asyncio
import json
import logging
from datetime import datetime, timezone
from pathlib import Path

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.staticfiles import StaticFiles

from ..chat.session import ChatSessionManager
from ..config import AppConfig
from ..state import StateManager
from .constants import WEB_DIR
from .router import router
from .rpc import RPCServer


def create_app(config: AppConfig | None = None) -> FastAPI:
    if config is None:
        config = AppConfig.load()

    app = FastAPI(
        title="Claude Board",
        description="Permission approval console for Claude Code",
        version="0.1.0",
    )

    state_manager = StateManager(yolo_default=config.yolo_mode_default)
    session_manager = ChatSessionManager()

    loaded_count = session_manager.load_sessions()
    if loaded_count > 0:
        logging.getLogger(__name__).info("Loaded %d session stubs from disk", loaded_count)

    connected_clients: list[WebSocket] = []

    app.state.config = config
    app.state.state_manager = state_manager
    app.state.session_manager = session_manager
    app.state.connected_clients = connected_clients

    async def broadcast_state(state: dict[str, object]) -> None:
        clients: list[WebSocket] = app.state.connected_clients
        if not clients:
            return

        sm: ChatSessionManager = app.state.session_manager
        state["chat_sessions"] = sm.list_sessions()

        message = json.dumps(
            {
                "type": "state_update",
                "data": state,
                "timestamp": datetime.now(tz=timezone.utc).isoformat(),
            }
        )

        disconnected: list[WebSocket] = []
        for client in clients:
            try:
                await client.send_text(message)
            except (OSError, WebSocketDisconnect):
                disconnected.append(client)

        for client in disconnected:
            if client in clients:
                clients.remove(client)

    state_manager.set_broadcast_callback(broadcast_state)
    app.include_router(router)

    if WEB_DIR.exists():
        app.mount("/static", StaticFiles(directory=WEB_DIR), name="static")

    return app


def run_server(
    host: str = "0.0.0.0",
    port: int = 8765,
    config: AppConfig | None = None,
    socket_path: Path | None = None,
) -> None:
    import uvicorn  # noqa: PLC0415

    if config is None:
        config = AppConfig.load()
        config.server.host = host
        config.server.port = port

    app = create_app(config)

    if socket_path:

        async def run_with_rpc() -> None:
            rpc_server = RPCServer(socket_path, app.state.state_manager)
            await rpc_server.start()

            uvi_config = uvicorn.Config(
                app,
                host=config.server.host,
                port=config.server.port,
                log_level="info",
            )
            server = uvicorn.Server(uvi_config)

            try:
                await server.serve()
            finally:
                await rpc_server.stop()

        asyncio.run(run_with_rpc())
    else:
        uvicorn.run(app, host=config.server.host, port=config.server.port)
