"""MCP server for docsctx with 12 tools matching Go implementation."""

from __future__ import annotations

from mcp.server.fastmcp import FastMCP

from docsctx.config import DocsSettings
from docsctx.db.repository import DocsRepository
from docsctx.embedder import create_embedder
from docsctx.search import GlobalSearch, LocalSearch


def build_mcp_server(repository: DocsRepository, settings: DocsSettings, host: str = "127.0.0.1", port: int = 8083) -> FastMCP:
    """Build a FastMCP server with 12 tools for docsctx."""
    server = FastMCP("docsctx", host=host, port=port)
    embedder = create_embedder(settings)
    local_search = LocalSearch(repository, embedder) if embedder is not None else None
    global_search = GlobalSearch(repository)

    @server.tool()
    def search_documents(query: str, limit: int = 5, version: str = "latest") -> dict:
        """Search documents by keyword.

        Args:
            query: Search query text.
            limit: Maximum number of results.
            version: Version filter - "latest" (default), "all", or a specific version number.
        """
        results = repository.search(query, top_k=limit)
        if version == "latest":
            results = [r for r in results if r.to_dict().get("is_latest", True)]
        elif version != "all":
            try:
                version_num = int(version)
                # Filter by checking the document's version
                filtered = []
                for r in results:
                    doc = repository.get_document(r.document_id)
                    if doc and doc.get("version") == version_num:
                        filtered.append(r)
                results = filtered
            except ValueError:
                pass  # Invalid version string, return all results
        return {
            "query": query,
            "version_filter": version,
            "results": [result.to_dict() for result in results],
            "count": len(results),
        }

    @server.tool()
    def local_search_vector(query: str, top_k: int = 10, depth: int = 2) -> dict:
        """Local search: vector similarity + graph walk. Requires embedding provider."""
        if local_search is None:
            return {"error": "Embedding provider not configured. Set DOCSCTX_EMBEDDING__PROVIDER.", "results": []}
        return local_search.search(query, top_k=top_k, graph_depth=depth)

    @server.tool()
    def global_search_communities(query: str, top_k: int = 5) -> dict:
        """Global search: community summaries."""
        return global_search.search(query, top_k=top_k)

    @server.tool()
    def query_entity(name: str) -> dict:
        """Get entity by name with full details."""
        entity = repository.get_entity_by_name(name)
        if not entity:
            return {"error": f"Entity not found: {name}"}
        return {"entity": entity}

    @server.tool()
    def find_relationships(entity_name: str) -> dict:
        """Find all relationships involving an entity."""
        from_rels = repository.list_relationships_from_entity(entity_name)
        to_rels = repository.list_relationships_to_entity(entity_name)
        return {
            "entity": entity_name,
            "relationships_from": from_rels,
            "relationships_to": to_rels,
            "total": len(from_rels) + len(to_rels),
        }

    @server.tool()
    def get_graph_neighborhood(entity_name: str, depth: int = 2) -> dict:
        """Get graph neighborhood around an entity."""
        entity = repository.get_entity_by_name(entity_name)
        if not entity:
            return {"error": f"Entity not found: {entity_name}"}

        # BFS walk
        visited = {entity_name}
        frontier = [entity["name"]]
        depth_map = {entity["name"]: 0}
        all_rels = []

        for _ in range(depth):
            next_frontier = []
            for ent_name in frontier:
                rels_from = repository.list_relationships_from_entity(ent_name)
                rels_to = repository.list_relationships_to_entity(ent_name)
                all_rels.extend(rels_from + rels_to)

                for rel in rels_from + rels_to:
                    neighbor = rel["target"] if rel["source"] in visited else rel["source"]
                    if neighbor not in visited:
                        visited.add(neighbor)
                        depth_map[neighbor] = depth_map[ent_name] + 1
                        next_frontier.append(neighbor)

            frontier = next_frontier
            if not frontier:
                break

        # Get all entities in neighborhood
        entities = []
        for ent_name in visited:
            ent = repository.get_entity_by_name(ent_name)
            if ent:
                entities.append({**ent, "depth": depth_map.get(ent_name, 0)})

        return {
            "root_entity": entity_name,
            "entities": entities,
            "relationships": all_rels,
            "neighborhood_size": len(visited),
        }

    @server.tool()
    def get_document_structure(document_id: int) -> dict:
        """Get full document structure with all analysis."""
        doc = repository.get_document(document_id)
        if not doc:
            return {"error": f"Document not found: {document_id}"}
        return {"document": doc}

    @server.tool()
    def list_entities(limit: int = 100) -> dict:
        """List all entities across documents."""
        # Collect entities from all documents
        all_docs = repository.list_documents(limit=1000)
        all_entities = {}
        for doc in all_docs:
            entities = repository.list_entities(doc["id"])
            for entity in entities:
                name = entity["name"]
                if name not in all_entities:
                    all_entities[name] = entity
        return {
            "entities": list(all_entities.values())[:limit],
            "total": len(all_entities),
        }

    @server.tool()
    def list_documents(limit: int = 20) -> dict:
        """List documents."""
        docs = repository.list_documents(limit=limit)
        return {"documents": docs, "count": len(docs)}

    @server.tool()
    def get_community_report(community_id: int) -> dict:
        """Get community report with entities and summary."""
        community = repository.get_community(community_id)
        if not community:
            return {"error": f"Community not found: {community_id}"}
        entities = repository.list_entities_in_community(community_id)
        return {
            "community": community.to_dict(),
            "entities": entities,
        }

    @server.tool()
    def get_chunk(chunk_id: int) -> dict:
        """Get a document chunk."""
        # Since we don't have a get_chunk method, search for it through documents
        all_docs = repository.list_documents(limit=1000)
        for doc in all_docs:
            full_doc = repository.get_document(doc["id"])
            if full_doc and "chunks" in full_doc:
                for chunk in full_doc["chunks"]:
                    if chunk["id"] == chunk_id:
                        return {"chunk": chunk}
        return {"error": f"Chunk not found: {chunk_id}"}

    @server.tool()
    def stats() -> dict:
        """Get stats."""
        stats = repository.get_stats()
        return {"stats": stats.to_dict()}

    return server


def build_mcp_server_entry(transport: str = "stdio", host: str = "127.0.0.1", port: int = 8083) -> dict:
    """Build an mcp.json server entry for docsctx."""
    if transport == "stdio":
        return {
            "type": "stdio",
            "command": "python",
            "args": ["-m", "docsctx.mcp"],
        }
    if transport == "streamable-http":
        return {
            "type": "http",
            "url": f"http://{host}:{port}/mcp",
        }
    raise ValueError(f"Unsupported MCP transport: {transport}. Use 'stdio' or 'streamable-http'.")


if __name__ == "__main__":
    # Run the MCP server
    from docsctx.config import DocsSettings

    settings = DocsSettings()
    repository = DocsRepository(settings.db_path)
    server = build_mcp_server(repository, settings)
    server.run(transport="stdio")
