"""@vault / @wiki mention parse · resolve · LLM context injection."""

from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Any, Protocol

_SNIPPET_MAX = 400
_HIT_LIMIT = 3

_MENTION_RE = re.compile(r"@(vault|wiki)\s+([^@\n]*)")


@dataclass(frozen=True)
class MentionRef:
    kind: str
    query: str
    span: tuple[int, int]


class VaultSearchLike(Protocol):
    def search(self, q: str, *, limit: int = 3) -> list[dict[str, Any]]: ...


class WikiSearchLike(Protocol):
    def search(self, query: str, *, limit: int = 3) -> list[Any]: ...


def parse_mentions(text: str) -> list[MentionRef]:
    """Parse @vault / @wiki refs; query runs until next @ or end of line."""
    if not (text or "").strip():
        return []
    refs: list[MentionRef] = []
    for match in _MENTION_RE.finditer(text):
        query = match.group(2).strip()
        if not query:
            continue
        refs.append(
            MentionRef(
                kind=match.group(1).lower(),
                query=query,
                span=(match.start(), match.end()),
            )
        )
    return refs


def _truncate_snippet(text: str) -> str:
    snippet = (text or "").strip()
    if len(snippet) <= _SNIPPET_MAX:
        return snippet
    return snippet[:_SNIPPET_MAX]


def _map_vault_hit(hit: dict[str, Any]) -> dict[str, str]:
    return {
        "vault_id": str(hit.get("vault_id") or ""),
        "title": str(hit.get("title") or ""),
        "snippet": _truncate_snippet(str(hit.get("snippet") or "")),
    }


def _map_wiki_hit(hit: Any) -> dict[str, str]:
    if isinstance(hit, dict):
        return {
            "page_id": str(hit.get("page_id") or ""),
            "title": str(hit.get("title") or ""),
            "snippet": _truncate_snippet(str(hit.get("summary") or hit.get("snippet") or "")),
        }
    return {
        "page_id": str(getattr(hit, "page_id", "") or ""),
        "title": str(getattr(hit, "title", "") or ""),
        "snippet": _truncate_snippet(str(getattr(hit, "summary", "") or "")),
    }


def resolve_mentions(
    refs: list[MentionRef],
    *,
    vault_search: VaultSearchLike | None = None,
    wiki_search: WikiSearchLike | None = None,
    limit: int = _HIT_LIMIT,
) -> dict[str, list[dict[str, str]]]:
    """Resolve mention refs to vault/wiki hits (max ``limit`` per kind)."""
    out: dict[str, list[dict[str, str]]] = {"vault": [], "wiki": []}
    if not refs:
        return out

    vault_seen: set[str] = set()
    wiki_seen: set[str] = set()

    for ref in refs:
        if ref.kind == "vault" and vault_search is not None and len(out["vault"]) < limit:
            for hit in vault_search.search(ref.query, limit=limit):
                row = _map_vault_hit(hit)
                vid = row["vault_id"]
                if not vid or vid in vault_seen:
                    continue
                vault_seen.add(vid)
                out["vault"].append(row)
                if len(out["vault"]) >= limit:
                    break
        elif ref.kind == "wiki" and wiki_search is not None and len(out["wiki"]) < limit:
            for hit in wiki_search.search(ref.query, limit=limit):
                row = _map_wiki_hit(hit)
                pid = row["page_id"]
                if not pid or pid in wiki_seen:
                    continue
                wiki_seen.add(pid)
                out["wiki"].append(row)
                if len(out["wiki"]) >= limit:
                    break
    return out


def format_mention_context(mentions: dict[str, list[dict[str, str]]]) -> str:
    """Format ephemeral LLM context block for resolved mentions."""
    blocks: list[str] = []
    vault_hits = mentions.get("vault") or []
    if vault_hits:
        lines = [
            f"- {h['title']} · {h['snippet']}"
            for h in vault_hits
            if h.get("title") or h.get("snippet")
        ]
        if lines:
            blocks.append("【@vault 检索】\n" + "\n".join(lines))
    wiki_hits = mentions.get("wiki") or []
    if wiki_hits:
        lines = [
            f"- {h['title']} · {h['snippet']}"
            for h in wiki_hits
            if h.get("title") or h.get("snippet")
        ]
        if lines:
            blocks.append("【@wiki 检索】\n" + "\n".join(lines))
    return "\n".join(blocks)
