#!/usr/bin/env node

"use strict";

/**
 * Verify tarball contents match expected file structure.
 * Uses `npm pack --dry-run --json` to inspect without creating a file.
 *
 * Modes:
 *   node scripts/verify-tarball.js          # Human-readable output
 *   node scripts/verify-tarball.js --json   # Machine-readable JSON output
 *
 * Exit codes:
 *   0 — tarball contents are valid
 *   1 — validation failed
 */

const { execSync } = require("child_process");
const path = require("path");

const ROOT = path.resolve(__dirname, "..");

// Files that MUST be in the tarball
const REQUIRED_FILES = [
  "bin/install.js",
  "bin/lib/cleanup.js",
  "bin/lib/constants.js",
  "bin/lib/diagnostics.js",
  "bin/lib/fs-utils.js",
  "bin/lib/install-phases.js",
  "bin/lib/locking.js",
  "bin/lib/logging.js",
  "bin/lib/migrations.js",
  "bin/lib/protections.js",
  "bin/lib/settings.js",
  "scaffold/global/compiled-hooks/dist/evolv-check-update.js",
  "scaffold/global/compiled-hooks/dist/evolv-statusline.js",
  "scaffold/global/compiled-hooks/dist/evolv-context-monitor.js",
  "scaffold/global/compiled-hooks/dist/evolv-session-guidance.js",
  "scaffold/global/global-catalog.yaml",
  "scaffold/project/primitives/_index.yaml",
  "package.json",
];

// Only these top-level directories should appear
const ALLOWED_TOP_DIRS = new Set(["bin", "scaffold", "scripts"]);

// Reasonable bounds for file count (guard against accidental inclusion/omission)
const MIN_FILES = 50;
const MAX_FILES = 1000;

function getPackFiles() {
  const output = execSync("npm pack --dry-run --json", {
    cwd: ROOT,
    encoding: "utf-8",
    stdio: ["pipe", "pipe", "pipe"],
  });
  const parsed = JSON.parse(output);
  // npm pack --json returns an array with one entry
  const entry = Array.isArray(parsed) ? parsed[0] : parsed;
  return entry.files.map((f) => f.path);
}

function validate(files) {
  const errors = [];

  // Check required files
  const fileSet = new Set(files);
  for (const req of REQUIRED_FILES) {
    if (!fileSet.has(req)) {
      errors.push(`missing required file: ${req}`);
    }
  }

  // Check top-level directories
  const topDirs = new Set();
  for (const f of files) {
    const firstSegment = f.split("/")[0];
    // Skip top-level files (package.json, etc.)
    if (f.includes("/")) {
      topDirs.add(firstSegment);
    }
  }
  for (const dir of topDirs) {
    if (!ALLOWED_TOP_DIRS.has(dir)) {
      errors.push(`unexpected top-level directory: ${dir}/`);
    }
  }

  // Check file count bounds
  if (files.length < MIN_FILES) {
    errors.push(
      `file count ${files.length} below minimum ${MIN_FILES} — likely missing files`,
    );
  }
  if (files.length > MAX_FILES) {
    errors.push(
      `file count ${files.length} exceeds maximum ${MAX_FILES} — likely accidental inclusion`,
    );
  }

  return errors;
}

function main() {
  const jsonMode = process.argv.includes("--json");

  let files;
  try {
    files = getPackFiles();
  } catch (err) {
    if (jsonMode) {
      console.log(
        JSON.stringify({
          ok: false,
          error: "npm pack failed",
          details: err.message,
        }),
      );
    } else {
      console.error(`\n  [FAIL] npm pack --dry-run failed: ${err.message}\n`);
    }
    process.exit(1);
  }

  const errors = validate(files);

  if (jsonMode) {
    console.log(
      JSON.stringify({
        ok: errors.length === 0,
        file_count: files.length,
        errors,
        top_dirs: [
          ...new Set(
            files.filter((f) => f.includes("/")).map((f) => f.split("/")[0]),
          ),
        ],
      }),
    );
  } else {
    console.log("\n  Tarball content verification...\n");
    if (errors.length === 0) {
      console.log(`  [OK] ${files.length} files, all required files present.`);
      console.log(
        `  [OK] Top-level dirs: ${[...new Set(files.filter((f) => f.includes("/")).map((f) => f.split("/")[0]))].join(", ")}`,
      );
      console.log("\n  Tarball contents verified.\n");
    } else {
      for (const err of errors) {
        console.error(`  [FAIL] ${err}`);
      }
      console.error(`\n  ${errors.length} tarball validation error(s).\n`);
    }
  }

  process.exit(errors.length > 0 ? 1 : 0);
}

main();
