// Ensure Cli Startup Build tests cover ensure cli startup build script behavior.
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
  ensureCliStartupBuild,
  hasCliStartupBuild,
  resolveCliStartupBuildTimeoutMs,
} from "../../scripts/ensure-cli-startup-build.mts";

const tempRoots: string[] = [];

function makeTempRoot(): string {
  const root = mkdtempSync(path.join(tmpdir(), "openclaw-cli-startup-build-"));
  tempRoots.push(root);
  mkdirSync(path.join(root, "scripts"), { recursive: true });
  writeFileSync(path.join(root, "scripts", "build-all.mts"), "", "utf8");
  return root;
}

afterEach(() => {
  for (const root of tempRoots.splice(0)) {
    rmSync(root, { recursive: true, force: true });
  }
});

describe("ensure-cli-startup-build", () => {
  it("detects an existing CLI startup build", () => {
    const root = makeTempRoot();
    mkdirSync(path.join(root, "dist"), { recursive: true });
    writeFileSync(path.join(root, "dist", "entry.js"), "export {};\n", "utf8");
    writeFileSync(
      path.join(root, "dist", "cli-startup-metadata.json"),
      '{"rootHelpText":"help\\n"}\n',
      "utf8",
    );

    expect(hasCliStartupBuild({ rootDir: root })).toBe(true);
  });

  it("skips the build profile when dist entry output and startup metadata already exist", () => {
    const root = makeTempRoot();
    mkdirSync(path.join(root, "dist"), { recursive: true });
    writeFileSync(path.join(root, "dist", "entry.mjs"), "export {};\n", "utf8");
    writeFileSync(
      path.join(root, "dist", "cli-startup-metadata.json"),
      '{"rootHelpText":"help\\n"}\n',
      "utf8",
    );

    const result = ensureCliStartupBuild({
      rootDir: root,
      spawnSync: () => {
        throw new Error("unexpected build");
      },
    });

    expect(result).toEqual({ built: false });
  });

  it("runs the cliStartup build profile when startup metadata is missing", () => {
    const root = makeTempRoot();
    mkdirSync(path.join(root, "dist"), { recursive: true });
    writeFileSync(path.join(root, "dist", "entry.js"), "export {};\n", "utf8");
    const calls: unknown[] = [];

    const result = ensureCliStartupBuild({
      rootDir: root,
      nodeExecPath: "/node",
      spawnSync: (command, args, options) => {
        calls.push({ command, args, options });
        return { status: 0 };
      },
      stdio: "pipe",
    });

    expect(result).toEqual({ built: true });
    expect(calls).toEqual([
      {
        command: "/node",
        args: ["--import", "tsx", path.join(root, "scripts", "build-all.mts"), "cliStartup"],
        options: expect.objectContaining({
          cwd: root,
          killSignal: "SIGKILL",
          stdio: "pipe",
          timeout: 10 * 60 * 1000,
        }),
      },
    ]);
  });

  it("runs the cliStartup build profile when dist entry output is missing", () => {
    const root = makeTempRoot();
    const calls: unknown[] = [];

    const result = ensureCliStartupBuild({
      rootDir: root,
      nodeExecPath: "/node",
      spawnSync: (command, args, options) => {
        calls.push({ command, args, options });
        return { status: 0 };
      },
      stdio: "pipe",
    });

    expect(result).toEqual({ built: true });
    expect(calls).toEqual([
      {
        command: "/node",
        args: ["--import", "tsx", path.join(root, "scripts", "build-all.mts"), "cliStartup"],
        options: expect.objectContaining({
          cwd: root,
          killSignal: "SIGKILL",
          stdio: "pipe",
          timeout: 10 * 60 * 1000,
        }),
      },
    ]);
  });

  it("uses the configured cliStartup build timeout", () => {
    const root = makeTempRoot();
    const calls: unknown[] = [];

    ensureCliStartupBuild({
      rootDir: root,
      env: { OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS: "1234" },
      spawnSync: (command, args, options) => {
        calls.push({ command, args, options });
        return { status: 0 };
      },
      stdio: "pipe",
    });

    expect(calls).toEqual([
      expect.objectContaining({
        options: expect.objectContaining({
          timeout: 1234,
        }),
      }),
    ]);
  });

  it("fails when the cliStartup build profile fails", () => {
    const root = makeTempRoot();

    expect(() =>
      ensureCliStartupBuild({
        rootDir: root,
        spawnSync: () => ({ status: 1 }),
        stdio: "pipe",
      }),
    ).toThrow("cliStartup build profile failed with exit code 1");
  });

  it("fails when spawning the cliStartup build profile fails", () => {
    const root = makeTempRoot();

    expect(() =>
      ensureCliStartupBuild({
        rootDir: root,
        spawnSync: () => ({ error: new Error("spawn denied") }),
        stdio: "pipe",
      }),
    ).toThrow("spawn denied");
  });
});

describe("resolveCliStartupBuildTimeoutMs", () => {
  it("parses only positive integer environment timeouts", () => {
    expect(resolveCliStartupBuildTimeoutMs({})).toBe(10 * 60 * 1000);
    expect(resolveCliStartupBuildTimeoutMs({ OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS: "" })).toBe(
      10 * 60 * 1000,
    );
    expect(resolveCliStartupBuildTimeoutMs({ OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS: "4321" })).toBe(
      4321,
    );

    for (const raw of ["nope", "10m", "1e3", "0", "-1", "9007199254740992"]) {
      expect(() =>
        resolveCliStartupBuildTimeoutMs({ OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS: raw }),
      ).toThrow(`invalid OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS: ${raw}`);
    }
  });
});
