import { readFile } from "node:fs/promises";
import { getCumulativeSavings, getUsagePath } from "./usage-tracker.js";
import { formatTable } from "../formatters.js";
// ---------------------------------------------------------------------------
// Loading
// ---------------------------------------------------------------------------
function isValidEntry(value) {
    if (typeof value !== "object" || value === null)
        return false;
    const obj = value;
    return (typeof obj["ts"] === "number" &&
        typeof obj["tool"] === "string" &&
        typeof obj["elapsed_ms"] === "number" &&
        typeof obj["session_id"] === "string");
}
async function loadEntries(options) {
    const usagePath = getUsagePath();
    let raw;
    try {
        raw = await readFile(usagePath, "utf-8");
    }
    catch {
        return [];
    }
    const sinceTs = options?.since ? new Date(options.since).getTime() : 0;
    const repoFilter = options?.repo ?? null;
    const toolFilter = options?.tool ?? null;
    const sessionFilter = options?.session_id ?? null;
    const entries = [];
    for (const line of raw.split("\n")) {
        const trimmed = line.trim();
        if (!trimmed)
            continue;
        try {
            const parsed = JSON.parse(trimmed);
            if (!isValidEntry(parsed))
                continue;
            if (parsed.ts < sinceTs)
                continue;
            if (repoFilter && parsed.repo !== repoFilter)
                continue;
            if (toolFilter && parsed.tool !== toolFilter)
                continue;
            if (sessionFilter && parsed.session_id !== sessionFilter)
                continue;
            entries.push(parsed);
        }
        catch {
            // Skip malformed lines
        }
    }
    return entries;
}
// ---------------------------------------------------------------------------
// Aggregation
// ---------------------------------------------------------------------------
export async function getUsageStats(options) {
    const entries = await loadEntries(options);
    if (entries.length === 0) {
        return {
            total_calls: 0,
            total_sessions: 0,
            avg_calls_per_session: 0,
            tools: [],
            top_repos: [],
            daily: [],
            query_types: [],
            earliest_ts: 0,
            latest_ts: 0,
        };
    }
    // Per-tool aggregation
    const toolMap = new Map();
    const repoMap = new Map();
    const sessionSet = new Set();
    const dailyMap = new Map();
    // codebase_retrieval query type breakdown
    const queryTypeQueries = new Map(); // type → total query count
    const queryTypeCalls = new Map(); // type → calls containing this type
    let earliest = Infinity;
    let latest = 0;
    for (const entry of entries) {
        // Tool stats
        const existing = toolMap.get(entry.tool) ?? { calls: 0, tokens: 0, elapsed: 0 };
        existing.calls += 1;
        existing.tokens += entry.result_tokens;
        existing.elapsed += entry.elapsed_ms;
        toolMap.set(entry.tool, existing);
        // Repo stats (skip empty)
        if (entry.repo) {
            repoMap.set(entry.repo, (repoMap.get(entry.repo) ?? 0) + 1);
        }
        // Sessions
        sessionSet.add(entry.session_id);
        // Daily
        const date = new Date(entry.ts).toISOString().slice(0, 10);
        const dayStats = dailyMap.get(date) ?? { calls: 0, tokens: 0 };
        dayStats.calls += 1;
        dayStats.tokens += entry.result_tokens;
        dailyMap.set(date, dayStats);
        // codebase_retrieval query type breakdown
        if (entry.tool === "codebase_retrieval") {
            const types = entry.args_summary?.["query_types"];
            if (Array.isArray(types)) {
                const seenInCall = new Set();
                for (const t of types) {
                    if (typeof t === "string") {
                        queryTypeQueries.set(t, (queryTypeQueries.get(t) ?? 0) + 1);
                        seenInCall.add(t);
                    }
                }
                for (const t of seenInCall) {
                    queryTypeCalls.set(t, (queryTypeCalls.get(t) ?? 0) + 1);
                }
            }
        }
        // Time range
        if (entry.ts < earliest)
            earliest = entry.ts;
        if (entry.ts > latest)
            latest = entry.ts;
    }
    // Build sorted arrays
    const tools = [...toolMap.entries()]
        .map(([tool, stats]) => ({
        tool,
        total_calls: stats.calls,
        total_result_tokens: stats.tokens,
        avg_elapsed_ms: Math.round(stats.elapsed / stats.calls),
        avg_result_tokens: Math.round(stats.tokens / stats.calls),
    }))
        .sort((a, b) => b.total_calls - a.total_calls);
    const top_repos = [...repoMap.entries()]
        .map(([repo, call_count]) => ({ repo, call_count }))
        .sort((a, b) => b.call_count - a.call_count)
        .slice(0, 20);
    const daily = [...dailyMap.entries()]
        .map(([date, stats]) => ({
        date,
        call_count: stats.calls,
        total_tokens: stats.tokens,
    }))
        .sort((a, b) => a.date.localeCompare(b.date));
    const totalSessions = sessionSet.size;
    const query_types = [...queryTypeQueries.entries()]
        .map(([type, query_count]) => ({
        type,
        query_count,
        call_count: queryTypeCalls.get(type) ?? 0,
    }))
        .sort((a, b) => b.query_count - a.query_count);
    return {
        total_calls: entries.length,
        total_sessions: totalSessions,
        avg_calls_per_session: Math.round((entries.length / totalSessions) * 10) / 10,
        tools,
        top_repos,
        daily,
        query_types,
        earliest_ts: earliest,
        latest_ts: latest,
    };
}
// ---------------------------------------------------------------------------
// Formatting
// ---------------------------------------------------------------------------
export function formatUsageReport(stats) {
    if (stats.total_calls === 0) {
        return "No usage data recorded yet.";
    }
    const lines = [];
    // Header
    const earliest = new Date(stats.earliest_ts).toISOString().slice(0, 10);
    const latest = new Date(stats.latest_ts).toISOString().slice(0, 10);
    lines.push("=== CodeSift Usage Report ===");
    lines.push(`Period: ${earliest} to ${latest}`);
    lines.push(`Total calls: ${stats.total_calls}`);
    lines.push(`Total sessions: ${stats.total_sessions}`);
    lines.push(`Avg calls/session: ${stats.avg_calls_per_session}`);
    lines.push("");
    // Tool breakdown
    lines.push("--- Tool Breakdown ---");
    const toolRows = stats.tools.map((t) => [
        t.tool,
        String(t.total_calls),
        String(t.total_result_tokens),
        String(t.avg_elapsed_ms),
        String(t.avg_result_tokens),
    ]);
    lines.push(formatTable(["Tool", "Calls", "Tokens", "Avg ms", "Avg tok"], toolRows));
    lines.push("");
    // codebase_retrieval query type breakdown
    if (stats.query_types.length > 0) {
        const crTool = stats.tools.find((t) => t.tool === "codebase_retrieval");
        const totalQueries = stats.query_types.reduce((sum, qt) => sum + qt.query_count, 0);
        lines.push("--- codebase_retrieval Query Types ---");
        const qtRows = stats.query_types.map((qt) => {
            const pct = totalQueries > 0 ? Math.round((qt.query_count / totalQueries) * 100) : 0;
            return [
                qt.type,
                String(qt.query_count),
                `${pct}%`,
                `${qt.call_count}/${crTool?.total_calls ?? "?"}`,
            ];
        });
        lines.push(formatTable(["Type", "Queries", "%", "In calls"], qtRows));
        lines.push("");
    }
    // Top repos
    if (stats.top_repos.length > 0) {
        lines.push("--- Top Repos ---");
        for (const r of stats.top_repos) {
            lines.push(`  ${r.repo}: ${r.call_count} calls`);
        }
        lines.push("");
    }
    // Daily breakdown (last 14 days max)
    if (stats.daily.length > 0) {
        lines.push("--- Daily Usage ---");
        const recentDays = stats.daily.slice(-14);
        for (const d of recentDays) {
            lines.push(`  ${d.date}: ${d.call_count} calls, ${d.total_tokens} tokens`);
        }
        if (stats.daily.length > 14) {
            lines.push(`  ... and ${stats.daily.length - 14} earlier days`);
        }
    }
    const saved = getCumulativeSavings();
    if (saved > 0) {
        lines.push(`\nEstimated tokens saved this session: ${saved.toLocaleString()} (~$${(saved * 30 / 1_000_000).toFixed(2)} at Opus rates)`);
    }
    return lines.join("\n");
}
//# sourceMappingURL=usage-stats.js.map