import path from "node:path";
import { recordContextFeedback } from "./context-feedback.js";
import { inspectGitState } from "./git.js";
import { loadPreparedScope, validateTouchedFilesAgainstAllowedFiles } from "./prepared-scope.js";
import { loadWorkflowState } from "./workflow-engine.js";
import { pathExists, readJson, writeText } from "../utils/fs.js";
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const MAX_ENTRIES = 100;
// ---------------------------------------------------------------------------
// Load / persist
// ---------------------------------------------------------------------------
function logPath(targetRoot) {
    return path.join(targetRoot, ".agent", "state", "execution-log.json");
}
export async function loadExecutionLog(targetRoot) {
    const fp = logPath(targetRoot);
    if (!(await pathExists(fp))) {
        return {
            artifactType: "kiwi-control/execution-log",
            version: 2,
            entries: [],
            totalExecutions: 0,
            totalTokensUsed: 0,
            averageTokensPerRun: 0,
            successRate: 0
        };
    }
    return readJson(fp);
}
export async function persistExecutionLog(targetRoot, state) {
    const fp = logPath(targetRoot);
    await writeText(fp, `${JSON.stringify(state, null, 2)}\n`);
    return fp;
}
// ---------------------------------------------------------------------------
// Record execution
// ---------------------------------------------------------------------------
export async function recordExecution(targetRoot, entry) {
    const state = await loadExecutionLog(targetRoot);
    if (entry.runKey && state.entries.some((existing) => existing.runKey === entry.runKey)) {
        return state;
    }
    const fullEntry = {
        ...entry,
        timestamp: new Date().toISOString()
    };
    const entries = [fullEntry, ...state.entries].slice(0, MAX_ENTRIES);
    const totalExecutions = state.totalExecutions + 1;
    const totalTokensUsed = state.totalTokensUsed + entry.tokensUsed;
    const averageTokensPerRun = totalExecutions > 0
        ? Math.round(totalTokensUsed / totalExecutions)
        : 0;
    const successCount = entries.filter((e) => e.success).length;
    const successRate = entries.length > 0
        ? Math.round((successCount / entries.length) * 100)
        : 0;
    const updated = {
        artifactType: "kiwi-control/execution-log",
        version: 2,
        entries,
        totalExecutions,
        totalTokensUsed,
        averageTokensPerRun,
        successRate
    };
    await persistExecutionLog(targetRoot, updated);
    return updated;
}
export async function recordPreparedScopeCompletion(targetRoot, options) {
    const preparedScope = await loadPreparedScope(targetRoot);
    if (!preparedScope) {
        return {
            evaluated: false,
            recorded: false,
            validation: null,
            entry: null,
            feedbackRecorded: false
        };
    }
    const gitState = await inspectGitState(targetRoot);
    if (!gitState.isGitRepo || gitState.changedFiles.length === 0) {
        return {
            evaluated: false,
            recorded: false,
            validation: null,
            entry: null,
            feedbackRecorded: false
        };
    }
    const validation = validateTouchedFilesAgainstAllowedFiles(preparedScope.allowedFiles, gitState.changedFiles);
    if (validation.touchedFiles.length === 0) {
        return {
            evaluated: false,
            recorded: false,
            validation,
            entry: null,
            feedbackRecorded: false
        };
    }
    const runKey = buildPreparedScopeRunKey(preparedScope.timestamp, validation.touchedFiles);
    const state = await loadExecutionLog(targetRoot);
    const workflow = await loadWorkflowState(targetRoot).catch(() => null);
    if (state.entries.some((entry) => entry.runKey === runKey)) {
        const existingEntry = state.entries.find((entry) => entry.runKey === runKey) ?? null;
        const feedbackRecorded = await maybeRecordAdaptiveFeedback(targetRoot, {
            preparedTask: preparedScope.task,
            allowedFiles: preparedScope.allowedFiles,
            touchedFiles: validation.touchedFiles,
            validationOk: validation.ok,
            confidence: existingEntry?.confidence ?? options.confidence ?? "unknown",
            tokensUsed: existingEntry?.tokensUsed ?? Math.max(0, options.tokensUsed ?? 0),
            runKey,
            completionSource: options.completionSource
        });
        return {
            evaluated: true,
            recorded: false,
            validation,
            entry: existingEntry,
            feedbackRecorded
        };
    }
    const entry = {
        task: preparedScope.task,
        tokensUsed: Math.max(0, options.tokensUsed ?? 0),
        filesTouched: validation.touchedFiles,
        success: validation.ok,
        duration: null,
        tool: options.tool ?? null,
        confidence: options.confidence ?? "unknown",
        runKey,
        completionSource: options.completionSource,
        workflow: workflow?.task ?? preparedScope.task,
        workflowStepId: workflow?.currentStepId ?? null,
        skillsApplied: workflow?.steps.find((step) => step.stepId === workflow.currentStepId)?.skillsApplied ?? [],
        tokenSource: (options.tokensUsed ?? 0) > 0 ? "measured" : "none",
        ...(validation.ok ? {} : { outOfScopeFiles: validation.outOfScopeFiles })
    };
    await recordExecution(targetRoot, entry);
    const feedbackRecorded = await maybeRecordAdaptiveFeedback(targetRoot, {
        preparedTask: preparedScope.task,
        allowedFiles: preparedScope.allowedFiles,
        touchedFiles: validation.touchedFiles,
        validationOk: validation.ok,
        confidence: entry.confidence,
        tokensUsed: entry.tokensUsed,
        runKey,
        completionSource: options.completionSource
    });
    return {
        evaluated: true,
        recorded: true,
        validation,
        entry: {
            ...entry,
            timestamp: new Date().toISOString()
        },
        feedbackRecorded
    };
}
export async function buildExecutionSummary(targetRoot) {
    const state = await loadExecutionLog(targetRoot);
    const recentExecutions = state.entries.slice(0, 5).map((e) => ({
        task: e.task,
        success: e.success,
        tokensUsed: e.tokensUsed,
        filesTouched: e.filesTouched.length,
        tool: e.tool,
        timestamp: e.timestamp
    }));
    // Compute token trend from last 5 vs previous 5
    let tokenTrend = "insufficient-data";
    if (state.entries.length >= 6) {
        const recent5 = state.entries.slice(0, 5);
        const prev5 = state.entries.slice(5, 10);
        if (prev5.length >= 3) {
            const recentAvg = recent5.reduce((s, e) => s + e.tokensUsed, 0) / recent5.length;
            const prevAvg = prev5.reduce((s, e) => s + e.tokensUsed, 0) / prev5.length;
            if (recentAvg < prevAvg * 0.85)
                tokenTrend = "improving";
            else if (recentAvg > prevAvg * 1.15)
                tokenTrend = "worsening";
            else
                tokenTrend = "stable";
        }
    }
    return {
        totalExecutions: state.totalExecutions,
        totalTokensUsed: state.totalTokensUsed,
        averageTokensPerRun: state.averageTokensPerRun,
        successRate: state.successRate,
        recentExecutions,
        tokenTrend
    };
}
function buildPreparedScopeRunKey(scopeTimestamp, touchedFiles) {
    const sortedTouchedFiles = [...touchedFiles].sort();
    return `${scopeTimestamp}::${sortedTouchedFiles.join("|")}`;
}
async function maybeRecordAdaptiveFeedback(targetRoot, options) {
    if (!isFeedbackCompletionSource(options.completionSource)) {
        return false;
    }
    const feedbackCandidateFiles = options.allowedFiles.filter((file) => !file.startsWith(".agent/"));
    if (!options.validationOk || options.touchedFiles.length === 0 || feedbackCandidateFiles.length === 0) {
        return false;
    }
    const usedFiles = options.touchedFiles.filter((file) => feedbackCandidateFiles.includes(file));
    if (usedFiles.length === 0) {
        return false;
    }
    const unusedFiles = feedbackCandidateFiles.filter((file) => !usedFiles.includes(file));
    await recordContextFeedback(targetRoot, {
        task: options.preparedTask,
        selectedFiles: feedbackCandidateFiles,
        usedFiles,
        unusedFiles,
        success: true,
        confidence: options.confidence,
        tokensSaved: options.tokensUsed,
        runKey: options.runKey,
        completionSource: options.completionSource
    });
    return true;
}
function isFeedbackCompletionSource(completionSource) {
    return completionSource === "checkpoint" || completionSource === "handoff";
}
