import { beforeEach, describe, expect, it, vi } from "vitest";
import { resolveSessionAgentIds } from "../../agents/agent-scope.js";
import { resolveBootstrapContextForRun } from "../../agents/bootstrap-files.js";
import { createOpenClawCodingTools } from "../../agents/pi-tools.js";
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
import { buildAgentSystemPrompt } from "../../agents/system-prompt.js";
import { resolveCommandsSystemPromptBundle } from "./commands-system-prompt.js";
import type { HandleCommandsParams } from "./commands-types.js";

const { createOpenClawCodingToolsMock } = vi.hoisted(() => ({
  createOpenClawCodingToolsMock: vi.fn(() => []),
}));

vi.mock("../../agents/bootstrap-files.js", () => ({
  resolveBootstrapContextForRun: vi.fn(async () => ({
    bootstrapFiles: [],
    contextFiles: [],
  })),
}));

vi.mock("../../agents/sandbox.js", () => ({
  resolveSandboxRuntimeStatus: vi.fn(() => ({ sandboxed: false, mode: "off" })),
}));

vi.mock("../../agents/skills.js", () => ({
  buildWorkspaceSkillSnapshot: vi.fn(() => ({ prompt: "", skills: [], resolvedSkills: [] })),
}));

vi.mock("../../agents/skills/refresh.js", () => ({
  getSkillsSnapshotVersion: vi.fn(() => "test-snapshot"),
}));

vi.mock("../../agents/agent-scope.js", () => ({
  resolveAgentConfig: vi.fn(() => undefined),
  resolveSessionAgentIds: vi.fn(() => ({ sessionAgentId: "main" })),
}));

vi.mock("../../agents/model-selection.js", () => ({
  resolveDefaultModelForAgent: vi.fn(() => ({ provider: "openai", model: "gpt-5" })),
}));

vi.mock("../../agents/system-prompt-params.js", () => ({
  buildSystemPromptParams: vi.fn(() => ({
    runtimeInfo: { host: "unknown", os: "unknown", arch: "unknown", node: process.version },
    userTimezone: "UTC",
    userTime: "12:00 PM",
    userTimeFormat: "12h",
  })),
}));

vi.mock("../../agents/system-prompt.js", () => ({
  buildAgentSystemPrompt: vi.fn(() => "system prompt"),
}));

vi.mock("../../agents/pi-tools.js", () => ({
  createOpenClawCodingTools: createOpenClawCodingToolsMock,
}));

vi.mock("../../tts/tts.js", () => ({
  buildTtsSystemPromptHint: vi.fn(() => undefined),
}));

vi.mock("../../infra/skills-remote.js", () => ({
  getRemoteSkillEligibility: vi.fn(() => false),
}));

function makeParams(): HandleCommandsParams {
  return {
    ctx: {
      SessionKey: "agent:main:default",
      SenderId: "sender-1",
      SenderName: "Alice",
      SenderUsername: "alice_u",
      SenderE164: "+15551234567",
    },
    cfg: {},
    command: {
      surface: "telegram",
      channel: "telegram",
      ownerList: [],
      senderId: "sender-1",
      senderIsOwner: true,
      isAuthorizedSender: true,
      rawBodyNormalized: "/context",
      commandBodyNormalized: "/context",
    },
    directives: {},
    elevated: {
      enabled: true,
      allowed: true,
      failures: [],
    },
    agentId: "main",
    sessionEntry: {
      sessionId: "session-1",
      updatedAt: Date.now(),
      groupId: "group-1",
      groupChannel: "#general",
      space: "guild-1",
      spawnedBy: "agent:parent",
    },
    sessionKey: "agent:main:default",
    workspaceDir: "/tmp/workspace",
    defaultGroupActivation: () => "mention",
    resolvedVerboseLevel: "off",
    resolvedReasoningLevel: "off",
    resolvedElevatedLevel: "off",
    resolveDefaultThinkingLevel: async () => undefined,
    provider: "openai",
    model: "gpt-5.4",
    contextTokens: 0,
    isGroup: false,
  } as unknown as HandleCommandsParams;
}

