/**
 * cache command — manage the local pipeline cache.
 *
 * Subcommands:
 *   cache clear   Delete all local cache manifests (.ailf/results/cache/).
 *   cache status  Show current cache entries and their ages.
 *
 * Operates on the *caller's* `.ailf/results/cache/` tree (not the eval
 * package's installed location); use `--cache-dir` or `AILF_CACHE_DIR` to
 * override (W0098).
 *
 * Note: This only affects the local file-system cache used to skip unchanged
 * pipeline steps. It does NOT touch the remote Content Lake eval cache.
 * Use --no-remote-cache on pipeline commands to bypass the remote cache.
 */
import { Command } from "commander";
import { existsSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
import { join, resolve } from "path";
import { getCallerCwd } from "./shared/resolve-output-dir.js";
/**
 * Resolve the local pipeline cache directory.
 * Precedence: explicit flag > `AILF_CACHE_DIR` env var > caller cwd default.
 */
function resolveCacheDir(flag) {
    if (flag)
        return resolve(getCallerCwd(), flag);
    if (process.env.AILF_CACHE_DIR)
        return resolve(getCallerCwd(), process.env.AILF_CACHE_DIR);
    return join(getCallerCwd(), ".ailf", "results", "cache");
}
export function createCacheCommand() {
    const cmd = new Command("cache").description("Manage the local pipeline cache (does not affect the remote Content Lake cache)");
    // -----------------------------------------------------------------------
    // cache clear
    // -----------------------------------------------------------------------
    cmd
        .command("clear")
        .description("Delete all local cache manifests so every pipeline step re-executes")
        .option("--cache-dir <path>", "Directory holding cache manifests (default: <cwd>/.ailf/results/cache)")
        .action((opts) => {
        const cacheDir = resolveCacheDir(opts.cacheDir);
        if (!existsSync(cacheDir)) {
            console.log("  ℹ️  No local cache directory found — nothing to clear.");
            return;
        }
        const files = readdirSync(cacheDir).filter((f) => f.endsWith(".json"));
        if (files.length === 0) {
            console.log("  ℹ️  Local cache directory is empty — nothing to clear.");
            return;
        }
        rmSync(cacheDir, { recursive: true, force: true });
        console.log(`  🗑️  Cleared ${files.length} local cache manifest(s).`);
        console.log("  ℹ️  Next pipeline run will re-execute all steps from scratch.");
        console.log("\n  Note: The remote Content Lake cache is unaffected.");
        console.log("  Use --no-remote-cache on pipeline commands to bypass it.\n");
    });
    // -----------------------------------------------------------------------
    // cache status
    // -----------------------------------------------------------------------
    cmd
        .command("status")
        .description("Show current local cache entries and their ages")
        .option("--cache-dir <path>", "Directory holding cache manifests (default: <cwd>/.ailf/results/cache)")
        .action((opts) => {
        const cacheDir = resolveCacheDir(opts.cacheDir);
        if (!existsSync(cacheDir)) {
            console.log("  ℹ️  No local cache directory found.");
            return;
        }
        const files = readdirSync(cacheDir).filter((f) => f.endsWith(".json"));
        if (files.length === 0) {
            console.log("  ℹ️  Local cache directory is empty.");
            return;
        }
        console.log("=== Local Pipeline Cache ===\n");
        console.log("  " +
            "Step".padEnd(22) +
            "Age".padEnd(18) +
            "Duration".padEnd(12) +
            "Outputs");
        console.log("  " + "-".repeat(65));
        for (const file of files.sort()) {
            const filePath = join(cacheDir, file);
            try {
                const raw = readFileSync(filePath, "utf-8");
                const manifest = JSON.parse(raw);
                if (!manifest.entry) {
                    console.log(`  ${manifest.step.padEnd(22)}(empty)`);
                    continue;
                }
                const age = formatAge(manifest.entry.timestamp);
                const duration = formatDuration(manifest.entry.durationMs);
                const outputs = manifest.entry.outputPaths.length;
                console.log("  " +
                    manifest.step.padEnd(22) +
                    age.padEnd(18) +
                    duration.padEnd(12) +
                    `${outputs} file(s)`);
            }
            catch {
                const step = file.replace(".json", "");
                console.log(`  ${step.padEnd(22)}(corrupt manifest)`);
            }
        }
        const totalSize = files.reduce((sum, f) => {
            try {
                return sum + statSync(join(cacheDir, f)).size;
            }
            catch {
                return sum;
            }
        }, 0);
        console.log();
        console.log(`  ${files.length} cached step(s), ${formatBytes(totalSize)} on disk`);
        console.log('  Run "ailf cache clear" to remove all local cache entries.\n');
    });
    return cmd;
}
// ---------------------------------------------------------------------------
// Formatting helpers
// ---------------------------------------------------------------------------
function formatAge(timestamp) {
    const then = new Date(timestamp).getTime();
    const now = Date.now();
    const diffMs = now - then;
    if (Number.isNaN(then))
        return "unknown";
    const seconds = Math.floor(diffMs / 1_000);
    const minutes = Math.floor(seconds / 60);
    const hours = Math.floor(minutes / 60);
    const days = Math.floor(hours / 24);
    if (days > 0)
        return `${days}d ${hours % 24}h ago`;
    if (hours > 0)
        return `${hours}h ${minutes % 60}m ago`;
    if (minutes > 0)
        return `${minutes}m ago`;
    return "just now";
}
function formatDuration(ms) {
    if (ms < 1_000)
        return `${ms}ms`;
    const seconds = (ms / 1_000).toFixed(1);
    return `${seconds}s`;
}
function formatBytes(bytes) {
    if (bytes < 1_024)
        return `${bytes} B`;
    const kb = (bytes / 1_024).toFixed(1);
    return `${kb} KB`;
}
