"""
title: Varaverk Docs
description: Search Varaverk's own documentation and return grounding context.
author: Varaverk
version: 1.0.0
required_open_webui_version: 0.4.0
"""

# ═══════════════════════════════════════════════════════════════════════════════════════════════
# PURPOSE
#   Open-WebUI tool that lets the chat model search Varaverk's documentation index and answer
#   from it, instead of from whatever it happens to remember about a private project it has
#   never seen.
#
# OPERATIONAL MODEL
#   Not installed by any Varaverk script. Open-WebUI stores tools in its own database, and
#   writing there directly would mean guessing at its schema, IDs and access control on a live
#   app. Paste this into Open-WebUI → Workspace → Tools → +, then set the two Valves.
#
#   Calls AI/ai_serve.js over HTTP because Open-WebUI runs in a container that cannot see
#   Varaverk's filesystem and has no WebGUI session, so the plugin's PHP API is unreachable to
#   it. The bridge returns chunks; the model already loaded in Open-WebUI does the generating.
#
# CONFIGURATION (Valves — set in the Open-WebUI tool editor, not here)
#   base_url   http://<host-lan-ip>:7822   — AI_HTTP_PORT from master.conf
#   secret     AI_HTTP_SECRET from master.conf
#
#   The secret is a Valve rather than a constant so this file stays committable. Do not paste
#   it into the code — this path is git-tracked and pushed to a remote.
#
# OPERATIONAL SAFEGUARDS
#   Retrieval only. The bridge exposes one read-only verb over documentation already in git;
#   this tool cannot write conf, run a script, or change anything.
#
#   Returns "no relevant documentation found" rather than an empty string on a miss, so the
#   model states that plainly instead of filling the silence from memory — the failure this
#   whole retrieval path exists to prevent.
#
#   Every failure is returned as readable text, never raised. An exception inside a tool call
#   surfaces to the user as an opaque error; a sentence explaining that the bridge is
#   unreachable is something they can act on.
#
#   Time-boxed at 30s. A hung retrieval must not hold the chat turn open indefinitely.
# ═══════════════════════════════════════════════════════════════════════════════════════════════

import json
import urllib.parse
import urllib.request

from pydantic import BaseModel, Field


class Tools:
    class Valves(BaseModel):
        base_url: str = Field(
            default="http://192.168.50.2:7822",
            description="AI retrieval bridge — host LAN IP and AI_HTTP_PORT. Not localhost: "
                        "Open-WebUI is a container and localhost is itself.",
        )
        secret: str = Field(
            default="",
            description="AI_HTTP_SECRET from Configurations/master.conf",
        )
        results: int = Field(
            default=8,
            description="Chunks to retrieve per query (1-25)",
        )

    def __init__(self):
        self.valves = self.Valves()

    def search_varaverk_docs(self, query: str, kind: str = "") -> str:
        """
        Search the Varaverk documentation index for passages relevant to a question about
        this specific home-media system: its scripts, configuration variables, safeguards,
        orchestrators, rsync behaviour, watchdogs, fallback logic or plugin internals.

        Always use this before answering any question about Varaverk. Varaverk is a private
        project and is not in your training data; without this tool you do not know what it
        is and must not guess.

        :param query: The question or topic to search for, in natural language.
        :param kind: Optional filter on where the text comes from. Use "readme" for
            definitional or narrative questions such as "what is Varaverk" or "why does this
            exist" — otherwise per-script header sections outrank the top-level prose and the
            answer will look absent when it is not. Leave empty for specific technical
            questions. One of: header, readme, manual, template, doc.
        :return: Numbered passages with their source paths, or a message saying nothing matched.
        """
        if not self.valves.secret:
            return ("The Varaverk docs tool is not configured: its 'secret' Valve is empty. "
                    "Set it to AI_HTTP_SECRET from Configurations/master.conf.")

        params = {
            "key": self.valves.secret,
            "q": query,
            "k": max(1, min(int(self.valves.results), 25)),
        }
        if kind:
            params["kind"] = kind

        url = f"{self.valves.base_url.rstrip('/')}/search?" + urllib.parse.urlencode(params)

        try:
            with urllib.request.urlopen(url, timeout=30) as r:
                data = json.load(r)
        except Exception as e:
            return (f"Could not reach the Varaverk retrieval bridge at "
                    f"{self.valves.base_url} ({e}). It is started by AI/start_ai_server.sh; "
                    f"check that AI_ENABLED and AI_HTTP_PORT are set and the index exists.")

        if not data.get("ok"):
            return f"Varaverk retrieval failed: {data.get('error', 'unknown error')}"

        results = data.get("results") or []
        if not results:
            return (f"No relevant documentation found for '{query}'. Say so plainly rather "
                    f"than answering from general knowledge — Varaverk is private and is not "
                    f"in your training data.")

        out = [f"{len(results)} passage(s) from the Varaverk documentation index:", ""]
        for i, r in enumerate(results, 1):
            label = " › ".join(x for x in (r.get("path"), r.get("section"), r.get("heading")) if x)
            out.append(f"[{i}] {label}  (score {r.get('score')})")
            out.append(r.get("content", "").strip())
            out.append("")
        out.append("Answer only from the passages above, and cite them by their [n] markers. "
                   "If they do not contain the answer, say so and name what is missing.")
        return "\n".join(out)
