/**
 * Extract collected data archives.
 *
 * Replaces the shell-based extract.sh — recursively walks agent data
 * directories, unpacks *.tar.gz archives and decompresses *.jsonl.gz files.
 *
 * Usage:
 *   visionclaw extract-data <data-root> [agent-name ...] [--all] [--force]
 */
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
import { createGunzip } from "node:zlib";
import { pipeline } from "node:stream/promises";
// ---------------------------------------------------------------------------
// 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();
}
/** Decompress a single .jsonl.gz file in-place (removes the .gz). */
async function gunzipFile(gzPath) {
    const outPath = gzPath.replace(/\.gz$/, "");
    const input = fs.createReadStream(gzPath);
    const output = fs.createWriteStream(outPath);
    await pipeline(input, createGunzip(), output);
    fs.unlinkSync(gzPath);
}
/** Decompress all .jsonl.gz files in a directory (non-recursive). */
async function gunzipDir(dir, label) {
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
        if (entry.isFile() && entry.name.endsWith(".jsonl.gz")) {
            console.log(`  GUNZIP ${entry.name}${label ? ` in ${label}` : ""}`);
            await gunzipFile(path.join(dir, entry.name));
        }
        // Also handle sub-directories (e.g. transcripts/<session>/subagents/)
        if (entry.isDirectory()) {
            const subDir = path.join(dir, entry.name);
            await gunzipDir(subDir, `${label ? label + "/" : ""}${entry.name}`);
        }
    }
}
/** Check if a directory has any files besides .tar.gz archives. */
function hasExtractedFiles(dir) {
    for (const entry of fs.readdirSync(dir)) {
        if (!entry.endsWith(".tar.gz") && entry !== ".DS_Store")
            return true;
    }
    return false;
}
async function extractAgent(baseDir, agent, force) {
    const agentDir = path.join(baseDir, agent);
    const stats = { extracted: 0, skipped: 0, gunzipped: 0 };
    for (const dateEntry of fs.readdirSync(agentDir, { withFileTypes: true })) {
        if (!dateEntry.isDirectory() || !DATE_PATTERN.test(dateEntry.name))
            continue;
        const dateDir = path.join(agentDir, dateEntry.name);
        for (const folder of ["logs", "transcripts", "workspace"]) {
            const folderPath = path.join(dateDir, folder);
            if (!fs.existsSync(folderPath) || !fs.statSync(folderPath).isDirectory())
                continue;
            // Find .tar.gz archives
            for (const file of fs.readdirSync(folderPath)) {
                if (!file.endsWith(".tar.gz"))
                    continue;
                const archivePath = path.join(folderPath, file);
                if (hasExtractedFiles(folderPath) && !force) {
                    console.log(`  SKIP ${folder}/ (already extracted)`);
                    stats.skipped++;
                }
                else {
                    const label = hasExtractedFiles(folderPath) ? "RE-EXTRACT" : "EXTRACT";
                    console.log(`  ${label} ${file} -> ${folder}/`);
                    execSync(`tar xzf "${archivePath}" -C "${folderPath}/"`, {
                        stdio: "pipe",
                    });
                    stats.extracted++;
                }
            }
            // Decompress .jsonl.gz files
            const before = stats.gunzipped;
            await gunzipDir(folderPath, "");
            const after = countGzFiles(folderPath);
            stats.gunzipped += (after === 0 ? 0 : after) + (stats.gunzipped - before);
        }
    }
    return stats;
}
function countGzFiles(_dir) {
    // Already decompressed by gunzipDir, return 0
    return 0;
}
function parseExtractArgs(args) {
    let dataRoot = "";
    const agents = [];
    let all = false;
    let force = false;
    for (const arg of args) {
        if (arg === "--all") {
            all = true;
        }
        else if (arg === "--force") {
            force = true;
        }
        else if (!arg.startsWith("-") && !dataRoot) {
            dataRoot = arg;
        }
        else if (!arg.startsWith("-")) {
            agents.push(arg);
        }
    }
    if (!dataRoot) {
        console.error("Usage: visionclaw extract-data <data-root> [agent-name ...] [--all] [--force]\n" +
            "\n" +
            "  <data-root>   Root directory containing agent data folders\n" +
            "  --all         Extract all agent directories\n" +
            "  --force       Re-extract even if already extracted");
        process.exit(1);
    }
    return { dataRoot, agents, all, force };
}
export async function runExtractData(args) {
    const opts = parseExtractArgs(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.length === 0) {
        agentsToProcess = findAgents(opts.dataRoot);
        if (agentsToProcess.length === 0) {
            console.log("No agent data directories found.");
            return;
        }
        if (!opts.all && agentsToProcess.length > 0 && opts.agents.length === 0) {
            // No agents specified and no --all flag — show available agents
            console.log("Available agents:");
            for (const a of agentsToProcess)
                console.log(`  ${a}`);
            console.log("\nUsage: visionclaw extract-data <data-root> [agent-name ...] [--all] [--force]");
            return;
        }
    }
    let totalExtracted = 0;
    let totalSkipped = 0;
    for (const agent of agentsToProcess) {
        const agentDir = path.join(opts.dataRoot, agent);
        if (!fs.existsSync(agentDir)) {
            console.log(`WARNING: Agent '${agent}' not found, skipping`);
            continue;
        }
        console.log(`\n=== ${agent} ===`);
        const stats = await extractAgent(opts.dataRoot, agent, opts.force);
        console.log(`  (${stats.extracted} extracted, ${stats.skipped} skipped)`);
        totalExtracted += stats.extracted;
        totalSkipped += stats.skipped;
    }
    console.log(`\nDone: ${totalExtracted} extracted, ${totalSkipped} skipped across ${agentsToProcess.length} agents`);
}
//# sourceMappingURL=extract.js.map