import assert from "node:assert/strict";
import test, { after, before } from "node:test";

import {
  DEFAULT_UNIDESK_MAIN_SERVER_ENV,
  createHwlabAgentRunDispatchAssembly,
  dispatchHwlabAgentRun
} from "./agentrun-dispatch.mjs";

const commitId = "8da27c83fa56d87e78e488a819afe898cf97c62b";
let previousProviderId;

before(() => {
  previousProviderId = process.env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID;
  process.env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID = "G14";
});

after(() => {
  if (previousProviderId === undefined) delete process.env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID;
  else process.env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID = previousProviderId;
});

test("HWLAB AgentRun assembly grants GitHub and UniDesk SSH through toolCredentials", () => {
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_001",
    prompt: "检查 HWLAB 工作区并给出状态。",
    commitId,
    conversationId: "conv_001",
    sessionId: "sess_001",
    threadId: "thread_001",
    unideskMainServerIp: "https://unidesk.example.test"
  });

  assert.equal(assembly.runPayload.tenantId, "hwlab");
  assert.equal(assembly.runPayload.projectId, "pikasTech/HWLAB");
  assert.equal(assembly.runPayload.providerId, "G14");
  assert.equal(assembly.runPayload.backendProfile, "deepseek");
  assert.equal(assembly.runPayload.workspaceRef.branch, "v0.2");
  assert.equal(assembly.runPayload.resourceBundleRef.kind, "gitbundle");
  assert.equal(assembly.runPayload.resourceBundleRef.commitId, commitId);
  assert.deepEqual(assembly.runPayload.resourceBundleRef.bundles, [
    { name: "hwlab-tools", subpath: "tools", target_path: "tools" },
    { name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" }
  ]);
  assert.deepEqual(assembly.runPayload.resourceBundleRef.promptRefs, [
    { name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }
  ]);
  assert.equal(Object.hasOwn(assembly.runPayload.resourceBundleRef, "toolAliases"), false);
  assert.equal(Object.hasOwn(assembly.runPayload.resourceBundleRef, "skillRefs"), false);
  assert.equal(Object.hasOwn(assembly.runPayload.resourceBundleRef, "workspaceFiles"), false);
  assert.deepEqual(assembly.boundaries.promptRefs, ["hwlab-v02-runtime"]);
  assert.deepEqual(assembly.boundaries.bundles, ["hwlab-tools", "hwlab-agent-skills"]);
  assert.equal(assembly.commandPayload.payload.traceId, "trc_hwlab_agentrun_001");
  assert.equal(assembly.commandPayload.payload.threadId, "thread_001");

  const secretScope = assembly.runPayload.executionPolicy.secretScope;
  assert.equal(secretScope.allowCredentialEcho, false);
  assert.equal(secretScope.providerCredentials[0].profile, "deepseek");
  assert.equal(secretScope.providerCredentials[0].secretRef.name, "agentrun-v01-provider-deepseek");
  const tools = secretScope.toolCredentials;
  assert.equal(tools.some((item) => item.tool === "github" && item.projection.envName === "GH_TOKEN"), true);
  assert.equal(tools.some((item) => item.tool === "unidesk-ssh" && item.projection.envName === "UNIDESK_SSH_CLIENT_TOKEN"), true);

  const transientEnv = assembly.runnerJobPayload.transientEnv;
  assert.equal(transientEnv.find((item) => item.name === DEFAULT_UNIDESK_MAIN_SERVER_ENV)?.value, "https://unidesk.example.test");
  assert.equal(transientEnv.some((item) => item.name === "UNIDESK_SSH_CLIENT_TOKEN"), false);
  assert.equal(assembly.boundaries.unideskSshCredentialSource, "toolCredentials");
  assert.equal(assembly.boundaries.githubCredentialSource, "toolCredentials");
});

