import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import {
  applyAgentBindings,
  applyAgentConfig,
  buildAgentSummaries,
  pruneAgentConfig,
  removeAgentBindings,
} from "./agents.js";

describe("agents helpers", () => {
  it("buildAgentSummaries includes default + configured agents", () => {
    const cfg: OpenClawConfig = {
      agents: {
        defaults: {
          workspace: "/main-ws",
          model: { primary: "anthropic/claude" },
        },
        list: [
          { id: "main" },
          {
            id: "work",
            default: true,
            name: "Work",
            workspace: "/work-ws",
            agentDir: "/state/agents/work/agent",
            model: "openai/gpt-4.1",
          },
        ],
      },
      bindings: [
        {
          agentId: "work",
          match: { channel: "whatsapp", accountId: "biz" },
        },
        { agentId: "main", match: { channel: "telegram" } },
      ],
    };

    const summaries = buildAgentSummaries(cfg);
    const main = summaries.find((summary) => summary.id === "main");
    const work = summaries.find((summary) => summary.id === "work");

    expect(main).toBeTruthy();
    expect(main?.workspace).toBe(
      path.join(resolveStateDir(process.env, os.homedir), "workspace-main"),
    );
    expect(main?.bindings).toBe(1);
    expect(main?.model).toBe("anthropic/claude");
    expect(main?.agentDir.endsWith(path.join("agents", "main", "agent"))).toBe(true);

    expect(work).toBeTruthy();
    expect(work?.name).toBe("Work");
    expect(work?.workspace).toBe(path.resolve("/work-ws"));
    expect(work?.agentDir).toBe(path.resolve("/state/agents/work/agent"));
    expect(work?.bindings).toBe(1);
    expect(work?.isDefault).toBe(true);
  });

  it("applyAgentConfig merges updates", () => {
    const cfg: OpenClawConfig = {
      agents: {
        list: [{ id: "work", workspace: "/old-ws", model: "anthropic/claude" }],
      },
    };

    const next = applyAgentConfig(cfg, {
      agentId: "work",
      name: "Work",
      workspace: "/new-ws",
      agentDir: "/state/work/agent",
    });

    const work = next.agents?.list?.find((agent) => agent.id === "work");
    expect(work?.name).toBe("Work");
    expect(work?.workspace).toBe("/new-ws");
    expect(work?.agentDir).toBe("/state/work/agent");
    expect(work?.model).toBe("anthropic/claude");
  });

  it("applyAgentBindings skips duplicates and reports conflicts", () => {
    const cfg: OpenClawConfig = {
      bindings: [
        {
          agentId: "main",
          match: { channel: "whatsapp", accountId: "default" },
        },
      ],
    };

    const result = applyAgentBindings(cfg, [
      {
        agentId: "main",
        match: { channel: "whatsapp", accountId: "default" },
      },
      {
        agentId: "work",
        match: { channel: "whatsapp", accountId: "default" },
      },
      {
        agentId: "work",
        match: { channel: "telegram" },
      },
    ]);

    expect(result.added).toHaveLength(1);
    expect(result.skipped).toHaveLength(1);
    expect(result.conflicts).toHaveLength(1);
    expect(result.config.bindings).toHaveLength(2);
  });

  it("applyAgentBindings upgrades channel-only binding to account-specific binding for same agent", () => {
    const cfg: OpenClawConfig = {
      bindings: [
        {
          agentId: "main",
          match: { channel: "telegram" },
        },
      ],
    };

    const result = applyAgentBindings(cfg, [
      {
        agentId: "main",
        match: { channel: "telegram", accountId: "work" },
      },
    ]);

    expect(result.added).toHaveLength(0);
    expect(result.updated).toHaveLength(1);
    expect(result.conflicts).toHaveLength(0);
    expect(result.config.bindings).toEqual([
      {
        agentId: "main",
        match: { channel: "telegram", accountId: "work" },
      },
    ]);
  });

  it("applyAgentBindings treats role-based bindings as distinct routes", () => {
    const cfg: OpenClawConfig = {
      bindings: [
        {
          agentId: "main",
          match: {
            channel: "discord",
            accountId: "guild-a",
            guildId: "123",
            roles: ["111", "222"],
          },
        },
      ],
    };

    const result = applyAgentBindings(cfg, [
      {
        agentId: "work",
        match: {
          channel: "discord",
          accountId: "guild-a",
          guildId: "123",
        },
      },
    ]);

    expect(result.added).toHaveLength(1);
    expect(result.conflicts).toHaveLength(0);
    expect(result.config.bindings).toHaveLength(2);
  });

  it("removeAgentBindings does not remove role-based bindings when removing channel-level routes", () => {
    const cfg: OpenClawConfig = {
      bindings: [
        {
          agentId: "main",
          match: {
            channel: "discord",
            accountId: "guild-a",
            guildId: "123",
            roles: ["111", "222"],
          },
        },
        {
          agentId: "main",
          match: {
            channel: "discord",
            accountId: "guild-a",
            guildId: "123",
          },
        },
      ],
    };

    const result = removeAgentBindings(cfg, [
      {
        agentId: "main",
        match: {
          channel: "discord",
          accountId: "guild-a",
          guildId: "123",
        },
      },
    ]);

    expect(result.removed).toHaveLength(1);
    expect(result.conflicts).toHaveLength(0);
    expect(result.config.bindings).toEqual([
      {
        agentId: "main",
        match: {
          channel: "discord",
          accountId: "guild-a",
          guildId: "123",
          roles: ["111", "222"],
        },
      },
    ]);
  });

  it("pruneAgentConfig removes agent, bindings, and allowlist entries", () => {
    const cfg: OpenClawConfig = {
      agents: {
        list: [
          { id: "work", default: true, workspace: "/work-ws" },
          { id: "home", workspace: "/home-ws" },
        ],
      },
      bindings: [
        { agentId: "work", match: { channel: "whatsapp" } },
        { agentId: "home", match: { channel: "telegram" } },
      ],
      tools: {
        agentToAgent: { enabled: true, allow: ["work", "home"] },
      },
    };

    const result = pruneAgentConfig(cfg, "work");
    expect(result.config.agents?.list?.some((agent) => agent.id === "work")).toBe(false);
    expect(result.config.agents?.list?.some((agent) => agent.id === "home")).toBe(true);
    expect(result.config.bindings).toHaveLength(1);
    expect(result.config.bindings?.[0]?.agentId).toBe("home");
    expect(result.config.tools?.agentToAgent?.allow).toEqual(["home"]);
    expect(result.removedBindings).toBe(1);
    expect(result.removedAllow).toBe(1);
  });
});
