import path from "node:path";
import { contextSelector } from "@shrey-junior/sj-core/core/context-selector.js";
import { generateInstructions, persistInstructions } from "@shrey-junior/sj-core/core/instruction-generator.js";
import { matchSkillsForTask } from "@shrey-junior/sj-core/core/skills-registry.js";
import { recordExecutionState } from "@shrey-junior/sj-core/core/execution-state.js";
import { recordRuntimeProgress } from "@shrey-junior/sj-core/core/runtime-lifecycle.js";
import { buildRuntimeDecision, buildRuntimeDecisionAction, buildRuntimeDecisionRecovery } from "@shrey-junior/sj-core/core/runtime-decision.js";
import { syncExecutionPlan } from "@shrey-junior/sj-core/core/execution-plan.js";
import { estimateTokens, persistTokenUsage } from "@shrey-junior/sj-core/core/token-estimator.js";
import { executeWorkflowStep } from "@shrey-junior/sj-core/core/workflow-engine.js";
import { pathExists, readJson, relativeFrom } from "@shrey-junior/sj-core/utils/fs.js";
import { syncPackSelectionSideEffects } from "./helpers/pack-selection.js";
export async function runPrepare(options) {
    const { targetRoot, task, logger } = options;
    logger.info(`Preparing context for: "${task}"`);
    const selectionStatePath = path.join(targetRoot, ".agent", "state", "context-selection.json");
    let selection = null;
    let skillRegistry = {
        activeSkills: [],
        suggestedSkills: []
    };
    let instructions = null;
    let instructionPath = null;
    let tokenUsagePath = null;
    let tokenEstimate = null;
    const workflowExecution = await executeWorkflowStep(targetRoot, {
        task,
        stepId: "prepare-context",
        input: task,
        expectedOutput: "Prepared scope and generated instructions are ready.",
        run: async () => {
            selection = await contextSelector(task, targetRoot, {
                ...(options.expand !== undefined ? { expand: options.expand } : {})
            });
            skillRegistry = await matchSkillsForTask(targetRoot, task).catch(() => ({
                activeSkills: [],
                suggestedSkills: []
            }));
            instructions = generateInstructions(task, selection, skillRegistry);
            instructionPath = await persistInstructions(targetRoot, instructions);
            tokenEstimate = await estimateTokens(targetRoot, selection.include, task);
            tokenUsagePath = await persistTokenUsage(targetRoot, task, tokenEstimate);
            return {
                selection,
                skillRegistry,
                instructions,
                instructionPath,
                tokenEstimate,
                tokenUsagePath
            };
        },
        validate: async (result) => {
            const artifactsReady = await pathExists(selectionStatePath) &&
                await pathExists(result.instructionPath) &&
                await pathExists(result.tokenUsagePath);
            const hasScope = result.selection.include.length > 0;
            const ok = artifactsReady && hasScope;
            return {
                ok,
                validation: ok
                    ? "Prepared scope exists, instructions were written, and token state was recorded."
                    : "Prepare did not produce a usable prepared scope and its required artifacts.",
                ...(ok
                    ? {}
                    : {
                        failureReason: !artifactsReady
                            ? "Required prepare artifacts were not written to .agent/state."
                            : "No files were selected into the prepared scope.",
                        suggestedFix: `Refine the task wording or inspect ignored files, then rerun kiwi-control prepare "${task}".`
                    })
            };
        },
        summarize: (result) => ({
            summary: `Prepared ${result.selection.include.length} selected files for "${task}".${options.expand ? " Expanded scope mode was active." : ""}`,
            files: result.selection.include,
            skillsApplied: result.skillRegistry.activeSkills.map((skill) => skill.skillId),
            estimatedTokens: result.tokenEstimate.selectedTokens
        })
    });
    if (!workflowExecution.ok || !selection || !instructions || !instructionPath || !tokenEstimate) {
        const nextCommand = workflowExecution.retryCommand ?? `kiwi-control prepare "${task}"`;
        await recordExecutionState(targetRoot, {
            type: "prepare-failed",
            lifecycle: "blocked",
            task,
            sourceCommand: `kiwi-control prepare "${task}"${options.expand ? " --expand" : ""}`,
            reason: workflowExecution.failureReason ?? `Prepare failed for "${task}".`,
            nextCommand,
            blockedBy: [workflowExecution.failureReason ?? workflowExecution.validation],
            reuseOperation: false,
            decision: buildRuntimeDecision({
                currentStepId: "prepare",
                currentStepStatus: "failed",
                nextCommand,
                readinessLabel: "Workflow blocked",
                readinessTone: "blocked",
                readinessDetail: workflowExecution.failureReason ?? `Prepare failed for "${task}".`,
                nextAction: buildRuntimeDecisionAction("Refresh the prepared scope", nextCommand, workflowExecution.failureReason ?? workflowExecution.validation, "critical"),
                recovery: buildRuntimeDecisionRecovery("blocked", workflowExecution.failureReason ?? workflowExecution.validation, nextCommand, nextCommand),
                decisionSource: "prepare-command"
            })
        }).catch(() => null);
        await recordRuntimeProgress(targetRoot, {
            type: "prepare_completed",
            stage: "blocked",
            status: "error",
            summary: workflowExecution.failureReason ?? `Prepare failed for "${task}".`,
            task,
            validation: workflowExecution.validation,
            ...(workflowExecution.failureReason ? { failureReason: workflowExecution.failureReason } : {}),
            nextSuggestedCommand: workflowExecution.retryCommand,
            nextRecommendedAction: workflowExecution.suggestedFix ?? workflowExecution.validation,
            validationStatus: "error"
        }).catch(() => null);
        await syncExecutionPlan(targetRoot, {
            task,
            forceState: "failed"
        }).catch(() => null);
        logger.error(workflowExecution.failureReason ?? `Prepare failed for "${task}".`);
        return 1;
    }
    const finalSelection = selection;
    const finalInstructions = instructions;
    const finalInstructionPath = instructionPath;
    const finalTokenEstimate = tokenEstimate;
    logger.info(`Context selected: ${finalSelection.include.length} files [confidence=${finalSelection.confidence}]${options.expand ? " [expanded]" : ""}`);
    logger.info(`Instructions written to: ${finalInstructionPath}`);
    await recordRuntimeProgress(targetRoot, {
        type: "prepare_completed",
        stage: "prepared",
        status: finalSelection.confidence === "low" ? "warn" : "ok",
        summary: `Prepared ${finalSelection.include.length} selected files for "${task}".`,
        task,
        files: finalSelection.include,
        skillsApplied: skillRegistry.activeSkills.map((skill) => skill.skillId),
        estimatedTokens: finalTokenEstimate.selectedTokens,
        validation: workflowExecution.validation,
        nextSuggestedCommand: `kiwi-control run "${task}"`,
        nextRecommendedAction: "Open the generated instructions and work inside the prepared scope.",
        validationStatus: finalSelection.confidence === "low" ? "warn" : "ok"
    }).catch(() => null);
    await syncExecutionPlan(targetRoot, {
        task,
        forceState: "ready"
    }).catch(() => null);
    await recordExecutionState(targetRoot, {
        type: "prepare-completed",
        lifecycle: "packet-created",
        task,
        sourceCommand: `kiwi-control prepare "${task}"${options.expand ? " --expand" : ""}`,
        reason: `Prepared ${finalSelection.include.length} selected files for "${task}".`,
        nextCommand: `kiwi-control run "${task}"`,
        blockedBy: [],
        artifacts: {
            instructions: [relativeFrom(targetRoot, finalInstructionPath)],
            contextSelection: [".agent/state/context-selection.json"],
            tokenUsage: [".agent/state/token-usage.json"]
        },
        reuseOperation: false,
        decision: buildRuntimeDecision({
            currentStepId: "generate_packets",
            currentStepStatus: "pending",
            nextCommand: `kiwi-control run "${task}"`,
            readinessLabel: "Packet created",
            readinessTone: "ready",
            readinessDetail: `Prepared ${finalSelection.include.length} selected files for "${task}".`,
            nextAction: buildRuntimeDecisionAction("Generate run packets", `kiwi-control run "${task}"`, `Prepared ${finalSelection.include.length} selected files for "${task}".`, "high"),
            decisionSource: "prepare-command"
        })
    }).catch(() => null);
    await syncPackSelectionSideEffects({
        repoRoot: options.repoRoot,
        targetRoot
    }).catch(() => null);
    const contextTrace = await readJson(path.join(targetRoot, ".agent", "state", "context-trace.json"));
    const indexing = await readJson(path.join(targetRoot, ".agent", "state", "indexing.json"));
    const tokenBreakdown = await readJson(path.join(targetRoot, ".agent", "state", "token-breakdown.json"));
    const result = {
        task,
        confidence: finalSelection.confidence,
        filesSelected: finalSelection.include.length,
        filesExcluded: finalSelection.exclude.length,
        selectedTokens: finalTokenEstimate.selectedTokens,
        fullRepoTokens: finalTokenEstimate.fullRepoTokens,
        savingsPercent: finalTokenEstimate.savingsPercent,
        estimationMethod: finalTokenEstimate.estimationMethod,
        estimateNote: finalTokenEstimate.estimateNote,
        topDirectories: finalTokenEstimate.directoryBreakdown.slice(0, 5).map((d) => ({
            directory: d.directory,
            tokens: d.tokens,
            fileCount: d.fileCount
        })),
        instructionPath: finalInstructionPath,
        reason: finalSelection.reason,
        constraintCount: finalInstructions.constraints.length,
        validationStepCount: finalInstructions.validationSteps.length,
        stopConditionCount: finalInstructions.stopConditions.length,
        contextTrace,
        indexing,
        tokenBreakdown,
        measuredUsage: finalTokenEstimate.measuredUsage,
        skills: {
            activeSkills: skillRegistry.activeSkills,
            suggestedSkills: skillRegistry.suggestedSkills
        }
    };
    if (options.json) {
        console.log(JSON.stringify(result, null, 2));
    }
    else {
        printPrepareReport(result, logger);
    }
    return 0;
}
function printPrepareReport(result, logger) {
    logger.info("");
    logger.info("╔══════════════════════════════════════════════╗");
    logger.info("║          Kiwi Control — prepare              ║");
    logger.info("╚═══════════════════════════════════════════��══╝");
    logger.info("");
    logger.info(`  Task:            ${result.task}`);
    logger.info(`  Confidence:      ${result.confidence.toUpperCase()}`);
    logger.info(`  Files selected:  ${result.filesSelected}`);
    logger.info(`  Files excluded:  ${result.filesExcluded} patterns`);
    logger.info("");
    logger.info("  Measured Scope:");
    logger.info(`    Selected files:${String(result.filesSelected).padStart(10)}  (measured)`);
    logger.info(`    Exclusions:    ${String(result.filesExcluded).padStart(10)}  patterns`);
    logger.info("");
    logger.info("  Token Usage:");
    if (result.measuredUsage.available) {
        logger.info(`    Measured:      ${formatTokens(result.measuredUsage.totalTokens)}  (${result.measuredUsage.totalRuns} runs, ${result.measuredUsage.source})`);
    }
    else {
        logger.info("    Measured:      unavailable");
    }
    logger.info(`    Selected:      ~${formatTokens(result.selectedTokens)}`);
    logger.info(`    Full repo:     ~${formatTokens(result.fullRepoTokens)}`);
    logger.info(`    Savings:       ~${result.savingsPercent}%`);
    logger.info(`    Method:        ${result.estimationMethod}`);
    logger.info(`    Note:          ${result.estimateNote}`);
    logger.info("");
    logger.info("  Skills:");
    logger.info(`    Active:        ${result.skills.activeSkills.length > 0 ? result.skills.activeSkills.map((skill) => skill.name).join(", ") : "none"}`);
    logger.info(`    Suggested:     ${result.skills.suggestedSkills.length > 0 ? result.skills.suggestedSkills.map((skill) => skill.name).join(", ") : "none"}`);
    logger.info("");
    logger.info("  How It Works:");
    logger.info(`    Total files:   ${String(result.contextTrace.fileAnalysis.totalFiles).padStart(10)}`);
    logger.info(`    Scanned files: ${String(result.contextTrace.fileAnalysis.scannedFiles).padStart(10)}`);
    logger.info(`    Skipped files: ${String(result.contextTrace.fileAnalysis.skippedFiles).padStart(10)}`);
    logger.info(`    Selected files:${String(result.contextTrace.fileAnalysis.selectedFiles).padStart(10)}`);
    logger.info(`    Excluded files:${String(result.contextTrace.fileAnalysis.excludedFiles).padStart(10)}`);
    logger.info(`    Honesty:       ${buildHonestyLine(result)}`);
    logger.info("");
    logger.info("  File Analysis:");
    for (const entry of result.contextTrace.fileAnalysis.selected.slice(0, 4)) {
        logger.info(`    selected  ${entry.file} — ${entry.reasons.join(", ")}`);
    }
    for (const entry of result.contextTrace.fileAnalysis.excluded.slice(0, 4)) {
        logger.info(`    excluded  ${entry.file} — ${(entry.note ?? entry.reasons.join(", "))}`);
    }
    for (const entry of result.contextTrace.fileAnalysis.skipped.slice(0, 3)) {
        logger.info(`    skipped   ${entry.path} — ${entry.reason}`);
    }
    logger.info("");
    logger.info("  Context Trace:");
    for (const step of result.contextTrace.expansionSteps) {
        logger.info(`    ${step.step}: ${step.summary}`);
    }
    logger.info("");
    logger.info("  Indexing:");
    logger.info(`    Visited dirs:  ${String(result.indexing.visitedDirectories).padStart(10)}`);
    logger.info(`    Depth reached: ${String(result.indexing.maxDepthExplored).padStart(10)}`);
    logger.info(`    Files found:   ${String(result.indexing.discoveredFiles).padStart(10)}`);
    logger.info(`    Files analyzed:${String(result.indexing.analyzedFiles).padStart(10)}`);
    logger.info(`    Index reused:  ${String(result.indexing.indexReusedFiles ?? 0).padStart(10)}`);
    logger.info(`    Index updated: ${String(result.indexing.indexUpdatedFiles ?? 0).padStart(10)}`);
    logger.info(`    Impact files:  ${String(result.indexing.impactFiles ?? 0).padStart(10)}`);
    logger.info(`    Ignore rules:  ${result.indexing.ignoreRulesApplied.join(", ") || "none"}`);
    logger.info("");
    if (result.topDirectories.length > 0) {
        logger.info("  Token Hotspots:");
        for (const dir of result.topDirectories.slice(0, 5)) {
            logger.info(`    ${dir.directory.padEnd(40)} ${formatTokens(dir.tokens).padStart(12)}  (${dir.fileCount} files)`);
        }
        logger.info("");
    }
    logger.info("  Token Breakdown:");
    for (const category of result.tokenBreakdown.categories) {
        logger.info(`    ${category.category.padEnd(16)} ~${formatTokens(category.estimated_tokens_avoided).padStart(12)}  [${category.basis}] ${category.note}`);
    }
    logger.info("");
    logger.info("  Instructions:");
    logger.info(`    Path:          ${result.instructionPath}`);
    logger.info(`    Constraints:   ${result.constraintCount}`);
    logger.info(`    Validations:   ${result.validationStepCount}`);
    logger.info(`    Stop guards:   ${result.stopConditionCount}`);
    logger.info("");
    logger.info(`  Reason:          ${result.reason}`);
    logger.info("");
}
function formatTokens(count) {
    if (count >= 1_000_000) {
        return `${(count / 1_000_000).toFixed(1)}M tokens`;
    }
    if (count >= 1_000) {
        return `${(count / 1_000).toFixed(1)}K tokens`;
    }
    return `${count} tokens`;
}
function buildHonestyLine(result) {
    const labels = ["heuristic"];
    if (result.contextTrace.honesty.lowConfidence) {
        labels.push("low confidence");
    }
    if (result.contextTrace.honesty.partialScan || result.indexing.partialScan || result.tokenBreakdown.partial_scan) {
        labels.push("partial scan");
    }
    return labels.join(", ");
}