test("HWLAB AgentRun assembly rejects reusable credentials in transientEnv", () => {
  assert.throws(
    () => createHwlabAgentRunDispatchAssembly({
      traceId: "trc_hwlab_agentrun_bad_env",
      prompt: "bad env",
      commitId,
      unideskMainServerIp: "https://unidesk.example.test",
      transientEnv: [{ name: "UNIDESK_SSH_CLIENT_TOKEN", value: "test-unidesk-ssh-client-token" }]
    }),
    /must use AgentRun tool\/provider credential assembly/u
  );
});

test("HWLAB AgentRun assembly allows explicit gitbundle items", () => {
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_bundle_override",
    prompt: "bundle override",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test",
    bundles: [{ name: "status-tools", subpath: "tools/status", target_path: "tools/status" }]
  });
  assert.deepEqual(assembly.runPayload.resourceBundleRef.bundles, [{ name: "status-tools", subpath: "tools/status", target_path: "tools/status" }]);
});

test("HWLAB AgentRun assembly allows explicit resource prompt refs", () => {
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_prompt_override",
    prompt: "prompt override",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test",
    promptRefs: [{ name: "custom-prompt", path: "internal/agent/prompts/custom.md", inject: "thread-start", required: true }]
  });
  assert.deepEqual(assembly.runPayload.resourceBundleRef.promptRefs, [{ name: "custom-prompt", path: "internal/agent/prompts/custom.md", inject: "thread-start", required: true }]);
  assert.equal(Object.hasOwn(assembly.runPayload.resourceBundleRef, "skillRefs"), false);
});

test("HWLAB AgentRun assembly does not duplicate explicit UniDesk main-server env", () => {
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_explicit_unidesk",
    prompt: "explicit unidesk main server",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test",
    transientEnv: [{ name: DEFAULT_UNIDESK_MAIN_SERVER_ENV, value: "https://explicit.example.test", sensitive: false }]
  });
  const entries = assembly.runnerJobPayload.transientEnv.filter((item) => item.name === DEFAULT_UNIDESK_MAIN_SERVER_ENV);
  assert.equal(entries.length, 1);
  assert.equal(entries[0].value, "https://explicit.example.test");
});

test("HWLAB AgentRun assembly requires full commit and main server when UniDesk SSH is enabled", () => {
  assert.throws(
    () => createHwlabAgentRunDispatchAssembly({ traceId: "trc_bad_commit", prompt: "bad", commitId: "HEAD", unideskMainServerIp: "https://unidesk.example.test" }),
    /full 40-character/u
  );
  assert.throws(
    () => createHwlabAgentRunDispatchAssembly({ traceId: "trc_no_unidesk", prompt: "bad", commitId }),
    /unideskMainServerIp is required/u
  );
});

test("dispatchHwlabAgentRun posts run command and runner job with commandId", async () => {
  const requests = [];
  const fetchImpl = async (url, init) => {
    const request = { url, method: init.method, body: JSON.parse(init.body) };
    requests.push(request);
    if (url.endsWith("/api/v1/runs")) return jsonResponse({ id: "run_hwlab_001" });
    if (url.endsWith("/api/v1/runs/run_hwlab_001/commands")) return jsonResponse({ id: "cmd_hwlab_001" });
    if (url.endsWith("/api/v1/runs/run_hwlab_001/runner-jobs")) return jsonResponse({ id: "job_hwlab_001", jobName: "agentrun-v01-runner-selftest" });
    return jsonResponse({ error: "unexpected path" }, { status: 404 });
  };
  const result = await dispatchHwlabAgentRun({
    fetchImpl,
    traceId: "trc_hwlab_dispatch_001",
    prompt: "dispatch smoke",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test"
  });

  assert.equal(result.run.id, "run_hwlab_001");
  assert.equal(result.command.id, "cmd_hwlab_001");
  assert.equal(result.runnerJob.jobName, "agentrun-v01-runner-selftest");
  assert.deepEqual(requests.map((item) => new URL(item.url).pathname), [
    "/api/v1/runs",
    "/api/v1/runs/run_hwlab_001/commands",
    "/api/v1/runs/run_hwlab_001/runner-jobs"
  ]);
  assert.equal(requests[2].body.commandId, "cmd_hwlab_001");
  assert.equal(requests[2].body.transientEnv.some((item) => item.name === "UNIDESK_SSH_CLIENT_TOKEN"), false);
});

