import { access, cp, readdir, rename, rm, stat } from "node:fs/promises";
import { constants as fsConstants } from "node:fs";
import { fileURLToPath } from "node:url";
import { join, relative, resolve } from "node:path";

function getTemplateDir() {
  return fileURLToPath(new URL("../../templates/.agent", import.meta.url));
}

async function exists(path) {
  try {
    await access(path, fsConstants.F_OK);
    return true;
  } catch {
    return false;
  }
}

function parseInitArgs(args) {
  const parsed = {
    target: ".",
    force: false,
    backup: false,
    dryRun: false,
  };
  let targetSet = false;

  for (const arg of args) {
    if (arg === "--force" || arg === "-f") {
      parsed.force = true;
      continue;
    }

    if (arg === "--backup" || arg === "-b") {
      parsed.backup = true;
      continue;
    }

    if (arg === "--dry-run" || arg === "-n") {
      parsed.dryRun = true;
      continue;
    }

    if (arg.startsWith("-")) {
      throw new Error(`Unknown option for init: ${arg}`);
    }

    if (targetSet) {
      throw new Error("Too many positional arguments. Only one target directory is supported.");
    }

    parsed.target = arg;
    targetSet = true;
  }

  return parsed;
}

async function validateTargetDir(targetDir) {
  let targetStat;
  try {
    targetStat = await stat(targetDir);
  } catch {
    throw new Error(`Target directory does not exist: ${targetDir}`);
  }

  if (!targetStat.isDirectory()) {
    throw new Error(`Target path is not a directory: ${targetDir}`);
  }
}

async function collectFiles(dir, base = dir) {
  const entries = await readdir(dir, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    const fullPath = join(dir, entry.name);
    if (entry.isDirectory()) {
      files.push(...(await collectFiles(fullPath, base)));
    } else {
      files.push(relative(base, fullPath));
    }
  }
  return files.sort();
}

export async function initCommand(args, { cwd, stdout, stderr }) {
  let parsed;
  try {
    parsed = parseInitArgs(args);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    stderr.write(`${message}\n`);
    return 1;
  }

  const targetDir = resolve(cwd, parsed.target);
  const agentDir = join(targetDir, ".agent");
  const templateDir = getTemplateDir();

  try {
    await validateTargetDir(targetDir);

    const templateExists = await exists(templateDir);
    if (!templateExists) {
      throw new Error(
        "Bundled template is missing. Run `npm run sync:template` before using init from source.",
      );
    }

    const agentExists = await exists(agentDir);

    if (parsed.dryRun) {
      const files = await collectFiles(templateDir);
      stdout.write(`Dry run: the following files would be copied to ${agentDir}/\n\n`);
      for (const file of files) {
        stdout.write(`  .agent/${file}\n`);
      }
      stdout.write(`\nTotal: ${files.length} files\n`);
      if (agentExists && !parsed.force) {
        stdout.write(`\nNote: .agent already exists. Use --force to replace it.\n`);
      }
      return 0;
    }

    if (agentExists && !parsed.force) {
      stderr.write(
        `.agent already exists at ${agentDir}. Re-run with --force to replace it.\n`,
      );
      return 1;
    }

    if (agentExists && parsed.force) {
      if (parsed.backup) {
        const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
        const backupDir = join(targetDir, `.agent-backup-${timestamp}`);
        await rename(agentDir, backupDir);
        stdout.write(`Backed up existing .agent to ${backupDir}\n`);
      } else {
        await rm(agentDir, { recursive: true, force: true });
      }
    }

    await cp(templateDir, agentDir, { recursive: true });

    stdout.write(`Initialized Antigravity Superpowers profile at ${agentDir}\n`);
    stdout.write("Next step: antigravity-superpowers validate\n");
    stdout.write(
      "Note: docs/plans/task.md is created at runtime by skills when task tracking starts.\n",
    );
    return 0;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    stderr.write(`Init failed: ${message}\n`);
    return 1;
  }
}
