/**
 * Builds a tool-call usage report from today's JSONL log file(s).
 *
 * Scans `<configDir>/logs/<today>*.jsonl` for entries with
 * `category === "tool_call"`, groups them by tool name, and produces a
 * Telegram-friendly plain-text report with bar charts, categories, deltas
 * vs. the previous invocation, and a Top-5 ranking.
 *
 * State (previous counts) is persisted to `<configDir>/.toolcall_state.json`
 * so each invocation can show change trends.
 */
import fs from "node:fs";
import fsp from "node:fs/promises";
import path from "node:path";
import readline from "node:readline";
import { createGunzip } from "node:zlib";
import { getConfigDir } from "../config/index.js";
// ---------------------------------------------------------------------------
// Friendly display names
// ---------------------------------------------------------------------------
const FRIENDLY = {
    mcp__visionclaw__finish: "Finish",
    mcp__visionclaw__notify_user: "Notify User",
    mcp__visionclaw__memory: "Memory",
    mcp__visionclaw__upgrade: "Upgrade",
    mcp__visionclaw__manage_calendar: "Calendar",
    mcp__visionclaw__manage_drive: "Drive",
    mcp__visionclaw__manage_email: "Email",
    mcp__visionclaw__manage_skills: "Skills",
    mcp__visionclaw__manage_mcp_servers: "MCP Servers",
    mcp__visionclaw__switch_session: "Switch Session",
    mcp__visionclaw__wait: "Wait",
    mcp__visionclaw__computer_use_click: "Click (Desktop)",
    mcp__visionclaw__computer_use_screenshot: "Screenshot (Desktop)",
    mcp__visionclaw__computer_use_type: "Type (Desktop)",
    mcp__visionclaw__computer_use_key: "Key (Desktop)",
    mcp__visionclaw__computer_use_scroll: "Scroll (Desktop)",
    mcp__visionclaw__computer_use_move: "Move (Desktop)",
    mcp__visionclaw__computer_use_drag: "Drag (Desktop)",
    mcp__serpapi__search: "Web Search",
    mcp__playwright__browser_navigate: "Navigate (Browser)",
    mcp__playwright__browser_snapshot: "Snapshot (Browser)",
    mcp__playwright__browser_click: "Click (Browser)",
    mcp__playwright__browser_fill_form: "Fill Form (Browser)",
    mcp__playwright__browser_take_screenshot: "Screenshot (Browser)",
    mcp__playwright__browser_type: "Type (Browser)",
    mcp__playwright__browser_close: "Close (Browser)",
    ToolSearch: "Tool Search",
};
// ---------------------------------------------------------------------------
// Category definitions
// ---------------------------------------------------------------------------
const CAT_INFO = {
    system: ["\u{1F527}", "System"],
    browser: ["\u{1F310}", "Browser"],
    desktop: ["\u{1F5A5}", "Desktop"],
    coding: ["\u{1F4BB}", "Coding"],
    search: ["\u{1F50D}", "Search"],
    other: ["\u{1F4E6}", "Other"],
};
const CODING_TOOLS = new Set([
    "Bash", "Write", "Read", "Edit", "Grep", "Glob",
    "ToolSearch", "NotebookEdit", "WebFetch", "Agent",
]);
function categorize(name) {
    const low = name.toLowerCase();
    if (low.includes("playwright") || low.includes("playwriter") || low.includes("browser"))
        return "browser";
    if (low.includes("computer_use"))
        return "desktop";
    if (low.includes("visionclaw"))
        return "system";
    if (low.includes("serpapi") || low.includes("search"))
        return "search";
    if (CODING_TOOLS.has(name))
        return "coding";
    return "other";
}
function friendly(name) {
    return FRIENDLY[name] ?? name;
}
function bar(count, maximum, width = 12) {
    const filled = maximum > 0 ? Math.round((count / maximum) * width) : 0;
    return "\u{2593}".repeat(filled) + "\u{2591}".repeat(width - filled);
}
function deltaStr(current, previous) {
    const diff = current - previous;
    if (diff > 0)
        return ` (+${diff} \u{2191})`;
    if (diff < 0)
        return ` (${diff} \u{2193})`;
    return "";
}
function stateFilePath() {
    return path.join(getConfigDir(), ".toolcall_state.json");
}
async function loadState() {
    try {
        const raw = await fsp.readFile(stateFilePath(), "utf-8");
        const data = JSON.parse(raw);
        return {
            counts: data.counts ?? {},
            total: data.total ?? 0,
            time: data.time ?? "",
        };
    }
    catch {
        return { counts: {}, total: 0, time: "" };
    }
}
async function saveState(counts, total) {
    const data = {
        counts,
        total,
        time: new Date().toISOString().replace("T", " ").slice(0, 19),
    };
    try {
        await fsp.writeFile(stateFilePath(), JSON.stringify(data, null, 2), "utf-8");
    }
    catch {
        // best-effort
    }
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Match the logger's local-date format: YYYY-MM-DD */
function todayLocalDate() {
    const d = new Date();
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, "0");
    const day = String(d.getDate()).padStart(2, "0");
    return `${y}-${m}-${day}`;
}
async function countToolsInFile(filePath) {
    const tools = {};
    const isGz = filePath.endsWith(".gz");
    const raw = fs.createReadStream(filePath);
    const input = isGz ? raw.pipe(createGunzip()) : raw;
    if (!isGz) {
        raw.setEncoding("utf-8");
    }
    const rl = readline.createInterface({ input, crlfDelay: Infinity });
    try {
        for await (const line of rl) {
            if (!line)
                continue;
            let obj;
            try {
                obj = JSON.parse(line);
            }
            catch {
                continue;
            }
            if (obj.category === "tool_call") {
                const tname = obj.data?.name ??
                    obj.tool_name ??
                    (typeof obj.message === "string" ? obj.message : undefined) ??
                    "unknown";
                tools[tname] = (tools[tname] ?? 0) + 1;
            }
        }
    }
    catch {
        // file unreadable — return whatever we got
    }
    return tools;
}
// ---------------------------------------------------------------------------
// Report formatting (pure computation, no I/O)
// ---------------------------------------------------------------------------
function formatReport(grand, dateLabel, prevCounts, prevTotal, prevTime) {
    const total = Object.values(grand).reduce((a, b) => a + b, 0);
    const mx = Math.max(...Object.values(grand));
    const hasPrev = Object.keys(prevCounts).length > 0;
    const totalDiff = total - prevTotal;
    const byCat = new Map();
    for (const [name, cnt] of Object.entries(grand)) {
        const cat = categorize(name);
        const list = byCat.get(cat) ?? [];
        list.push([name, cnt]);
        byCat.set(cat, list);
    }
    const catOrder = [...byCat.keys()].sort((a, b) => {
        const sumA = (byCat.get(a) ?? []).reduce((s, [, v]) => s + v, 0);
        const sumB = (byCat.get(b) ?? []).reduce((s, [, v]) => s + v, 0);
        return sumB - sumA;
    });
    const L = [];
    L.push("\u{1F4CA} Tool Call Report");
    L.push(`\u{1F4C5} ${dateLabel}`);
    L.push("\u{2501}".repeat(22));
    let totalLine = `\u{1F522} Total: ${total}`;
    if (hasPrev) {
        if (totalDiff > 0)
            totalLine += `  (+${totalDiff} \u{2191})`;
        else if (totalDiff < 0)
            totalLine += `  (${totalDiff} \u{2193})`;
        else
            totalLine += "  (unchanged)";
    }
    L.push(totalLine);
    if (hasPrev && prevTime) {
        L.push(`\u{1F4CB} vs. last: ${prevTime}  |  prev: ${prevTotal}`);
    }
    L.push("");
    for (const cat of catOrder) {
        const items = byCat.get(cat) ?? [];
        const [emoji, label] = CAT_INFO[cat] ?? ["\u{1F4E6}", cat.charAt(0).toUpperCase() + cat.slice(1)];
        const catTotal = items.reduce((s, [, v]) => s + v, 0);
        const pct = ((catTotal / total) * 100).toFixed(0);
        const prevCatTotal = items.reduce((s, [n]) => s + (prevCounts[n] ?? 0), 0);
        const catDiff = catTotal - prevCatTotal;
        let catDelta = "";
        if (hasPrev && catDiff !== 0) {
            catDelta = catDiff > 0 ? `  +${catDiff}` : `  ${catDiff}`;
        }
        L.push(`${emoji} ${label} \u{2014} ${catTotal} (${pct}%)${catDelta}`);
        const sorted = [...items].sort((a, b) => b[1] - a[1]);
        for (const [name, cnt] of sorted) {
            const b = bar(cnt, mx);
            const prevCnt = prevCounts[name] ?? 0;
            const d = hasPrev ? deltaStr(cnt, prevCnt) : "";
            L.push(`   ${b} ${friendly(name)}: ${cnt}${d}`);
        }
        L.push("");
    }
    L.push("\u{1F3C6} Top 5");
    const top5 = Object.entries(grand)
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5);
    const medals = { 1: "\u{1F947}", 2: "\u{1F948}", 3: "\u{1F949}" };
    for (let i = 0; i < top5.length; i++) {
        const [name, cnt] = top5[i];
        const icon = medals[i + 1] ?? ` ${i + 1}.`;
        const prevCnt = prevCounts[name] ?? 0;
        const d = hasPrev ? deltaStr(cnt, prevCnt) : "";
        L.push(`  ${icon} ${friendly(name)}: ${cnt}${d}`);
    }
    return L.join("\n");
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
const MAX_LOG_FILES_PER_DAY = 50;
/**
 * Build a tool-call usage report for today only.
 * Reads all of today's log files from `<configDir>/logs/` — both the main
 * `YYYY-MM-DD.jsonl`, rotated `YYYY-MM-DD-N.jsonl`, and compressed
 * `YYYY-MM-DD.jsonl.gz` files.
 * Capped at {@link MAX_LOG_FILES_PER_DAY} files as a safety guard.
 */
