import { describe, expect, it, vi } from "vitest";
import {
  forwardSignalToVitestProcessGroup,
  installVitestProcessGroupCleanup,
  resolveVitestProcessGroupSignalTarget,
  shouldUseDetachedVitestProcessGroup,
} from "../../scripts/vitest-process-group.mjs";

describe("vitest process group helpers", () => {
  it("uses detached process groups on non-Windows hosts", () => {
    expect(shouldUseDetachedVitestProcessGroup("darwin")).toBe(true);
    expect(shouldUseDetachedVitestProcessGroup("linux")).toBe(true);
    expect(shouldUseDetachedVitestProcessGroup("win32")).toBe(false);
  });

  it("targets the process group on Unix and the direct pid on Windows", () => {
    expect(resolveVitestProcessGroupSignalTarget({ childPid: 4200, platform: "darwin" })).toBe(
      -4200,
    );
    expect(resolveVitestProcessGroupSignalTarget({ childPid: 4200, platform: "win32" })).toBe(4200);
    expect(resolveVitestProcessGroupSignalTarget({ childPid: undefined, platform: "darwin" })).toBe(
      null,
    );
  });

  it("forwards signals to the computed target and ignores cleanup races", () => {
    const kill = vi.fn();
    expect(
      forwardSignalToVitestProcessGroup({
        child: { pid: 4200 },
        signal: "SIGTERM",
        platform: "darwin",
        kill,
      }),
    ).toBe(true);
    expect(kill).toHaveBeenCalledWith(-4200, "SIGTERM");

    kill.mockImplementationOnce(() => {
      const error = new Error("gone") as NodeJS.ErrnoException;
      error.code = "ESRCH";
      throw error;
    });
    expect(
      forwardSignalToVitestProcessGroup({
        child: { pid: 4200 },
        signal: "SIGTERM",
        platform: "darwin",
        kill,
      }),
    ).toBe(false);

    kill.mockImplementationOnce(() => {
      const error = new Error("permission race") as NodeJS.ErrnoException;
      error.code = "EPERM";
      throw error;
    });
    expect(
      forwardSignalToVitestProcessGroup({
        child: { pid: 4200 },
        signal: "SIGTERM",
        platform: "darwin",
        kill,
      }),
    ).toBe(false);
  });

  it("installs and removes process cleanup listeners", () => {
    const listeners = new Map<string, Set<() => void>>();
    const fakeProcess = {
      on(event: string, handler: () => void) {
        const set = listeners.get(event) ?? new Set();
        set.add(handler);
        listeners.set(event, set);
      },
      off(event: string, handler: () => void) {
        listeners.get(event)?.delete(handler);
      },
    };
    const kill = vi.fn();
    const teardown = installVitestProcessGroupCleanup({
      child: { pid: 4200 },
      processObject: fakeProcess as unknown as NodeJS.Process,
      platform: "darwin",
      kill,
    });

    expect(listeners.get("SIGINT")?.size).toBe(1);
    expect(listeners.get("SIGTERM")?.size).toBe(1);
    expect(listeners.get("exit")?.size).toBe(1);

    listeners.get("SIGTERM")?.values().next().value?.();
    expect(kill).toHaveBeenCalledWith(-4200, "SIGTERM");

    teardown();
    expect(listeners.get("SIGINT")?.size ?? 0).toBe(0);
    expect(listeners.get("SIGTERM")?.size ?? 0).toBe(0);
    expect(listeners.get("exit")?.size ?? 0).toBe(0);
  });
});
