"""
Server App - 服务启动入口

职责：
- 配置加载
- 服务初始化
- 路由注册
- 服务启动
"""

import uuid
import socket
import asyncio
from typing import Optional, Dict, Any, List

from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

from core.logger import log_event, set_log_side
from core.config import load_config, load_mcp_config, load_settings
from core.mcp_manager import MCPManager
from tools.base import BaseTool

from .constants import ServerConfig
from .session import ConnectionSessionManager
from .handler import WebSocketHandler


class ServerApp:
    """
    服务应用

    负责：
    1. 加载配置
    2. 初始化组件
    3. 注册路由
    4. 启动服务
    """

    def __init__(self, config_path: Optional[str] = None):
        """
        初始化服务应用

        Args:
            config_path: 配置文件路径（可选）
        """
        set_log_side("SERVER")
        # 如果没有指定配置路径，使用默认的 config.json
        if config_path:
            self.config = load_config(config_path)
        else:
            self.config = load_config()  # 使用默认值 "config.json"

        # 加载 MCP 配置
        self.mcp_config = load_mcp_config()
        self.mcp_manager: Optional[MCPManager] = None
        self.mcp_tools: List[BaseTool] = []

        # 创建 FastAPI 应用
        self.app = FastAPI(title="Agent WebSocket Server")
        self._setup_middleware()

        # 初始化组件
        self._init_components()

        # 注册路由
        self._register_routes()

        # 注册启动事件（MCP 后台初始化，不阻塞服务启动）
        @self.app.on_event("startup")
        async def startup_event():
            asyncio.create_task(self._init_mcp())

        # 注册关闭事件（用于清理 MCP）
        @self.app.on_event("shutdown")
        async def shutdown_event():
            await self._cleanup_mcp()

    def _setup_middleware(self):
        """设置中间件"""
        self.app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

    def _init_components(self):
        """初始化组件"""
        # 确定默认模型：setting.json > config.provider.default
        model_name = self._get_default_model()

        log_event(
            "default_model_resolved",
            model_name=model_name,
        )

        # 创建会话管理器
        self.session_manager = ConnectionSessionManager()

        # 创建 MCP 工具获取函数
        mcp_tools_getter = lambda: self.mcp_tools

        # 创建 WebSocket 处理器（传递 mcp_tools_getter）
        self.ws_handler = WebSocketHandler(
            session_manager=self.session_manager,
            model_name=model_name,
            config=self.config,
            mcp_tools_getter=mcp_tools_getter,
        )

        log_event("components_initialized")

    def _get_default_model(self) -> str:
        """
        获取默认模型名称

        优先级：setting.json > config.provider.default
        """
        # 检查 setting.json 中是否有持久化的模型选择
        settings = load_settings()
        saved_model = settings.get("default_model")
        if saved_model and saved_model in self.config.provider.models:
            return saved_model

        # 使用配置中的默认模型
        default = self.config.provider.default
        if default and default in self.config.provider.models:
            return default

        # 兜底：使用 models 中的第一个
        if self.config.provider.models:
            return next(iter(self.config.provider.models))

        raise ValueError("配置中没有定义任何模型")

    async def _init_mcp(self):
        """初始化 MCP 连接"""
        if self.mcp_config.mcp_servers:
            log_event("mcp_init_start", server_count=len(self.mcp_config.mcp_servers))
            try:
                self.mcp_manager = MCPManager(self.mcp_config)
                await self.mcp_manager.connect_all()
                self.mcp_tools = self.mcp_manager.get_tools()
                log_event("mcp_init_complete", tool_count=len(self.mcp_tools))

            except Exception as e:
                log_event("mcp_init_error", error=str(e))

    async def _cleanup_mcp(self):
        """清理 MCP 连接"""
        if self.mcp_manager:
            try:
                await self.mcp_manager.cleanup()
                log_event("mcp_cleanup_complete")
            except Exception as e:
                log_event("mcp_cleanup_error", error=str(e))

    def _register_routes(self):
        """注册路由"""

        @self.app.get("/")
        async def root():
            """健康检查"""
            return {
                "status": "ok",
                "service": "Agent WebSocket Server",
                "sessions": self.session_manager.session_count,
                "connections": self.ws_handler.get_active_connections(),
            }

        @self.app.get("/health")
        async def health():
            """健康检查端点"""
            return {"status": "healthy"}

        @self.app.get("/context/{session_id}")
        async def get_context(session_id: str):
            """获取上下文信息"""
            session = self.session_manager.get_session(session_id)
            if not session:
                return {"token_count": 0, "max_tokens": 0, "percent": 0.0, "threshold": 80}
            return session.get_context_info(session_id)

        @self.app.post("/clear/{session_id}")
        async def clear_session(session_id: str):
            """清除会话上下文（重新开始会话）"""
            session = self.session_manager.get_session(session_id)
            if not session:
                return {"success": False, "message": "会话不存在"}

            # 清除会话历史
            session.clear_history()
            log_event("session_cleared", session_id=session_id)

            return {"success": True, "message": "会话已清除"}

        @self.app.post("/compact/{session_id}")
        async def compact_session(session_id: str):
            """压缩会话上下文（只有占比超过60%才执行）"""
            session = self.session_manager.get_session(session_id)
            if not session:
                return {"success": False, "message": "会话不存在"}

            # 获取上下文信息检查占比
            context_info = session.get_context_info(session_id)
            percent = context_info.get("percent", 0)

            if percent < 40:
                return {
                    "success": True,
                    "message": f"上下文占比 {percent:.1f}% 未达到 40%，无需压缩",
                    "skipped": True,
                    "percent": percent,
                }

            # 执行压缩
            try:
                result = await session.compact_session()
                log_event("session_compacted", session_id=session_id, percent=percent)
                return {
                    "success": True,
                    "message": f"压缩完成（压缩前占比 {percent:.1f}%）",
                    "compressed_count": result.get("compressed_count", 0),
                }
            except Exception as e:
                log_event("session_compact_error", session_id=session_id, error=str(e))
                return {"success": False, "message": f"压缩失败: {str(e)}"}

        @self.app.get("/sessions")
        async def list_sessions(limit: int = 20):
            """获取会话列表"""
            from core.session import get_session_manager
            import json

            persistent_mgr = get_session_manager()
            sessions = persistent_mgr.list_sessions()

            session_list = []
            for s in sessions[:limit]:
                # 从 context.jsonl 第一行读取预览
                preview = ""
                try:
                    context_file = persistent_mgr.session_storage.storage_dir / s.id / "context.jsonl"
                    if context_file.exists():
                        with open(context_file, "r", encoding="utf-8") as f:
                            first_line = f.readline().strip()
                            if first_line:
                                msg_data = json.loads(first_line)
                                if msg_data.get("role") == "user":
                                    text = msg_data.get("content", "")
                                    preview = text[:500] + ("..." if len(text) > 500 else "")
                except Exception:
                    pass

                session_list.append({
                    "id": s.id,
                    "title": s.title,
                    "directory": s.directory,
                    "preview": preview,
                    "created_at": s.created_at.isoformat() if hasattr(s, 'created_at') and s.created_at else None,
                    "updated_at": s.updated_at.isoformat() if hasattr(s, 'updated_at') and s.updated_at else None,
                })

            return {"sessions": session_list, "total": len(sessions)}

        @self.app.get("/models")
        async def get_models():
            """获取可用模型列表"""
            from provider.models import get_model_list
            return {"models": get_model_list()}

        @self.app.websocket("/ws/{session_id}")
        async def websocket_endpoint(websocket: WebSocket, session_id: str):
            """WebSocket 端点（指定会话 ID，用于恢复历史会话）"""
            agent_type = websocket.query_params.get("agent_type", "general")
            await self.ws_handler.handle_connection(websocket, session_id, agent_type)

        @self.app.websocket("/ws")
        async def websocket_endpoint_auto(websocket: WebSocket):
            """WebSocket 端点（自动生成会话 ID）"""
            session_id = str(uuid.uuid4())
            agent_type = websocket.query_params.get("agent_type", "general")
            await self.ws_handler.handle_connection(websocket, session_id, agent_type)

    def run(
        self,
        host: Optional[str] = None,
        port: Optional[int] = None,
    ):
        """
        启动服务

        Args:
            host: 主机地址
            port: 端口号
        """
        host = host or ServerConfig.DEFAULT_HOST
        port = port or ServerConfig.DEFAULT_PORT

        # 自动切换端口
        port = self._find_available_port(host, port)

        log_event(
            "server_starting",
            host=host,
            port=port,
        )

        uvicorn.run(
            self.app,
            host=host,
            port=port,
            ws_ping_interval=ServerConfig.WS_PING_INTERVAL,
            ws_ping_timeout=ServerConfig.WS_PING_TIMEOUT,
        )

    def _find_available_port(self, host: str, port: int) -> int:
        """
        查找可用端口

        Args:
            host: 主机地址
            port: 起始端口

        Returns:
            可用端口
        """
        for attempt in range(ServerConfig.MAX_PORT_ATTEMPTS):
            current_port = port + attempt
            if self._is_port_available(host, current_port):
                if attempt > 0:
                    log_event(
                        "port_switched",
                        original_port=port,
                        new_port=current_port,
                    )
                return current_port

        # 所有端口都被占用
        raise RuntimeError(
            f"无法找到可用端口（尝试了 {port} 到 {port + ServerConfig.MAX_PORT_ATTEMPTS - 1}）"
        )

    def _is_port_available(self, host: str, port: int) -> bool:
        """检查端口是否可用"""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.bind((host, port))
                return True
        except OSError:
            return False


def start_server(
    host: str = ServerConfig.DEFAULT_HOST,
    port: int = ServerConfig.DEFAULT_PORT,
    config_path: Optional[str] = None,
):
    """
    启动服务器

    Args:
        host: 主机地址
        port: 端口号
        config_path: 配置文件路径
    """
    server_app = ServerApp(config_path=config_path)
    server_app.run(host=host, port=port)


# 延迟初始化的 app 实例（用于 uvicorn 直接运行）
# 注意：直接运行时需要确保配置文件存在
_lazy_app: Optional[FastAPI] = None


def get_app() -> FastAPI:
    """
    获取 FastAPI 应用实例（延迟初始化）

    用于 uvicorn 直接运行：uvicorn src.server.app:get_app --factory
    """
    return ServerApp().app


if __name__ == "__main__":
    start_server()
