import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

// We test the internal findPackageRoot logic by importing the module and testing
// resolveMinionRoot indirectly. Since resolveMinionRoot caches, we need to
// re-import fresh for each test or clear the cache.

// Instead, we extract and test the findPackageRoot function directly by
// creating test directory structures with package.json files.

function findPackageRoot(startDir: string, name: string): string | null {
  let dir = startDir;
  for (;;) {
    const pkgPath = path.join(dir, "package.json");
    try {
      if (fs.existsSync(pkgPath)) {
        const raw = fs.readFileSync(pkgPath, "utf8");
        const pkg = JSON.parse(raw) as { name?: string };
        if (pkg.name === name) {
          return dir;
        }
      }
    } catch {
      // ignore parse errors and keep walking
    }
    const parent = path.dirname(dir);
    if (parent === dir) {
      return null;
    }
    dir = parent;
  }
}

describe("findPackageRoot", () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "core-bridge-test-"));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it("finds 'minion' package by name", () => {
    const pkgDir = path.join(tmpDir, "node_modules", "minion");
    fs.mkdirSync(pkgDir, { recursive: true });
    fs.writeFileSync(path.join(pkgDir, "package.json"), JSON.stringify({ name: "minion" }));

    const subDir = path.join(pkgDir, "dist");
    fs.mkdirSync(subDir, { recursive: true });

    const result = findPackageRoot(subDir, "minion");
    expect(result).toBe(pkgDir);
  });

  it("finds '@nikolasp98/minion' scoped package by name", () => {
    const pkgDir = path.join(tmpDir, "node_modules", "@nikolasp98", "minion");
    fs.mkdirSync(pkgDir, { recursive: true });
    fs.writeFileSync(
      path.join(pkgDir, "package.json"),
      JSON.stringify({ name: "@nikolasp98/minion" }),
    );

    const subDir = path.join(pkgDir, "dist");
    fs.mkdirSync(subDir, { recursive: true });

    const result = findPackageRoot(subDir, "@nikolasp98/minion");
    expect(result).toBe(pkgDir);
  });

  it("returns null when package not found", () => {
    const subDir = path.join(tmpDir, "some", "deep", "path");
    fs.mkdirSync(subDir, { recursive: true });

    const result = findPackageRoot(subDir, "minion");
    expect(result).toBeNull();
  });

  it("skips directories with different package name", () => {
    // Create an unrelated package.json at tmpDir
    fs.writeFileSync(
      path.join(tmpDir, "package.json"),
      JSON.stringify({ name: "some-other-package" }),
    );

    const subDir = path.join(tmpDir, "nested");
    fs.mkdirSync(subDir, { recursive: true });

    const result = findPackageRoot(subDir, "minion");
    expect(result).toBeNull();
  });

  it("skips malformed package.json and keeps walking", () => {
    // Create a valid target higher up
    const pkgDir = tmpDir;
    fs.writeFileSync(path.join(pkgDir, "package.json"), JSON.stringify({ name: "minion" }));

    // Create malformed package.json in subdirectory
    const subDir = path.join(tmpDir, "broken");
    fs.mkdirSync(subDir, { recursive: true });
    fs.writeFileSync(path.join(subDir, "package.json"), "not valid json{{{");

    const deepDir = path.join(subDir, "deeper");
    fs.mkdirSync(deepDir, { recursive: true });

    const result = findPackageRoot(deepDir, "minion");
    expect(result).toBe(pkgDir);
  });
});

describe("resolveMinionRoot package name list", () => {
  it("the names list in core-bridge.ts includes both minion and @nikolasp98/minion", async () => {
    const coreBridgeSrc = fs.readFileSync(
      path.join(__dirname, "core-bridge.ts"),
      "utf8",
    );
    expect(coreBridgeSrc).toContain('"minion"');
    expect(coreBridgeSrc).toContain('"@nikolasp98/minion"');
  });

  it("MINION_ROOT env var takes priority over auto-detection", () => {
    // This is a documentation-style test verifying the source code pattern
    const coreBridgeSrc = fs.readFileSync(
      path.join(__dirname, "core-bridge.ts"),
      "utf8",
    );
    // The override check happens before the candidate loop
    expect(coreBridgeSrc).toMatch(/process\.env\.MINION_ROOT.*trim\(\)/);
    expect(coreBridgeSrc.indexOf("MINION_ROOT")).toBeLessThan(
      coreBridgeSrc.indexOf("candidates"),
    );
  });
});
