import { spawnSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
  isDirectScriptExecution,
  resolvePnpmSpawnCall,
  resolveSpawnCall,
  shouldUseCmdExeForCommand,
} from "../../scripts/ui.js";

describe("scripts/ui windows spawn behavior", () => {
  it("wraps Windows command launchers with cmd.exe without enabling shell mode", () => {
    expect(
      shouldUseCmdExeForCommand("C:\\Users\\dev\\AppData\\Local\\pnpm\\pnpm.CMD", "win32"),
    ).toBe(true);

    expect(
      resolveSpawnCall(
        "C:\\Program Files\\nodejs\\pnpm.cmd",
        ["run", "build", "-t", "path with spaces"],
        { PATH: "C:\\bin" },
        { comSpec: "C:\\Windows\\System32\\cmd.exe", cwd: "C:\\repo\\ui", platform: "win32" },
      ),
    ).toEqual({
      command: "C:\\Windows\\System32\\cmd.exe",
      args: [
        "/d",
        "/s",
        "/c",
        '""C:\\Program Files\\nodejs\\pnpm.cmd" run build -t "path with spaces""',
      ],
      options: {
        cwd: "C:\\repo\\ui",
        stdio: "inherit",
        env: { PATH: "C:\\bin" },
        shell: false,
        windowsVerbatimArguments: true,
      },
    });
  });

  it("does not use cmd.exe for non-command launchers", () => {
    expect(shouldUseCmdExeForCommand("C:\\Program Files\\nodejs\\node.exe", "win32")).toBe(false);
    expect(shouldUseCmdExeForCommand("C:\\tools\\pnpm.com", "win32")).toBe(false);
    expect(shouldUseCmdExeForCommand("/usr/local/bin/pnpm", "linux")).toBe(false);

    expect(
      resolveSpawnCall(
        "C:\\Program Files\\nodejs\\pnpm.exe",
        ["run", "build"],
        { PATH: "C:\\bin" },
        { cwd: "C:\\repo\\ui", platform: "win32" },
      ),
    ).toEqual({
      command: "C:\\Program Files\\nodejs\\pnpm.exe",
      args: ["run", "build"],
      options: {
        cwd: "C:\\repo\\ui",
        stdio: "inherit",
        env: { PATH: "C:\\bin" },
        shell: false,
      },
    });
  });

  it("rejects unsafe cmd.exe arguments before launch", () => {
    expect(() =>
      resolveSpawnCall("C:\\tools\\pnpm.cmd", ["run", "build", "evil&calc"], undefined, {
        platform: "win32",
      }),
    ).toThrow(/unsafe windows cmd\.exe argument/i);
    expect(() =>
      resolveSpawnCall("C:\\tools\\pnpm.cmd", ["run", "build", "%PATH%"], undefined, {
        platform: "win32",
      }),
    ).toThrow(/unsafe windows cmd\.exe argument/i);
  });

  it("routes Windows Corepack pnpm entrypoints through node", () => {
    const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-pnpm-runner-"));
    const npmExecPath = path.join(tempDir, "pnpm.mjs");
    fs.writeFileSync(npmExecPath, "console.log('pnpm');\n");

    try {
      expect(
        resolvePnpmSpawnCall(
          ["run", "build"],
          {
            npm_execpath: npmExecPath,
            ComSpec: "C:\\Windows\\System32\\cmd.exe",
          },
          {
            cwd: "C:\\repo\\ui",
            nodeExecPath: "C:\\Program Files\\nodejs\\node.exe",
            platform: "win32",
          },
        ),
      ).toEqual({
        command: "C:\\Program Files\\nodejs\\node.exe",
        args: [npmExecPath, "run", "build"],
        options: {
          cwd: "C:\\repo\\ui",
          stdio: "inherit",
          env: {
            npm_execpath: npmExecPath,
            ComSpec: "C:\\Windows\\System32\\cmd.exe",
          },
          shell: false,
          windowsVerbatimArguments: undefined,
        },
      });
    } finally {
      fs.rmSync(tempDir, { force: true, recursive: true });
    }
  });

  it("keeps non-Windows launches direct even with shell metacharacters", () => {
    expect(
      resolveSpawnCall(
        "/usr/local/bin/pnpm",
        ["run", "build", "contains&metacharacters"],
        { PATH: "/bin" },
        { cwd: "/repo/ui", platform: "linux" },
      ),
    ).toEqual({
      command: "/usr/local/bin/pnpm",
      args: ["run", "build", "contains&metacharacters"],
      options: {
        cwd: "/repo/ui",
        stdio: "inherit",
        env: { PATH: "/bin" },
        shell: false,
      },
    });
  });

  it("detects direct execution through a junctioned script path", () => {
    const realScriptPath = path.resolve("repo/openclaw/scripts/ui.js");
    const junctionScriptPath = path.resolve("linked/openclaw/scripts/ui.js");
    const realpath = (entry: string) => (entry === junctionScriptPath ? realScriptPath : entry);

    expect(isDirectScriptExecution(junctionScriptPath, realScriptPath, realpath)).toBe(true);
  });

  it("honors build-all no-pnpm mode before requiring a pnpm runner", () => {
    const result = spawnSync(process.execPath, ["scripts/ui.js", "build", "--help"], {
      cwd: path.resolve("."),
      encoding: "utf8",
      env: {
        ...process.env,
        OPENCLAW_BUILD_ALL_NO_PNPM: "1",
        PATH: "",
      },
    });

    const output = `${result.stdout}${result.stderr}`;
    expect(result.status).toBe(0);
    expect(output).not.toContain("Missing UI runner");
    expect(output).toContain("vite");
  });
});