test("dispatchHwlabAgentRun unwraps AgentRun manager response envelopes", async () => {
  const fetchImpl = async (url, init) => {
    if (url.endsWith("/api/v1/runs")) return jsonResponse({ ok: true, data: { id: "run_hwlab_envelope" } });
    if (url.endsWith("/api/v1/runs/run_hwlab_envelope/commands")) return jsonResponse({ ok: true, data: { id: "cmd_hwlab_envelope" } });
    if (url.endsWith("/api/v1/runs/run_hwlab_envelope/runner-jobs")) return jsonResponse({ ok: true, data: { id: "job_hwlab_envelope", jobName: "agentrun-v01-runner-envelope" } });
    return jsonResponse({ ok: false, failureKind: "unexpected-path" }, { status: 404 });
  };

  const result = await dispatchHwlabAgentRun({
    fetchImpl,
    traceId: "trc_hwlab_dispatch_envelope",
    prompt: "dispatch envelope smoke",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test"
  });

  assert.equal(result.run.id, "run_hwlab_envelope");
  assert.equal(result.command.id, "cmd_hwlab_envelope");
  assert.equal(result.runnerJob.jobName, "agentrun-v01-runner-envelope");
});

function jsonResponse(body, { status = 200 } = {}) {
  return {
    ok: status >= 200 && status < 300,
    status,
    text: async () => JSON.stringify(body)
  };
}

test("HWLAB AgentRun assembly fixes tenantId/projectId/providerId/backendProfile regardless of options (#792)", () => {
  // Even if caller passes contradicting projectId / providerId / backendProfile,
  // the assembly must use HWLAB-fixed values to keep AgentRun tenant/policy boundaries
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_concept_boundary",
    prompt: "concept boundary",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test",
    projectId: "pikasTech/SomeOtherProject",
    providerId: "OtherProvider",
    backendProfile: "codex"
  });
  // runPayload is the AgentRun tenant/policy payload
  assert.equal(assembly.runPayload.tenantId, "hwlab");
  assert.equal(assembly.runPayload.projectId, "pikasTech/HWLAB");
  assert.equal(assembly.runPayload.providerId, "G14");
  assert.equal(assembly.runPayload.backendProfile, "deepseek");
  // workspaceRef is fixed to v0.2 lane
  assert.equal(assembly.runPayload.workspaceRef.kind, "git-worktree");
  assert.equal(assembly.runPayload.workspaceRef.repo, "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
  assert.equal(assembly.runPayload.workspaceRef.branch, "v0.2");
  // resourceBundleRef.commitId must be the 40-char sha
  assert.equal(assembly.runPayload.resourceBundleRef.commitId, commitId);
  assert.equal(assembly.runPayload.resourceBundleRef.commitId.length, 40);
});

test("HWLAB AgentRun assembly command payload does not leak business projectId into AgentRun command projectId (#792)", () => {
  // Business-side projectId belongs in workspaceRef only, not in AgentRun command.projectId.
  const assembly = createHwlabAgentRunDispatchAssembly({
    traceId: "trc_hwlab_agentrun_command_project",
    prompt: "do not leak business project",
    commitId,
    unideskMainServerIp: "https://unidesk.example.test"
  });
  // commandPayload.projectId must equal runPayload.projectId (HWLAB-fixed), NOT any business project
  assert.equal(assembly.commandPayload.payload.projectId, assembly.runPayload.projectId);
  assert.equal(assembly.commandPayload.payload.projectId, "pikasTech/HWLAB");
  assert.equal(assembly.runPayload.projectId, "pikasTech/HWLAB");
});
