import { parseArgs } from "node:util";

import manifest from "./subtrees.json";

interface Subtree {
  readonly name: string;
  readonly prefix: string;
  readonly upstream: string;
  readonly ref: string;
  readonly squash: true;
  readonly role: string;
  readonly refreshPolicy: "allowed" | "pinned";
}

interface CommandResult {
  readonly exitCode: number;
  readonly stdout: string;
  readonly stderr: string;
}

const cwd = process.cwd();
const subtrees = manifest.subtrees as readonly Subtree[];

const run = async (
  command: readonly string[],
  options: { readonly inherit?: boolean } = {}
): Promise<CommandResult> => {
  const process = Bun.spawn([...command], {
    cwd,
    stdin: options.inherit ? "inherit" : "ignore",
    stdout: options.inherit ? "inherit" : "pipe",
    stderr: options.inherit ? "inherit" : "pipe",
  });
  const [exitCode, stdout, stderr] = await Promise.all([
    process.exited,
    options.inherit ? Promise.resolve("") : new Response(process.stdout).text(),
    options.inherit ? Promise.resolve("") : new Response(process.stderr).text(),
  ]);
  return { exitCode, stdout, stderr };
};

const shellQuote = (value: string) => `'${value.replaceAll("'", `'\\''`)}'`;
const formatCommand = (command: readonly string[]) =>
  command.map(shellQuote).join(" ");

const requireRepositoryRoot = async () => {
  const result = await run(["git", "rev-parse", "--show-toplevel"]);
  if (result.exitCode !== 0) {
    throw new Error("The current directory is not inside a Git checkout.");
  }
  if (result.stdout.trim() !== cwd) {
    throw new Error(
      `Run this script from the repository root: ${result.stdout.trim()}`
    );
  }
};

const requireCleanWorktree = async () => {
  const result = await run(["git", "status", "--porcelain"]);
  if (result.exitCode !== 0) {
    throw new Error(result.stderr.trim());
  }
  if (result.stdout.trim() !== "") {
    throw new Error(
      "Subtree mutations require a clean worktree. Commit or stash changes first."
    );
  }
};

const getSubtree = (name: string | undefined) => {
  if (name === undefined) {
    throw new Error(
      `A subtree name is required. Available: ${subtrees.map((entry) => entry.name).join(", ")}`
    );
  }
  const subtree = subtrees.find((entry) => entry.name === name);
  if (subtree === undefined) {
    throw new Error(
      `Unknown subtree ${shellQuote(name)}. Available: ${subtrees.map((entry) => entry.name).join(", ")}`
    );
  }
  if (
    !subtree.prefix.startsWith("repos/") ||
    !subtree.upstream.startsWith("https://")
  ) {
    throw new Error(
      `Unsafe subtree manifest entry ${shellQuote(subtree.name)}.`
    );
  }
  return subtree;
};

const readSubtreeMetadata = async (subtree: Subtree) => {
  const merge = await run([
    "git",
    "log",
    "--all",
    "-n",
    "1",
    "--format=%H%x09%ci%x09%s",
    "--",
    subtree.prefix,
  ]);
  if (merge.exitCode !== 0) {
    throw new Error(merge.stderr.trim());
  }
  const mergeLine = merge.stdout.trim();
  if (mergeLine === "") {
    return { mergeLine: "No subtree merge found", split: undefined };
  }
  const mergeSha = mergeLine.split("\t")[0];
  if (mergeSha === undefined) {
    return { mergeLine, split: undefined };
  }
  const squash = await run([
    "git",
    "show",
    "--no-patch",
    "--format=%b",
    `${mergeSha}^2`,
  ]);
  const split = squash.stdout
    .split("\n")
    .find((line) => line.startsWith("git-subtree-split: "))
    ?.slice("git-subtree-split: ".length);
  return { mergeLine, split };
};

const readStatus = async (subtree: Subtree) => {
  const [dirty, metadata] = await Promise.all([
    run(["git", "status", "--porcelain", "--", subtree.prefix]),
    readSubtreeMetadata(subtree),
  ]);
  if (dirty.exitCode !== 0) {
    throw new Error(dirty.stderr.trim());
  }
  return {
    dirty: dirty.stdout.trim() !== "",
    latest: metadata.mergeLine,
  };
};

const updateCommand = (subtree: Subtree) => [
  "git",
  "subtree",
  "pull",
  `--prefix=${subtree.prefix}`,
  subtree.upstream,
  subtree.ref,
  "--squash",
];

const addCommand = (subtree: Subtree) => [
  "git",
  "subtree",
  "add",
  `--prefix=${subtree.prefix}`,
  subtree.upstream,
  subtree.ref,
  "--squash",
];

const { values, positionals } = parseArgs({
  args: process.argv.slice(2),
  options: {
    "dry-run": { type: "boolean", default: false },
    "force-pinned": { type: "boolean", default: false },
    help: { type: "boolean", short: "h", default: false },
    yes: { type: "boolean", short: "y", default: false },
  },
  allowPositionals: true,
  strict: true,
});

const [operation = "list", name, ...extra] = positionals;
if (values.help || extra.length > 0) {
  console.log(`Usage: bun run subtree [list|status|preview|add|update] [name] [options]

