import { hasBinary } from "../agents/skills.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { formatDocsLink } from "../terminal/links.js";
import { isRich, theme } from "../terminal/theme.js";
import { formatCliCommand } from "../cli/command-format.js";
const SEARCH_TOOL = "https://docs.clawd.bot/mcp.SearchClawdbot";
const SEARCH_TIMEOUT_MS = 30_000;
const DEFAULT_SNIPPET_MAX = 220;
function resolveNodeRunner() {
    if (hasBinary("pnpm"))
        return { cmd: "pnpm", args: ["dlx"] };
    if (hasBinary("npx"))
        return { cmd: "npx", args: ["-y"] };
    throw new Error("Missing pnpm or npx; install a Node package runner.");
}
async function runNodeTool(tool, toolArgs, options = {}) {
    const runner = resolveNodeRunner();
    const argv = [runner.cmd, ...runner.args, tool, ...toolArgs];
    return await runCommandWithTimeout(argv, {
        timeoutMs: options.timeoutMs ?? SEARCH_TIMEOUT_MS,
        input: options.input,
    });
}
async function runTool(tool, toolArgs, options = {}) {
    if (hasBinary(tool)) {
        return await runCommandWithTimeout([tool, ...toolArgs], {
            timeoutMs: options.timeoutMs ?? SEARCH_TIMEOUT_MS,
            input: options.input,
        });
    }
    return await runNodeTool(tool, toolArgs, options);
}
function extractLine(lines, prefix) {
    const line = lines.find((value) => value.startsWith(prefix));
    if (!line)
        return undefined;
    return line.slice(prefix.length).trim();
}
function normalizeSnippet(raw, fallback) {
    const base = raw && raw.trim().length > 0 ? raw : fallback;
    const cleaned = base.replace(/\s+/g, " ").trim();
    if (!cleaned)
        return "";
    if (cleaned.length <= DEFAULT_SNIPPET_MAX)
        return cleaned;
    return `${cleaned.slice(0, DEFAULT_SNIPPET_MAX - 3)}...`;
}
function firstParagraph(text) {
    const parts = text
        .split(/\n\s*\n/)
        .map((chunk) => chunk.trim())
        .filter(Boolean);
    return parts[0] ?? "";
}
function parseSearchOutput(raw) {
    const normalized = raw.replace(/\r/g, "");
    const blocks = normalized
        .split(/\n(?=Title: )/g)
        .map((chunk) => chunk.trim())
        .filter(Boolean);
    const results = [];
    for (const block of blocks) {
        const lines = block.split("\n");
        const title = extractLine(lines, "Title:");
        const link = extractLine(lines, "Link:");
        if (!title || !link)
            continue;
        const content = extractLine(lines, "Content:");
        const contentIndex = lines.findIndex((line) => line.startsWith("Content:"));
        const body = contentIndex >= 0
            ? lines
                .slice(contentIndex + 1)
                .join("\n")
                .trim()
            : "";
        const snippet = normalizeSnippet(content, firstParagraph(body));
        results.push({ title, link, snippet: snippet || undefined });
    }
    return results;
}
function escapeMarkdown(text) {
    return text.replace(/[()[\]]/g, "\\$&");
}
function buildMarkdown(query, results) {
    const lines = [`# Docs search: ${escapeMarkdown(query)}`, ""];
    if (results.length === 0) {
        lines.push("_No results._");
        return lines.join("\n");
    }
    for (const item of results) {
        const title = escapeMarkdown(item.title);
        const snippet = item.snippet ? escapeMarkdown(item.snippet) : "";
        const suffix = snippet ? ` - ${snippet}` : "";
        lines.push(`- [${title}](${item.link})${suffix}`);
    }
    return lines.join("\n");
}
function formatLinkLabel(link) {
    return link.replace(/^https?:\/\//i, "");
}
function renderRichResults(query, results, runtime) {
    runtime.log(`${theme.heading("Docs search:")} ${theme.info(query)}`);
    if (results.length === 0) {
        runtime.log(theme.muted("No results."));
        return;
    }
    for (const item of results) {
        const linkLabel = formatLinkLabel(item.link);
        const link = formatDocsLink(item.link, linkLabel);
        runtime.log(`${theme.muted("-")} ${theme.command(item.title)} ${theme.muted("(")}${link}${theme.muted(")")}`);
        if (item.snippet) {
            runtime.log(`  ${theme.muted(item.snippet)}`);
        }
    }
}
async function renderMarkdown(markdown, runtime) {
    runtime.log(markdown.trimEnd());
}
export async function docsSearchCommand(queryParts, runtime) {
    const query = queryParts.join(" ").trim();
    if (!query) {
        const docs = formatDocsLink("/", "docs.clawd.bot");
        if (isRich()) {
            runtime.log(`${theme.muted("Docs:")} ${docs}`);
            runtime.log(`${theme.muted("Search:")} ${formatCliCommand('openclaw-cn docs "your query"')}`);
        }
        else {
            runtime.log("Docs: https://docs.clawd.bot/");
            runtime.log(`Search: ${formatCliCommand('openclaw-cn docs "your query"')}`);
        }
        return;
    }
    const payload = JSON.stringify({ query });
    const res = await runTool("mcporter", ["call", SEARCH_TOOL, "--args", payload, "--output", "text"], { timeoutMs: SEARCH_TIMEOUT_MS });
    if (res.code !== 0) {
        const err = res.stderr.trim() || res.stdout.trim() || `exit ${res.code}`;
        runtime.error(`Docs search failed: ${err}`);
        runtime.exit(1);
        return;
    }
    const results = parseSearchOutput(res.stdout);
    if (isRich()) {
        renderRichResults(query, results, runtime);
        return;
    }
    const markdown = buildMarkdown(query, results);
    await renderMarkdown(markdown, runtime);
}
