import path from "node:path";
import { loadCcusageCompatibleSessionUsage } from "../integrations/ccusage.js";
import { pathExists, readJson, writeText } from "../utils/fs.js";
function measuredUsagePath(targetRoot) {
    return path.join(targetRoot, ".agent", "state", "measured-usage.json");
}
export async function buildMeasuredUsage(targetRoot) {
    const ccusageRuns = await loadCcusageCompatibleSessionUsage(targetRoot);
    const executionRuns = await loadMeasuredExecutionRuns(targetRoot);
    const runs = [
        ...ccusageRuns.map((run) => ({
            runId: run.sessionId,
            source: "ccusage-session",
            workflow: "codex-session",
            task: null,
            timestamp: run.lastActivity,
            totalTokens: run.totalTokens,
            inputTokens: run.inputTokens,
            cachedInputTokens: run.cachedInputTokens,
            outputTokens: run.outputTokens,
            reasoningOutputTokens: run.reasoningOutputTokens,
            files: []
        })),
        ...executionRuns
    ].sort((left, right) => right.timestamp.localeCompare(left.timestamp));
    const totalTokens = runs.reduce((sum, run) => sum + run.totalTokens, 0);
    const workflows = aggregateWorkflowUsage(runs);
    const files = aggregateFileUsage(executionRuns);
    const source = resolveUsageSource(ccusageRuns.length > 0, executionRuns.length > 0);
    return {
        artifactType: "kiwi-control/measured-usage",
        version: 1,
        timestamp: new Date().toISOString(),
        available: runs.length > 0,
        source,
        totalTokens,
        totalRuns: runs.length,
        runs,
        workflows,
        files,
        note: runs.length > 0
            ? buildMeasuredUsageNote(ccusageRuns.length > 0, executionRuns.length > 0, files.length > 0)
            : "No measured token usage was found for this repo. Falling back to heuristic estimates is expected."
    };
}
export async function persistMeasuredUsage(targetRoot, state) {
    const outputPath = measuredUsagePath(targetRoot);
    await writeText(outputPath, `${JSON.stringify(state, null, 2)}\n`);
    return outputPath;
}
export async function loadMeasuredUsage(targetRoot) {
    const outputPath = measuredUsagePath(targetRoot);
    if (!(await pathExists(outputPath))) {
        return buildMeasuredUsage(targetRoot);
    }
    try {
        const state = await readJson(outputPath);
        if (state.artifactType !== "kiwi-control/measured-usage" || state.version !== 1) {
            return buildMeasuredUsage(targetRoot);
        }
        return state;
    }
    catch {
        return buildMeasuredUsage(targetRoot);
    }
}
async function loadMeasuredExecutionRuns(targetRoot) {
    const executionLogPath = path.join(targetRoot, ".agent", "state", "execution-log.json");
    if (!(await pathExists(executionLogPath))) {
        return [];
    }
    try {
        const payload = await readJson(executionLogPath);
        return (payload.entries ?? [])
            .filter((entry) => entry.tokensUsed > 0)
            .map((entry, index) => ({
            runId: `${entry.timestamp}-${index}`,
            source: "execution-log",
            workflow: entry.completionSource ?? "execution-log",
            task: entry.task ?? null,
            timestamp: entry.timestamp,
            totalTokens: entry.tokensUsed,
            inputTokens: null,
            cachedInputTokens: null,
            outputTokens: null,
            reasoningOutputTokens: null,
            files: entry.filesTouched ?? []
        }));
    }
    catch {
        return [];
    }
}
function aggregateWorkflowUsage(runs) {
    const map = new Map();
    for (const run of runs) {
        const current = map.get(run.workflow) ?? { tokens: 0, runs: 0 };
        current.tokens += run.totalTokens;
        current.runs += 1;
        map.set(run.workflow, current);
    }
    return [...map.entries()]
        .map(([workflow, value]) => ({ workflow, tokens: value.tokens, runs: value.runs }))
        .sort((left, right) => right.tokens - left.tokens);
}
function aggregateFileUsage(runs) {
    const map = new Map();
    for (const run of runs) {
        if (run.files.length === 0 || run.totalTokens <= 0) {
            continue;
        }
        const perFileTokens = Math.round(run.totalTokens / run.files.length);
        for (const file of run.files) {
            const current = map.get(file) ?? { tokens: 0, runs: 0 };
            current.tokens += perFileTokens;
            current.runs += 1;
            map.set(file, current);
        }
    }
    return [...map.entries()]
        .map(([file, value]) => ({
        file,
        tokens: value.tokens,
        runs: value.runs,
        attribution: "allocated"
    }))
        .sort((left, right) => right.tokens - left.tokens);
}
function resolveUsageSource(hasCcusage, hasExecutionLog) {
    if (hasCcusage && hasExecutionLog) {
        return "mixed";
    }
    if (hasCcusage) {
        return "ccusage-session";
    }
    if (hasExecutionLog) {
        return "execution-log";
    }
    return "none";
}
function buildMeasuredUsageNote(hasCcusage, hasExecutionLog, hasFileUsage) {
    const parts = [];
    if (hasCcusage) {
        parts.push("Measured run usage came from Codex session logs using a ccusage-compatible parser.");
    }
    if (hasExecutionLog) {
        parts.push("Measured workflow and per-file views were enriched from repo-local execution-log entries with token totals.");
    }
    if (!hasFileUsage) {
        parts.push("Per-file measured attribution is only available when execution-log entries include non-zero token totals.");
    }
    return parts.join(" ");
}
