#!/usr/bin/env node

import path from "node:path";
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import JSZip from "jszip";
import { validateSkill } from "./quick_validate.mjs";

const EXCLUDE_DIRS = new Set(["__pycache__", "node_modules"]);
const EXCLUDE_GLOBS = [/\.pyc$/i];
const EXCLUDE_FILES = new Set([".DS_Store"]);
const ROOT_EXCLUDE_DIRS = new Set(["evals"]);

function shouldExclude(relPath) {
  const parts = relPath.split("/").filter(Boolean);
  if (parts.some((part) => EXCLUDE_DIRS.has(part))) {
    return true;
  }
  if (parts.length > 1 && ROOT_EXCLUDE_DIRS.has(parts[1])) {
    return true;
  }
  const name = parts[parts.length - 1] || "";
  if (EXCLUDE_FILES.has(name)) {
    return true;
  }
  return EXCLUDE_GLOBS.some((pattern) => pattern.test(name));
}

async function collectFiles(dir, baseDir, acc = []) {
  const entries = await readdir(dir, { withFileTypes: true });
  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      await collectFiles(fullPath, baseDir, acc);
      continue;
    }
    if (!entry.isFile()) continue;
    acc.push({
      fullPath,
      arcname: path.relative(baseDir, fullPath).replace(/\\/g, "/"),
    });
  }
  return acc;
}

export async function packageSkill(skillPath, outputDir) {
  const resolvedSkillPath = path.resolve(skillPath);
  const skillStats = await stat(resolvedSkillPath).catch(() => null);
  if (!skillStats) {
    process.stdout.write(`❌ Error: Skill folder not found: ${resolvedSkillPath}\n`);
    return null;
  }
  if (!skillStats.isDirectory()) {
    process.stdout.write(`❌ Error: Path is not a directory: ${resolvedSkillPath}\n`);
    return null;
  }

  const skillMdPath = path.join(resolvedSkillPath, "SKILL.md");
  const skillMdStats = await stat(skillMdPath).catch(() => null);
  if (!skillMdStats || !skillMdStats.isFile()) {
    process.stdout.write(`❌ Error: SKILL.md not found in ${resolvedSkillPath}\n`);
    return null;
  }

  process.stdout.write("🔍 Validating skill...\n");
  const validation = await validateSkill(resolvedSkillPath);
  if (!validation.valid) {
    process.stdout.write(`❌ Validation failed: ${validation.message}\n`);
    process.stdout.write("   Please fix the validation errors before packaging.\n");
    return null;
  }
  process.stdout.write(`✅ ${validation.message}\n\n`);

  const outputPath = outputDir ? path.resolve(outputDir) : process.cwd();
  await mkdir(outputPath, { recursive: true });

  const skillName = path.basename(resolvedSkillPath);
  const archivePath = path.join(outputPath, `${skillName}.skill`);
  const zip = new JSZip();
  const baseDir = path.dirname(resolvedSkillPath);
  const files = await collectFiles(resolvedSkillPath, baseDir);

  for (const file of files) {
    if (shouldExclude(file.arcname)) {
      process.stdout.write(`  Skipped: ${file.arcname}\n`);
      continue;
    }
    zip.file(file.arcname, await readFile(file.fullPath));
    process.stdout.write(`  Added: ${file.arcname}\n`);
  }

  const buffer = await zip.generateAsync({
    type: "nodebuffer",
    compression: "DEFLATE",
    compressionOptions: { level: 9 },
  });
  await writeFile(archivePath, buffer);

  process.stdout.write(`\n✅ Successfully packaged skill to: ${archivePath}\n`);
  return archivePath;
}

async function main(argv = process.argv) {
  if (argv.length < 3) {
    process.stdout.write("Usage: node package_skill.mjs <path/to/skill-folder> [output-directory]\n");
    process.stdout.write("\nExample:\n");
    process.stdout.write("  node package_skill.mjs skills/public/my-skill\n");
    process.stdout.write("  node package_skill.mjs skills/public/my-skill ./dist\n");
    process.exitCode = 1;
    return;
  }

  const skillPath = argv[2];
  const outputDir = argv[3];

  process.stdout.write(`📦 Packaging skill: ${skillPath}\n`);
  if (outputDir) {
    process.stdout.write(`   Output directory: ${outputDir}\n`);
  }
  process.stdout.write("\n");

  const result = await packageSkill(skillPath, outputDir);
  process.exitCode = result ? 0 : 1;
}

const entryPath = fileURLToPath(import.meta.url);
if (process.argv[1] && path.resolve(process.argv[1]) === entryPath) {
  main().catch((error) => {
    process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
    process.exitCode = 1;
  });
}
