#!/usr/bin/env node

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

function parseArgs(argv) {
  const args = {
    clean: false,
    label: "",
    dryRun: false
  };
  for (let i = 0; i < argv.length; i += 1) {
    const token = argv[i];
    if (token === "--clean") {
      args.clean = true;
      continue;
    }
    if (token === "--dry-run") {
      args.dryRun = true;
      continue;
    }
    if (token === "--label") {
      args.label = String(argv[i + 1] ?? "").trim();
      i += 1;
    }
  }
  return args;
}

function sanitizeLabel(value) {
  return value
    .trim()
    .replace(/\s+/g, "_")
    .replace(/[^a-zA-Z0-9_-]/g, "")
    .slice(0, 48);
}

function formatTimestamp(date) {
  const pad = (v) => String(v).padStart(2, "0");
  return [
    date.getFullYear(),
    pad(date.getMonth() + 1),
    pad(date.getDate())
  ].join("-")
    + "_"
    + [pad(date.getHours()), pad(date.getMinutes()), pad(date.getSeconds())].join("-");
}

function ensureDir(dirPath) {
  fs.mkdirSync(dirPath, { recursive: true });
}

function listFilesRecursively(rootDir) {
  if (!fs.existsSync(rootDir)) {
    return [];
  }
  const output = [];
  const stack = [rootDir];
  while (stack.length > 0) {
    const current = stack.pop();
    if (!current) continue;
    for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
      const full = path.join(current, entry.name);
      if (entry.isDirectory()) {
        stack.push(full);
      } else if (entry.isFile()) {
        output.push(full);
      }
    }
  }
  return output;
}

function copyFilePreserveRelative(fromPath, baseDir, targetBaseDir) {
  const rel = path.relative(baseDir, fromPath);
  const target = path.join(targetBaseDir, rel);
  ensureDir(path.dirname(target));
  fs.copyFileSync(fromPath, target);
  return target;
}

function copyDirectoryPreserveRelative(fromDir, baseDir, targetBaseDir) {
  const files = listFilesRecursively(fromDir);
  const copied = [];
  for (const filePath of files) {
    copied.push(copyFilePreserveRelative(filePath, baseDir, targetBaseDir));
  }
  return copied;
}

function removePath(targetPath) {
  if (!fs.existsSync(targetPath)) {
    return;
  }
  fs.rmSync(targetPath, { recursive: true, force: true });
}

function pickReportEntries(reportsDir) {
  if (!fs.existsSync(reportsDir)) {
    return [];
  }

  const staticKeep = new Set([
    "v2_0_1_clarification_eval_plan.md",
    "v2_0_2_eval_plan.md",
    "v2_pilot_eval_plan.md"
  ]);

  const entries = [];
  for (const entry of fs.readdirSync(reportsDir, { withFileTypes: true })) {
    const full = path.join(reportsDir, entry.name);
    if (entry.isDirectory()) {
      if (entry.name.startsWith("minimal_package_")) {
        entries.push(full);
      }
      continue;
    }
    if (!entry.isFile()) {
      continue;
    }
    if (staticKeep.has(entry.name)) {
      continue;
    }

    const isGenerated =
      entry.name.startsWith("assistant-stage1-")
      || entry.name.startsWith("assistant-compare-")
      || entry.name.startsWith("normalizer_eval_")
      || entry.name.startsWith("normalizer_v")
      || entry.name.startsWith("minimal_package_");

    if (isGenerated) {
      entries.push(full);
    }
  }
  return entries;
}

function pickLogFiles(logDir, filePattern) {
  if (!fs.existsSync(logDir)) {
    return [];
  }
  return fs
    .readdirSync(logDir, { withFileTypes: true })
    .filter((entry) => entry.isFile() && filePattern.test(entry.name))
    .map((entry) => path.join(logDir, entry.name));
}

function writeJson(filePath, payload) {
  ensureDir(path.dirname(filePath));
  fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf-8");
}

