"""Traversal queries over the knowledge graph: entity/citation -> chunks, neighbors."""

from __future__ import annotations

from typing import Any

from .builder import _normalize_entity


def _entity_key(text: str) -> str:
    return f"entity:{_normalize_entity(text)}"


def _chunk_payload(graph: Any, chunk_node: str) -> dict[str, Any]:
    data = graph.nodes[chunk_node]
    doc_node = next(
        (src for src, _, e in graph.in_edges(chunk_node, data=True) if e.get("kind") == "CONTAINS"),
        None,
    )
    doc = graph.nodes[doc_node] if doc_node is not None else {}
    return {
        "chunk": chunk_node,
        "chunk_index": data.get("chunk_index", 0),
        "text": data.get("text", ""),
        "source_path": doc.get("source_path", ""),
        "ocr_engine": doc.get("ocr_engine", ""),
        "ocr_confidence_mean": doc.get("ocr_confidence_mean", 0.0),
        "timestamp": doc.get("timestamp", ""),
    }


def chunks_for_entity(graph: Any, entity_text: str) -> list[dict[str, Any]]:
    """All chunks mentioning the given entity (case-insensitive)."""
    key = _entity_key(entity_text)
    if key not in graph:
        return []
    chunks = [
        src for src, _, edge in graph.in_edges(key, data=True) if edge.get("kind") == "MENTIONS"
    ]
    return [_chunk_payload(graph, c) for c in sorted(chunks)]


def chunks_for_citation(graph: Any, identifier: str) -> list[dict[str, Any]]:
    """All chunks citing the given identifier (e.g. '10.1038/nature12345')."""
    key = identifier if identifier.startswith("citation:") else f"citation:{identifier}"
    if key not in graph:
        return []
    chunks = [src for src, _, edge in graph.in_edges(key, data=True) if edge.get("kind") == "CITES"]
    return [_chunk_payload(graph, c) for c in sorted(chunks)]


def related_entities(graph: Any, entity_text: str, limit: int = 10) -> list[tuple[str, int]]:
    """Entities co-occurring with the given one, as (text, shared-chunk weight)."""
    key = _entity_key(entity_text)
    if key not in graph:
        return []
    neighbors: dict[str, int] = {}
    for _, target, edge in graph.out_edges(key, data=True):
        if edge.get("kind") == "CO_OCCURS":
            neighbors[target] = edge.get("weight", 1)
    for source, _, edge in graph.in_edges(key, data=True):
        if edge.get("kind") == "CO_OCCURS":
            neighbors[source] = max(neighbors.get(source, 0), edge.get("weight", 1))
    ranked = sorted(
        ((graph.nodes[n].get("text", n), w) for n, w in neighbors.items()),
        key=lambda item: item[1],
        reverse=True,
    )
    return ranked[:limit]


def expand_context(graph: Any, entity_text: str, limit: int = 5) -> list[dict[str, Any]]:
    """Chunks mentioning the entity or its strongest co-occurring neighbors.

    This is the graph-RAG primitive: one hop of CO_OCCURS expansion, so a query
    for 'BRCA1' also surfaces chunks that only mention its interactors.
    """
    seen: set[str] = set()
    results: list[dict[str, Any]] = []

    seeds = [entity_text] + [text for text, _ in related_entities(graph, entity_text, limit=limit)]
    for seed in seeds:
        for payload in chunks_for_entity(graph, seed):
            if payload["chunk"] in seen:
                continue
            seen.add(payload["chunk"])
            payload = {**payload, "via_entity": seed}
            results.append(payload)
    return results
