"""
LSP 服务器管理器

管理多个 LSP 客户端实例，实现连接复用。
"""

import asyncio
import logging
from typing import Dict, List, Optional, Tuple

from .client import LSPClient
from .server import ServerConfig, get_server_for_file
from .protocol import (
    Location,
    DocumentSymbol,
    SymbolInformation,
    Hover,
    Diagnostic,
    CallHierarchyItem,
    CallHierarchyIncomingCall,
    CallHierarchyOutgoingCall,
)

logger = logging.getLogger(__name__)


class LSPManager:
    """
    LSP 服务器管理器

    管理多个 LSP 客户端实例，根据文件类型自动选择合适的语言服务器。
    实现连接复用，避免重复启动语言服务器。
    """

    def __init__(self):
        """初始化 LSP 管理器"""
        # 客户端缓存: (server_id, root_path) -> LSPClient
        self._clients: Dict[Tuple[str, str], LSPClient] = {}
        self._lock = asyncio.Lock()

    async def get_client(self, file_path: str) -> Optional[LSPClient]:
        """
        获取文件对应的 LSP 客户端

        如果客户端不存在，会自动创建并初始化。

        Args:
            file_path: 文件路径

        Returns:
            LSP 客户端实例，如果没有匹配的服务器返回 None
        """
        server = get_server_for_file(file_path)
        if not server:
            logger.debug(f"No LSP server found for file: {file_path}")
            return None

        if not server.is_available():
            logger.warning(f"LSP server {server.id} is not available (command not found)")
            return None

        root_path = server.find_root(file_path)
        if not root_path:
            logger.warning(f"Could not find root path for file: {file_path}")
            return None

        key = (server.id, root_path)

        async with self._lock:
            if key in self._clients:
                return self._clients[key]

            # 创建新客户端
            client = LSPClient(
                server_id=server.id,
                command=server.command,
                env=server.env
            )

            # 初始化客户端
            success = await client.initialize(root_path)
            if not success:
                logger.error(f"Failed to initialize LSP client for {server.id}")
                return None

            self._clients[key] = client
            logger.info(f"Created LSP client for {server.id} at {root_path}")
            return client

    async def shutdown_all(self) -> None:
        """关闭所有 LSP 客户端"""
        async with self._lock:
            tasks = [client.shutdown() for client in self._clients.values()]
            if tasks:
                await asyncio.gather(*tasks, return_exceptions=True)
            self._clients.clear()
            logger.info("All LSP clients shut down")

    async def shutdown_client(self, server_id: str, root_path: str) -> None:
        """关闭指定的 LSP 客户端"""
        key = (server_id, root_path)
        async with self._lock:
            if key in self._clients:
                await self._clients[key].shutdown()
                del self._clients[key]
                logger.info(f"LSP client {server_id} at {root_path} shut down")

    # 便捷方法：封装常用 LSP 操作

    async def go_to_definition(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> List[Location]:
        """
        跳转到定义

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            定义位置列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.text_document_definition(file_path, line, character)

    async def find_references(
        self,
        file_path: str,
        line: int,
        character: int,
        include_declaration: bool = True
    ) -> List[Location]:
        """
        查找引用

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）
            include_declaration: 是否包含声明

        Returns:
            引用位置列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.text_document_references(file_path, line, character, include_declaration)

    async def hover(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> Optional[Hover]:
        """
        获取悬停信息

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            悬停信息
        """
        client = await self.get_client(file_path)
        if not client:
            return None
        return await client.text_document_hover(file_path, line, character)

    async def document_symbol(self, file_path: str) -> List[DocumentSymbol]:
        """
        获取文档符号

        Args:
            file_path: 文件路径

        Returns:
            文档符号列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.document_symbol(file_path)

    async def workspace_symbol(self, file_path: str, query: str) -> List[SymbolInformation]:
        """
        搜索工作区符号

        Args:
            file_path: 用于确定工作区的文件路径
            query: 搜索查询

        Returns:
            符号列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.workspace_symbol(query)

    async def go_to_implementation(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> List[Location]:
        """
        跳转到实现

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            实现位置列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.text_document_implementation(file_path, line, character)

    async def prepare_call_hierarchy(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> List[CallHierarchyItem]:
        """
        准备调用层次

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            调用层次项列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        return await client.prepare_call_hierarchy(file_path, line, character)

    async def incoming_calls(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> List[CallHierarchyIncomingCall]:
        """
        获取传入调用

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            传入调用列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []

        items = await client.prepare_call_hierarchy(file_path, line, character)
        if not items:
            return []

        return await client.incoming_calls(items[0])

    async def outgoing_calls(
        self,
        file_path: str,
        line: int,
        character: int
    ) -> List[CallHierarchyOutgoingCall]:
        """
        获取传出调用

        Args:
            file_path: 文件路径
            line: 行号（1-based）
            character: 字符偏移（1-based）

        Returns:
            传出调用列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []

        items = await client.prepare_call_hierarchy(file_path, line, character)
        if not items:
            return []

        return await client.outgoing_calls(items[0])

    async def get_diagnostics(self, file_path: str) -> List[Diagnostic]:
        """
        获取文件诊断信息

        Args:
            file_path: 文件路径

        Returns:
            诊断信息列表
        """
        client = await self.get_client(file_path)
        if not client:
            return []
        # 先打开文件以触发诊断
        await client.open_file(file_path)
        # 等待一小段时间让服务器发送诊断
        await asyncio.sleep(0.5)
        return client.get_diagnostics(file_path)


# 全局单例
_manager: Optional[LSPManager] = None


def get_lsp_manager() -> LSPManager:
    """获取全局 LSP 管理器实例"""
    global _manager
    if _manager is None:
        _manager = LSPManager()
    return _manager


async def shutdown_lsp_manager() -> None:
    """关闭全局 LSP 管理器"""
    global _manager
    if _manager is not None:
        await _manager.shutdown_all()
        _manager = None
