/**
 * Batch-process multiple agent data directories through the pipeline.
 *
 * Replaces the shell-based process.sh — iterates agent directories,
 * invokes `process-data` + `convert-openai-chat` on each, optionally
 * generating system prompts from workspace config.
 *
 * Usage:
 *   visionclaw batch-process <data-root> [agent-name ...] [--all] [--force]
 *     [--since=YYYY-MM-DD-HH-mm+HH:MM]
 */
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
const DATE_PATTERN = /^\d{8}T\d{6}$/;
/** List agent directories that contain at least one timestamped sub-dir. */
function findAgents(baseDir) {
    const agents = [];
    for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {
        if (!entry.isDirectory() || entry.name.startsWith("."))
            continue;
        const agentDir = path.join(baseDir, entry.name);
        for (const sub of fs.readdirSync(agentDir, { withFileTypes: true })) {
            if (sub.isDirectory() && DATE_PATTERN.test(sub.name)) {
                agents.push(entry.name);
                break;
            }
        }
    }
    return agents.sort();
}
/** Check if a directory has extracted log files (not just .tar.gz). */
function hasExtractedLogs(logsDir) {
    if (!fs.existsSync(logsDir))
        return false;
    for (const entry of fs.readdirSync(logsDir)) {
        if (entry.endsWith(".jsonl") || entry.endsWith(".jsonl.gz"))
            return true;
        // Check one level deeper (logs/logs/)
        const sub = path.join(logsDir, entry);
        if (fs.statSync(sub).isDirectory()) {
            for (const f of fs.readdirSync(sub)) {
                if (f.endsWith(".jsonl") || f.endsWith(".jsonl.gz"))
                    return true;
            }
        }
    }
    return false;
}
/** Resolve the path to the VisionClaw CLI entry point (tsx or dist). */
function resolveCliEntry() {
    // When running via tsx in dev, argv[1] is the .ts file
    // Otherwise look for the compiled dist entry
    const devEntry = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../index.ts");
    const distEntry = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../index.js");
    if (fs.existsSync(devEntry))
        return devEntry;
    return distEntry;
}
/** Resolve the VisionClaw project root (parent of src/ or dist/). */
function resolveProjectRoot() {
    return path.resolve(path.dirname(new URL(import.meta.url).pathname), "../..");
}
function processDir(dataDir, label, options) {
    const logsDir = path.join(dataDir, "logs");
    if (!hasExtractedLogs(logsDir)) {
        console.log(`SKIP: ${label} (no extracted logs — run extract-data first)`);
        return { label, status: "skipped", reason: "no logs" };
    }
    const outputDir = path.join(dataDir, "output");
    if (fs.existsSync(outputDir) && !options.force) {
        console.log(`SKIP: ${label} (output/ exists, use --force to re-process)`);
        return { label, status: "skipped", reason: "output exists" };
    }
    if (fs.existsSync(outputDir) && options.force) {
        fs.rmSync(outputDir, { recursive: true, force: true });
    }
    console.log("");
    console.log("==========================================");
    console.log(`Processing: ${label}`);
    console.log("==========================================");
    try {
        // Stage 1: process-data (sanitize + restructure + samples)
        const sinceArg = options.since ? ` --since ${options.since}` : "";
        execSync(`npx tsx "${options.cliEntry}" process-data "${dataDir}" --samples${sinceArg}`, { cwd: options.projectRoot, stdio: "inherit" });
        // Stage 2: Generate system-prompt.md if needed
        const promptPath = path.join(outputDir, "workspace", "system-prompt.md");
        if (!fs.existsSync(promptPath)) {
            const configPath = path.join(dataDir, "workspace", "config.json");
            const ownerPath = path.join(dataDir, "workspace", "owner.json");
            if (fs.existsSync(configPath) && fs.existsSync(ownerPath)) {
                console.log("\nGenerating system-prompt.md from workspace config...");
                const script = `
          import { buildSystemPrompt } from "./src/agent/system-prompt.js";
          import fs from "node:fs";
          const config = JSON.parse(fs.readFileSync(${JSON.stringify(configPath)}, "utf-8"));
          const owner = JSON.parse(fs.readFileSync(${JSON.stringify(ownerPath)}, "utf-8"));
          const prompt = buildSystemPrompt(config, owner);
          fs.mkdirSync(${JSON.stringify(path.dirname(promptPath))}, { recursive: true });
          fs.writeFileSync(${JSON.stringify(promptPath)}, prompt);
          console.log("  system-prompt.md:", prompt.length, "chars");
        `;
                const tmpScript = path.join(options.projectRoot, `_gen-prompt-${Date.now()}.ts`);
                fs.writeFileSync(tmpScript, script);
                try {
                    execSync(`npx tsx "${tmpScript}"`, {
                        cwd: options.projectRoot,
                        stdio: "inherit",
                    });
                }
                finally {
                    fs.unlinkSync(tmpScript);
                }
            }
            else {
                console.log("  WARN: Cannot generate system-prompt.md (missing config.json or owner.json)");
            }
        }
        // Stage 3: convert-openai-chat
        if (fs.existsSync(promptPath)) {
            execSync(`npx tsx "${options.cliEntry}" convert-openai-chat "${outputDir}"`, { cwd: options.projectRoot, stdio: "inherit" });
        }
        else {
            console.log("  SKIP convert-openai-chat (no system-prompt.md)");
        }
        return { label, status: "succeeded" };
    }
    catch (err) {
        console.error(`FAILED: ${label} — ${err instanceof Error ? err.message : String(err)}`);
        return { label, status: "failed" };
    }
}
function parseBatchProcessArgs(args) {
    let dataRoot = "";
    const agents = [];
    let all = false;
    let force = false;
    let since = "";
    for (const arg of args) {
        if (arg === "--all") {
            all = true;
        }
        else if (arg === "--force") {
            force = true;
        }
        else if (arg.startsWith("--since=")) {
            since = arg.slice("--since=".length);
        }
        else if (!arg.startsWith("-") && !dataRoot) {
            dataRoot = arg;
        }
        else if (!arg.startsWith("-")) {
            agents.push(arg);
        }
    }
    if (!dataRoot) {
        console.error("Usage: visionclaw batch-process <data-root> [agent-name ...] [--all] [--force]\n" +
            "       [--since=YYYY-MM-DD-HH-mm+HH:MM]\n" +
            "\n" +
            "  <data-root>   Root directory containing agent data folders\n" +
            "  --all         Process all agent directories\n" +
            "  --force       Re-process even if output/ already exists\n" +
            "  --since       Only include log entries from this time onward");
        process.exit(1);
    }
    return { dataRoot, agents, all, force, since };
}
export function runBatchProcess(args) {
    const opts = parseBatchProcessArgs(args);
    if (!fs.existsSync(opts.dataRoot) || !fs.statSync(opts.dataRoot).isDirectory()) {
        console.error(`Not a directory: ${opts.dataRoot}`);
        process.exit(1);
    }
    let agentsToProcess = opts.agents;
    if (opts.all) {
        agentsToProcess = findAgents(opts.dataRoot);
    }
    if (agentsToProcess.length === 0) {
        const available = findAgents(opts.dataRoot);
        if (available.length > 0) {
            console.log("Available agents:");
            for (const a of available)
                console.log(`  ${a}`);
        }
        console.log("\nUsage: visionclaw batch-process <data-root> [agent-name ...] [--all] [--force]");
        return;
    }
    const projectRoot = resolveProjectRoot();
    const cliEntry = resolveCliEntry();
    const results = [];
    for (const agent of agentsToProcess) {
        const agentDir = path.join(opts.dataRoot, agent);
        if (!fs.existsSync(agentDir)) {
            console.log(`SKIP: ${agent} (directory not found)`);
            results.push({ label: agent, status: "skipped", reason: "not found" });
            continue;
        }
        // Check for timestamped sub-directories
        const dateDirs = [];
        for (const entry of fs.readdirSync(agentDir, { withFileTypes: true })) {
            if (entry.isDirectory() && DATE_PATTERN.test(entry.name)) {
                dateDirs.push(entry.name);
            }
        }
        if (dateDirs.length > 0) {
            for (const dateStamp of dateDirs.sort()) {
                const r = processDir(path.join(agentDir, dateStamp), `${agent}/${dateStamp}`, { force: opts.force, since: opts.since, projectRoot, cliEntry });
                results.push(r);
            }
        }
        else {
            // Flat structure — treat agent dir itself as the data dir
            const r = processDir(agentDir, agent, {
                force: opts.force,
                since: opts.since,
                projectRoot,
                cliEntry,
            });
            results.push(r);
        }
    }
    // Summary
    const succeeded = results.filter((r) => r.status === "succeeded").length;
    const failed = results.filter((r) => r.status === "failed").length;
    const skipped = results.filter((r) => r.status === "skipped").length;
    console.log("");
    console.log("==========================================");
    console.log(`Summary: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped`);
    console.log("==========================================");
    if (failed > 0)
        process.exit(1);
}
//# sourceMappingURL=batch-process.js.map