import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { findGitRoot, resolveGitHeadPath } from "./git-root.js";

async function makeTempDir(label: string): Promise<string> {
  return fs.mkdtemp(path.join(os.tmpdir(), `openclaw-${label}-`));
}

async function expectGitRootResolution(params: {
  label: string;
  setup: (
    temp: string,
  ) => Promise<{ startPath: string; expectedRoot: string | null; expectedHead: string | null }>;
}): Promise<void> {
  const temp = await makeTempDir(params.label);
  const { startPath, expectedRoot, expectedHead } = await params.setup(temp);
  expect(findGitRoot(startPath)).toBe(expectedRoot);
  expect(resolveGitHeadPath(startPath)).toBe(expectedHead);
}

describe("git-root", () => {
  it.each([
    {
      name: "starting at the repo root itself",
      label: "git-root-self",
      setup: async (temp: string) => {
        const repoRoot = path.join(temp, "repo");
        await fs.mkdir(path.join(repoRoot, ".git"), { recursive: true });
        return {
          startPath: repoRoot,
          expectedRoot: repoRoot,
          expectedHead: path.join(repoRoot, ".git", "HEAD"),
        };
      },
    },
    {
      name: ".git is a directory",
      label: "git-root-dir",
      setup: async (temp: string) => {
        const repoRoot = path.join(temp, "repo");
        const workspace = path.join(repoRoot, "nested", "workspace");
        await fs.mkdir(path.join(repoRoot, ".git"), { recursive: true });
        await fs.mkdir(workspace, { recursive: true });
        return {
          startPath: workspace,
          expectedRoot: repoRoot,
          expectedHead: path.join(repoRoot, ".git", "HEAD"),
        };
      },
    },
    {
      name: ".git is a gitdir pointer file",
      label: "git-root-file",
      setup: async (temp: string) => {
        const repoRoot = path.join(temp, "repo");
        const workspace = path.join(repoRoot, "nested", "workspace");
        const gitDir = path.join(repoRoot, ".actual-git");
        await fs.mkdir(workspace, { recursive: true });
        await fs.mkdir(gitDir, { recursive: true });
        await fs.writeFile(path.join(repoRoot, ".git"), "gitdir: .actual-git\n", "utf-8");
        return {
          startPath: workspace,
          expectedRoot: repoRoot,
          expectedHead: path.join(gitDir, "HEAD"),
        };
      },
    },
    {
      name: "invalid gitdir content still keeps root detection",
      label: "git-root-invalid-file",
      setup: async (temp: string) => {
        const parentRoot = path.join(temp, "repo");
        const childRoot = path.join(parentRoot, "child");
        const nested = path.join(childRoot, "nested");
        await fs.mkdir(path.join(parentRoot, ".git"), { recursive: true });
        await fs.mkdir(nested, { recursive: true });
        await fs.writeFile(path.join(childRoot, ".git"), "not-a-gitdir-pointer\n", "utf-8");
        return {
          startPath: nested,
          expectedRoot: childRoot,
          expectedHead: path.join(parentRoot, ".git", "HEAD"),
        };
      },
    },
    {
      name: "invalid gitdir content without a parent repo",
      label: "git-root-invalid-only",
      setup: async (temp: string) => {
        const repoRoot = path.join(temp, "repo");
        const nested = path.join(repoRoot, "nested");
        await fs.mkdir(nested, { recursive: true });
        await fs.writeFile(path.join(repoRoot, ".git"), "not-a-gitdir-pointer\n", "utf-8");
        return {
          startPath: nested,
          expectedRoot: repoRoot,
          expectedHead: null,
        };
      },
    },
  ])("resolves git roots when $name", async ({ label, setup }) => {
    await expectGitRootResolution({ label, setup });
  });

  it("respects maxDepth traversal limit", async () => {
    const temp = await makeTempDir("git-root-depth");
    const repoRoot = path.join(temp, "repo");
    const nested = path.join(repoRoot, "a", "b", "c");
    await fs.mkdir(path.join(repoRoot, ".git"), { recursive: true });
    await fs.mkdir(nested, { recursive: true });

    expect(findGitRoot(nested, { maxDepth: 2 })).toBeNull();
    expect(resolveGitHeadPath(nested, { maxDepth: 2 })).toBeNull();
  });
});
