import { describe, test, expect, beforeEach, afterEach } from "bun:test";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { detectPackageManager } from "@/utils/package-manager.js";

describe("detectPackageManager", () => {
  let localPath: string;

  beforeEach(() => {
    localPath = mkdtempSync(join(tmpdir(), "hu-pm-test-"));
  });

  afterEach(() => {
    rmSync(localPath, { recursive: true, force: true });
  });

  test("detects bun from bun.lock", () => {
    writeFileSync(join(localPath, "bun.lock"), "");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("bun");
    expect(result.installArgs).toEqual(["install", "--frozen-lockfile"]);
  });

  test("detects bun from bun.lockb (binary lockfile)", () => {
    writeFileSync(join(localPath, "bun.lockb"), "");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("bun");
    expect(result.installArgs).toEqual(["install", "--frozen-lockfile"]);
  });

  test("detects npm from package-lock.json", () => {
    writeFileSync(join(localPath, "package-lock.json"), "{}");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("npm");
    expect(result.installArgs).toEqual(["install", "--ignore-engines"]);
  });

  test("detects yarn from yarn.lock (classic, no packageManager field)", () => {
    writeFileSync(join(localPath, "yarn.lock"), "");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("yarn");
    expect(result.installArgs).toEqual(["install", "--frozen-lockfile"]);
  });

  test("detects yarn v2+ with --immutable when packageManager field specifies yarn >= 2", () => {
    writeFileSync(join(localPath, "yarn.lock"), "");
    writeFileSync(
      join(localPath, "package.json"),
      JSON.stringify({ packageManager: "yarn@4.1.0" }),
    );
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("yarn");
    expect(result.installArgs).toEqual(["install", "--immutable"]);
  });

  test("detects yarn v1 with --frozen-lockfile when packageManager field specifies yarn < 2", () => {
    writeFileSync(join(localPath, "yarn.lock"), "");
    writeFileSync(
      join(localPath, "package.json"),
      JSON.stringify({ packageManager: "yarn@1.22.19" }),
    );
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("yarn");
    expect(result.installArgs).toEqual(["install", "--frozen-lockfile"]);
  });

  test("detects pnpm from pnpm-lock.yaml", () => {
    writeFileSync(join(localPath, "pnpm-lock.yaml"), "");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("pnpm");
    expect(result.installArgs).toEqual(["install", "--frozen-lockfile"]);
  });

  test("bun takes precedence over other lockfiles", () => {
    writeFileSync(join(localPath, "bun.lock"), "");
    writeFileSync(join(localPath, "yarn.lock"), "");
    writeFileSync(join(localPath, "package-lock.json"), "{}");
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("bun");
  });

  test("explicit installCmd override with >= 2 elements takes precedence", () => {
    writeFileSync(join(localPath, "bun.lock"), "");
    const result = detectPackageManager(localPath, ["pnpm", "install", "--no-frozen-lockfile"]);
    expect(result.cmd).toBe("pnpm");
    expect(result.installArgs).toEqual(["install", "--no-frozen-lockfile"]);
  });

  test("installCmd with single element falls through to lockfile detection", () => {
    writeFileSync(join(localPath, "bun.lock"), "");
    const result = detectPackageManager(localPath, ["pnpm"]);
    expect(result.cmd).toBe("bun");
  });

  test("no indicators falls back to npm", () => {
    const result = detectPackageManager(localPath);
    expect(result.cmd).toBe("npm");
    expect(result.installArgs).toEqual(["install", "--ignore-engines"]);
  });
});