function main() {
  const args = parseArgs(process.argv.slice(2));
  const scriptFile = fileURLToPath(import.meta.url);
  const root = path.resolve(path.dirname(scriptFile), "..");
  const reportsDir = path.resolve(root, "reports");
  const docsRunsDir = path.resolve(root, "docs", "runs");
  const tracesDir = path.resolve(root, "data", "traces");
  const sessionsDir = path.resolve(root, "data", "assistant_sessions");

  const stamp = formatTimestamp(new Date());
  const label = sanitizeLabel(args.label);
  const runFolderName = label ? `${stamp}_${label}` : stamp;
  const runDir = path.join(docsRunsDir, runFolderName);

  const reportEntries = pickReportEntries(reportsDir);
  const traceFiles = pickLogFiles(tracesDir, /\.json$/i);
  const sessionFiles = pickLogFiles(sessionsDir, /\.json$/i);

  const copiedReports = [];
  const copiedLogs = [];

  if (!args.dryRun) {
    ensureDir(runDir);
    ensureDir(path.join(runDir, "reports"));
    ensureDir(path.join(runDir, "logs", "traces"));
    ensureDir(path.join(runDir, "logs", "assistant_sessions"));
  }

  for (const sourcePath of reportEntries) {
    const stat = fs.statSync(sourcePath);
    if (args.dryRun) {
      copiedReports.push(sourcePath);
      continue;
    }
    if (stat.isDirectory()) {
      copiedReports.push(...copyDirectoryPreserveRelative(sourcePath, reportsDir, path.join(runDir, "reports")));
    } else {
      copiedReports.push(copyFilePreserveRelative(sourcePath, reportsDir, path.join(runDir, "reports")));
    }
  }

  for (const sourcePath of traceFiles) {
    if (!args.dryRun) {
      copiedLogs.push(copyFilePreserveRelative(sourcePath, tracesDir, path.join(runDir, "logs", "traces")));
    } else {
      copiedLogs.push(sourcePath);
    }
  }

  for (const sourcePath of sessionFiles) {
    if (!args.dryRun) {
      copiedLogs.push(copyFilePreserveRelative(sourcePath, sessionsDir, path.join(runDir, "logs", "assistant_sessions")));
    } else {
      copiedLogs.push(sourcePath);
    }
  }

  if (args.clean && !args.dryRun) {
    for (const entryPath of reportEntries) {
      removePath(entryPath);
    }
    for (const entryPath of traceFiles) {
      removePath(entryPath);
    }
    for (const entryPath of sessionFiles) {
      removePath(entryPath);
    }
  }

  const manifest = {
    schema_version: "run_artifact_manifest_v0_1",
    run_folder: runDir,
    generated_at: new Date().toISOString(),
    options: {
      clean: args.clean,
      dry_run: args.dryRun,
      label: label || null
    },
    counts: {
      report_entries_found: reportEntries.length,
      trace_logs_found: traceFiles.length,
      session_logs_found: sessionFiles.length,
      report_files_copied: copiedReports.length,
      log_files_copied: copiedLogs.length
    },
    preserved_static_reports: [
      "v2_0_1_clarification_eval_plan.md",
      "v2_0_2_eval_plan.md",
      "v2_pilot_eval_plan.md"
    ]
  };

  if (!args.dryRun) {
    writeJson(path.join(runDir, "manifest.json"), manifest);
    fs.writeFileSync(
      path.join(runDir, "README.md"),
      [
        "# Run Artifact Bundle",
        "",
        `- generated_at: ${manifest.generated_at}`,
        `- clean_mode: ${args.clean}`,
        `- reports_copied: ${manifest.counts.report_files_copied}`,
        `- logs_copied: ${manifest.counts.log_files_copied}`
      ].join("\n"),
      "utf-8"
    );
  }

  const summary = [
    `run_folder=${runDir}`,
    `report_entries_found=${reportEntries.length}`,
    `trace_logs_found=${traceFiles.length}`,
    `session_logs_found=${sessionFiles.length}`,
    `clean_mode=${args.clean}`,
    `dry_run=${args.dryRun}`
  ].join("\n");
  process.stdout.write(`${summary}\n`);
}

main();