Commands:
  list                 List configured vendored subtrees
  status [name]        Show local subtree metadata and dirtiness
  preview <name>       Fetch the configured upstream and show incoming commits
  add <name>           Add an absent manifest entry with git subtree add --squash
  update <name>        Pull a configured subtree with git subtree pull --squash

Options:
  --dry-run            Print mutating/fetch commands without executing them
  --yes, -y            Required for add and update
  --force-pinned       Allow updating a pinned historical subtree together with --yes`);
  process.exit(extra.length > 0 ? 1 : 0);
}

try {
  await requireRepositoryRoot();

  switch (operation) {
    case "list": {
      for (const subtree of subtrees) {
        console.log(
          `${subtree.name}\t${subtree.prefix}\t${subtree.upstream}#${subtree.ref}\t${subtree.refreshPolicy}`
        );
      }
      break;
    }
    case "status": {
      const selected = name === undefined ? subtrees : [getSubtree(name)];
      for (const subtree of selected) {
        const status = await readStatus(subtree);
        console.log(`${subtree.name}:`);
        console.log(`  prefix: ${subtree.prefix}`);
        console.log(`  upstream: ${subtree.upstream}#${subtree.ref}`);
        console.log(`  policy: ${subtree.refreshPolicy}`);
        console.log(`  prefix dirty: ${status.dirty ? "yes" : "no"}`);
        console.log(`  latest metadata: ${status.latest}`);
      }
      break;
    }
    case "preview": {
      const subtree = getSubtree(name);
      const fetch = [
        "git",
        "fetch",
        "--no-tags",
        subtree.upstream,
        subtree.ref,
      ];
      if (values["dry-run"]) {
        console.log(formatCommand(fetch));
        console.log(
          formatCommand([
            "git",
            "log",
            "--oneline",
            "--decorate",
            "<last-subtree-split>..FETCH_HEAD",
          ])
        );
        break;
      }
      const fetched = await run(fetch, { inherit: true });
      if (fetched.exitCode !== 0) {
        process.exit(fetched.exitCode);
      }
      const metadata = await readSubtreeMetadata(subtree);
      if (metadata.split === undefined) {
        throw new Error(
          `No git-subtree-split metadata found for ${subtree.prefix}.`
        );
      }
      const log = await run([
        "git",
        "log",
        "--oneline",
        "--decorate",
        `${metadata.split}..FETCH_HEAD`,
      ]);
      if (log.exitCode !== 0) {
        throw new Error(log.stderr.trim());
      }
      console.log(log.stdout.trim() || `${subtree.name} is up to date.`);
      break;
    }
    case "add":
    case "update": {
      const subtree = getSubtree(name);
      const prefixType = await run([
        "git",
        "cat-file",
        "-t",
        `HEAD:${subtree.prefix}`,
      ]);
      const prefixExists =
        prefixType.exitCode === 0 && prefixType.stdout.trim() === "tree";
      if (
        operation === "update" &&
        subtree.refreshPolicy === "pinned" &&
        !values["force-pinned"]
      ) {
        throw new Error(
          `${subtree.name} is pinned historical reference material. Pass --force-pinned --yes only when historical synchronization is explicitly required.`
        );
      }
      if (!values["dry-run"]) {
        await requireCleanWorktree();
      }
      if (operation === "add" && prefixExists) {
        throw new Error(
          `Cannot add ${subtree.name}: ${subtree.prefix} already exists.`
        );
      }
      if (operation === "update" && !prefixExists) {
        throw new Error(
          `Cannot update ${subtree.name}: ${subtree.prefix} does not exist.`
        );
      }
      const command =
        operation === "add" ? addCommand(subtree) : updateCommand(subtree);
      console.log(formatCommand(command));
      if (values["dry-run"]) {
        break;
      }
      if (!values.yes) {
        throw new Error(
          `Refusing to ${operation} without --yes. Re-run after reviewing the command above.`
        );
      }
      const result = await run(command, { inherit: true });
      if (result.exitCode !== 0) {
        process.exit(result.exitCode);
      }
      break;
    }
    default:
      throw new Error(
        `Unknown command ${shellQuote(operation)}. Use list, status, preview, add, or update.`
      );
  }
} catch (error) {
  console.error(error instanceof Error ? error.message : String(error));
  process.exit(1);
}