export async function buildToolUsageReport() {
    const logsDir = path.join(getConfigDir(), "logs");
    const today = todayLocalDate();
    let allFiles;
    try {
        allFiles = await fsp.readdir(logsDir);
    }
    catch {
        return `No log file for today (${today}).`;
    }
    const todayFiles = allFiles
        .filter((f) => {
        if (!f.startsWith(today))
            return false;
        return f.endsWith(".jsonl") || f.endsWith(".jsonl.gz");
    })
        .sort();
    if (todayFiles.length === 0) {
        return `No log file for today (${today}).`;
    }
    const capped = todayFiles.length > MAX_LOG_FILES_PER_DAY;
    const filesToScan = capped ? todayFiles.slice(0, MAX_LOG_FILES_PER_DAY) : todayFiles;
    const grand = {};
    for (const f of filesToScan) {
        const fileCounts = await countToolsInFile(path.join(logsDir, f));
        for (const [name, cnt] of Object.entries(fileCounts)) {
            grand[name] = (grand[name] ?? 0) + cnt;
        }
    }
    if (Object.keys(grand).length === 0) {
        return `No tool calls found for today (${today}).`;
    }
    const { counts: prevCounts, total: prevTotal, time: prevTime } = await loadState();
    const total = Object.values(grand).reduce((a, b) => a + b, 0);
    let report = formatReport(grand, today, prevCounts, prevTotal, prevTime);
    if (capped) {
        report += `\n\n\u{26A0} Scanned ${filesToScan.length} of ${todayFiles.length} log files (cap reached).`;
    }
    await saveState(grand, total);
    return report;
}
//# sourceMappingURL=tool-usage.js.map