// The process wrapper against the real oxy binary on a scratch repository.
// Runs with `npm test`; needs oxy on PATH, at ~/.local/bin/oxy, or in OXY_BIN.

import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Oxy, findBinary } from "../out/oxy.mjs";

const binary = process.env.OXY_BIN ?? findBinary(undefined);
const oxy = new Oxy(binary);

test("root, status, cat, log and commit round trip", async () => {
  const dir = mkdtempSync(join(tmpdir(), "oxy-vscode-"));
  const home = join(dir, "home");
  mkdirSync(home);
  process.env.OXY_HOME = home;
  await oxy.check(dir, ["init", "repo"]);
  const root = join(dir, "repo");
  await oxy.check(root, ["identity", "create", "--name", "Test", "--email", "t@example.org"]);

  const version = await oxy.version();
  assert.equal(version.protocol, 1);
  assert.equal(await oxy.root(dir), null, "outside a repository");
  const info = await oxy.root(root);
  assert.equal(info?.bookmark, "main");
  assert.ok(info?.root.endsWith("repo"));

  writeFileSync(join(root, "a.txt"), "one\n");
  let status = await oxy.status(root);
  assert.deepEqual(status.paths, [{ path: "a.txt", status: "Added" }]);
  assert.equal(status.clean, false);
  await oxy.commit(root, "first");
  status = await oxy.status(root);
  assert.equal(status.clean, true);

  writeFileSync(join(root, "a.txt"), "one\ntwo\n");
  assert.equal((await oxy.cat(root, "a.txt")).toString(), "one\n", "the base version");
  const log = await oxy.log(root, 10);
  assert.equal(log.changes.filter((c) => !c.working_copy).length, 1);
  assert.equal(log.changes.find((c) => !c.working_copy)?.signature?.status, "unsigned");
  assert.ok((await oxy.prompt(root)).includes("main"));
  const bookmarks = await oxy.bookmarks(root);
  assert.deepEqual(bookmarks.map((b) => [b.name, b.current]), [["main", true]]);
  await assert.rejects(oxy.cat(root, "missing.txt"), /not in that state/);

  // Write operations: hunks, commit by hunk, discard, amend, undo, op log.
  writeFileSync(join(root, "b.txt"), "b\n");
  const hunks = await oxy.hunks(root);
  assert.ok(hunks.length >= 2, JSON.stringify(hunks));
  const bHunk = hunks.find((h) => h.path === "b.txt");
  await oxy.commitHunks(root, "b only", [bHunk.index]);
  status = await oxy.status(root);
  assert.deepEqual(status.paths.map((p) => p.path), ["a.txt"], "a.txt stays uncommitted");
  await oxy.discard(root, ["a.txt"]);
  assert.equal((await oxy.status(root)).clean, true);
  writeFileSync(join(root, "b.txt"), "b\nmore\n");
  await oxy.amend(root, "b, amended");
  assert.equal((await oxy.status(root)).clean, true);
  const log2 = await oxy.log(root, 10);
  assert.ok(log2.changes.some((c) => c.message === "b, amended"));
  const ops = await oxy.opLog(root);
  assert.ok(ops[0].current && ops.length > 3);
  assert.match(await oxy.undo(root), /Undone/);
  assert.equal((await oxy.status(root)).clean, false, "the amend is undone; its edit is back on disk as uncommitted work");
});