describe("resolveCommandsSystemPromptBundle", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    createOpenClawCodingToolsMock.mockClear();
    createOpenClawCodingToolsMock.mockReturnValue([]);
  });

  it("opts command tool builds into gateway subagent binding", async () => {
    await resolveCommandsSystemPromptBundle(makeParams());

    expect(vi.mocked(createOpenClawCodingTools)).toHaveBeenCalledWith(
      expect.objectContaining({
        allowGatewaySubagentBinding: true,
        sessionKey: "agent:main:default",
        workspaceDir: "/tmp/workspace",
        messageProvider: "telegram",
        senderId: "sender-1",
        senderName: "Alice",
        senderUsername: "alice_u",
        senderE164: "+15551234567",
      }),
    );
  });

  it("uses the canonical target session for sandbox runtime resolution", async () => {
    const params = makeParams();
    params.ctx.SessionKey = "agent:main:telegram:slash-session";
    params.sessionKey = "agent:main:telegram:direct:target-session";

    await resolveCommandsSystemPromptBundle(params);

    expect(vi.mocked(resolveSandboxRuntimeStatus)).toHaveBeenCalledWith({
      cfg: params.cfg,
      sessionKey: "agent:main:telegram:direct:target-session",
    });
  });

  it("uses the canonical target session agent for tool creation", async () => {
    const params = makeParams();
    params.agentId = "main";
    params.sessionKey = "agent:target:telegram:direct:target-session";
    vi.mocked(resolveSessionAgentIds).mockReturnValue({
      sessionAgentId: "target",
      defaultAgentId: "main",
    });

    await resolveCommandsSystemPromptBundle(params);

    expect(vi.mocked(createOpenClawCodingTools)).toHaveBeenCalledWith(
      expect.objectContaining({
        agentId: "target",
        sessionKey: "agent:target:telegram:direct:target-session",
      }),
    );
  });

  it("prefers the target session entry for bootstrap and tool metadata", async () => {
    const params = makeParams();
    params.sessionEntry = {
      sessionId: "wrapper-session",
      updatedAt: Date.now(),
      groupId: "wrapper-group",
      groupChannel: "#wrapper",
      space: "wrapper-space",
      spawnedBy: "agent:wrapper",
    };
    params.sessionStore = {
      "agent:target:telegram:direct:target-session": {
        sessionId: "target-session",
        updatedAt: Date.now(),
        groupId: "target-group",
        groupChannel: "#target",
        space: "target-space",
        spawnedBy: "agent:target-parent",
      },
    } as HandleCommandsParams["sessionStore"];
    params.sessionKey = "agent:target:telegram:direct:target-session";

    await resolveCommandsSystemPromptBundle(params);

    expect(vi.mocked(resolveBootstrapContextForRun)).toHaveBeenCalledWith(
      expect.objectContaining({
        sessionId: "target-session",
      }),
    );
    expect(vi.mocked(createOpenClawCodingTools)).toHaveBeenCalledWith(
      expect.objectContaining({
        groupId: "target-group",
        groupChannel: "#target",
        groupSpace: "target-space",
        spawnedBy: "agent:target-parent",
      }),
    );
  });

  it("uses the resolved session key and forwards full-access block reasons", async () => {
    vi.mocked(resolveSandboxRuntimeStatus).mockImplementation(({ sessionKey }) => {
      expect(sessionKey).toBe("agent:target:default");
      return { sandboxed: true, mode: "workspace-write" } as never;
    });

    const params = makeParams();
    params.sessionKey = "agent:target:default";
    params.ctx.SessionKey = "agent:source:default";
    params.elevated = {
      enabled: true,
      allowed: false,
      failures: [],
    };

    await resolveCommandsSystemPromptBundle(params);

    expect(vi.mocked(buildAgentSystemPrompt)).toHaveBeenCalledWith(
      expect.objectContaining({
        sandboxInfo: expect.objectContaining({
          enabled: true,
          elevated: expect.objectContaining({
            fullAccessAvailable: false,
            fullAccessBlockedReason: "host-policy",
          }),
        }),
      }),
    );
  });
});
