import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";
import { runNodeWatchedPaths } from "../../scripts/run-node.mjs";
import { runWatchMain } from "../../scripts/watch-node.mjs";

const createFakeProcess = () =>
  Object.assign(new EventEmitter(), {
    pid: 4242,
    execPath: "/usr/local/bin/node",
  }) as unknown as NodeJS.Process;

const createWatchHarness = () => {
  const child = Object.assign(new EventEmitter(), {
    kill: vi.fn(),
  });
  const spawn = vi.fn(() => child);
  const fakeProcess = createFakeProcess();
  return { child, spawn, fakeProcess };
};

describe("watch-node script", () => {
  it("wires node watch to run-node with watched source/config paths", async () => {
    const { child, spawn, fakeProcess } = createWatchHarness();

    const runPromise = runWatchMain({
      args: ["gateway", "--force"],
      cwd: "/tmp/openclaw",
      env: { PATH: "/usr/bin" },
      now: () => 1700000000000,
      process: fakeProcess,
      spawn,
    });

    queueMicrotask(() => child.emit("exit", 0, null));
    const exitCode = await runPromise;

    expect(exitCode).toBe(0);
    expect(spawn).toHaveBeenCalledTimes(1);
    expect(spawn).toHaveBeenCalledWith(
      "/usr/local/bin/node",
      [
        ...runNodeWatchedPaths.flatMap((watchPath) => ["--watch-path", watchPath]),
        "--watch-preserve-output",
        "scripts/run-node.mjs",
        "gateway",
        "--force",
      ],
      expect.objectContaining({
        cwd: "/tmp/openclaw",
        stdio: "inherit",
        env: expect.objectContaining({
          PATH: "/usr/bin",
          OPENCLAW_WATCH_MODE: "1",
          OPENCLAW_WATCH_SESSION: "1700000000000-4242",
          OPENCLAW_WATCH_COMMAND: "gateway --force",
        }),
      }),
    );
  });

  it("terminates child on SIGINT and returns shell interrupt code", async () => {
    const { child, spawn, fakeProcess } = createWatchHarness();

    const runPromise = runWatchMain({
      args: ["gateway", "--force"],
      process: fakeProcess,
      spawn,
    });

    fakeProcess.emit("SIGINT");
    const exitCode = await runPromise;

    expect(exitCode).toBe(130);
    expect(child.kill).toHaveBeenCalledWith("SIGTERM");
    expect(fakeProcess.listenerCount("SIGINT")).toBe(0);
    expect(fakeProcess.listenerCount("SIGTERM")).toBe(0);
  });

  it("terminates child on SIGTERM and returns shell terminate code", async () => {
    const { child, spawn, fakeProcess } = createWatchHarness();

    const runPromise = runWatchMain({
      args: ["gateway", "--force"],
      process: fakeProcess,
      spawn,
    });

    fakeProcess.emit("SIGTERM");
    const exitCode = await runPromise;

    expect(exitCode).toBe(143);
    expect(child.kill).toHaveBeenCalledWith("SIGTERM");
    expect(fakeProcess.listenerCount("SIGINT")).toBe(0);
    expect(fakeProcess.listenerCount("SIGTERM")).toBe(0);
  });
});
