"""Inspector tools: operate on already-returned results."""

import logging
from copy import deepcopy

_LOG = logging.getLogger(__name__)


async def open_context(tools, chunk_id: str, width: int = 500) -> dict:
    """Expand context around a chunk by looking up adjacent chunks in kbinfos."""
    chunks = tools.kbinfos.get("chunks", [])
    idx = _find_chunk_index(chunks, chunk_id)
    if idx is None:
        return {"chunks": [], "doc_aggs": []}
    start = max(0, idx - 2)
    end = min(len(chunks), idx + 2)
    context = chunks[start:end]
    _LOG.info("open_context: chunk=%s index=%d context=%d chunks", chunk_id, idx, len(context))
    return {"chunks": context, "doc_aggs": _collect_doc_aggs(tools, context)}


async def compare_sources(tools, chunk_ids: list[str]) -> dict:
    """Find chunks in kbinfos and list the document sources they come from."""
    if not chunk_ids:
        return {"chunks": [], "doc_aggs": []}
    chunks = tools.kbinfos.get("chunks", [])
    matched = [c for c in chunks if _chunk_id(c) in chunk_ids]
    return {"chunks": matched, "doc_aggs": _collect_doc_aggs(tools, matched)}


async def grep_within(tools, doc_id: str, pattern: str) -> dict:
    """Find a keyword within a document and return its chunks narrowed to the
    matching sentences (+/- 1 neighbour).

    ``pattern`` is treated as the keyword string (comma-separate for several).
    Delegates to :func:`_narrow_by_keywords`, which keeps only keyword-bearing
    sentences and drops chunks with no match. Operates on copies so the shared
    ``kbinfos`` citation pool is never mutated.
    """
    from rag.advanced_rag.harness.tools.search import _narrow_by_keywords

    chunks = [deepcopy(c) for c in tools.kbinfos.get("chunks", []) if c.get("doc_id") == doc_id]
    matched = _narrow_by_keywords(chunks, pattern)
    return {"chunks": matched, "doc_aggs": _collect_doc_aggs(tools, matched)}


async def request_adjacent(tools, chunk_id: str, direction: str = "next", count: int = 3) -> dict:
    """Get adjacent entries before or after a chunk."""
    chunks = tools.kbinfos.get("chunks", [])
    idx = _find_chunk_index(chunks, chunk_id)
    if idx is None:
        return {"chunks": [], "doc_aggs": []}

    if direction == "prev":
        start = max(0, idx - count)
        end = idx
    else:
        start = idx + 1
        end = min(len(chunks), start + count)

    adjacent = chunks[start:end]
    return {"chunks": adjacent, "doc_aggs": _collect_doc_aggs(tools, adjacent)}


# Helpers


def _find_chunk_index(chunks: list[dict], chunk_id: str) -> int | None:
    for i, c in enumerate(chunks):
        if _chunk_id(c) == chunk_id:
            return i
    return None


def _chunk_id(ck: dict) -> str:
    return str(ck.get("chunk_id") or ck.get("id") or "")


def _collect_doc_aggs(tools, chunks: list[dict]) -> list[dict]:
    seen = set()
    aggs = []
    for c in chunks:
        doc_id = c.get("doc_id")
        if doc_id and doc_id not in seen:
            seen.add(doc_id)
            aggs.append({"doc_id": doc_id, "doc_name": c.get("docnm_kwd", "")})
    return aggs
