"""Wiki 图谱与概念页 API。"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from fastapi import APIRouter, Depends, HTTPException, Query

from niannian.brain.cerebellum.wiki_store import WikiStore
from niannian.http.core_auth import require_core_access
from niannian.vault.store import VaultStore

if TYPE_CHECKING:
    from niannian.client import NianNian


def _filter_namespace(items: dict[str, Any], *, namespace: str, object_id: str | None) -> dict[str, Any]:
    out: dict[str, Any] = {}
    for key, val in items.items():
        if not isinstance(val, dict):
            continue
        ns = str(val.get("namespace") or "subject")
        if namespace == "subject" and ns == "subject":
            out[key] = val
        elif namespace.startswith("object:") and ns == namespace:
            out[key] = val
        elif object_id and ns == f"object:{object_id}":
            out[key] = val
    return out


def create_wiki_router(nn: NianNian) -> APIRouter:
    router = APIRouter(tags=["wiki"])
    wiki = WikiStore(nn._store)
    vault_store = VaultStore(
        nn._home,
        manifest=getattr(nn._store, "manifest", None),
        config=nn._config,
    )

    @router.get("/v1/wiki/graph")
    def wiki_graph(
        namespace: str = Query("subject"),
        object_id: str | None = Query(None),
        _role: str = Depends(require_core_access),
    ):
        graph = wiki.load_graph()
        nodes = _filter_namespace(graph.get("nodes") or {}, namespace=namespace, object_id=object_id)
        node_ids = set(nodes.keys())
        edges = [
            e
            for e in (graph.get("edges") or [])
            if isinstance(e, dict) and e.get("from") in node_ids and e.get("to") in node_ids
        ]
        return {
            "version": graph.get("version", 1),
            "namespace": namespace if not object_id else f"object:{object_id}",
            "nodes": nodes,
            "edges": edges,
        }

    @router.get("/v1/wiki/resolve")
    def wiki_resolve(
        vault_id: str = Query(...),
        _role: str = Depends(require_core_access),
    ):
        vid = str(vault_id or "").strip()
        if not vid:
            raise HTTPException(status_code=400, detail="vault_id required")
        entry = vault_store.find_entry(vid)
        rel_path = str(entry.get("rel_path") or "") if isinstance(entry, dict) else ""
        wiki_page_id_hint: str | None = None
        if rel_path:
            try:
                doc = vault_store.read_document(rel_path)
                fm = doc.get("frontmatter") if isinstance(doc.get("frontmatter"), dict) else {}
                raw_hint = fm.get("wiki_page_id")
                wiki_page_id_hint = str(raw_hint).strip() if raw_hint else None
            except FileNotFoundError:
                pass
        page_id = wiki.find_page_id_by_vault(
            vault_id=vid,
            vault_rel_path=rel_path or None,
            wiki_page_id_hint=wiki_page_id_hint,
        )
        if page_id is None:
            raise HTTPException(status_code=404, detail="page_not_found")
        return {"page_id": page_id, "vault_id": vid}

    @router.get("/v1/wiki/pages/{page_id:path}")
    def wiki_page(
        page_id: str,
        object_id: str | None = Query(None),
        _role: str = Depends(require_core_access),
    ):
        pages = wiki.pages_map()
        page = pages.get(page_id)
        if page is None:
            raise HTTPException(status_code=404, detail="page_not_found")
        ns = str(page.get("namespace") or "subject")
        if object_id:
            if ns != f"object:{object_id}":
                raise HTTPException(status_code=403, detail="disclosure_denied")
        elif ns != "subject":
            raise HTTPException(status_code=403, detail="disclosure_denied")
        backlinks = [
            {"from": e.get("from"), "type": e.get("type"), "weight": e.get("weight")}
            for e in (wiki.load_graph().get("edges") or [])
            if isinstance(e, dict) and e.get("to") == page_id
        ]
        return {**page, "backlinks": backlinks}

    return router
