import { describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../runtime-api.js";

const resolveFeishuAccountMock = vi.hoisted(() => vi.fn());
const createFeishuClientMock = vi.hoisted(() => vi.fn());

vi.mock("./accounts.js", () => ({
  resolveFeishuAccount: resolveFeishuAccountMock,
}));

vi.mock("./client.js", () => ({
  createFeishuClient: createFeishuClientMock,
}));

import {
  listFeishuDirectoryGroups,
  listFeishuDirectoryGroupsLive,
  listFeishuDirectoryPeers,
  listFeishuDirectoryPeersLive,
} from "./directory.js";

describe("feishu directory (config-backed)", () => {
  const cfg = {} as ClawdbotConfig;

  function makeStaticAccount() {
    return {
      configured: false,
      config: {
        allowFrom: ["user:alice", "user:bob"],
        dms: {
          "user:carla": {},
        },
        groups: {
          "chat-1": {},
        },
        groupAllowFrom: ["chat-2"],
      },
    };
  }

  resolveFeishuAccountMock.mockImplementation(() => makeStaticAccount());

  it("merges allowFrom + dms into peer entries", async () => {
    const peers = await listFeishuDirectoryPeers({ cfg, query: "a" });
    expect(peers).toEqual([
      { kind: "user", id: "alice" },
      { kind: "user", id: "carla" },
    ]);
  });

  it("normalizes spaced provider-prefixed peer entries", async () => {
    resolveFeishuAccountMock.mockReturnValueOnce({
      configured: false,
      config: {
        allowFrom: [" feishu:user:ou_alice "],
        dms: {
          " lark:dm:ou_carla ": {},
        },
        groups: {},
        groupAllowFrom: [],
      },
    });

    const peers = await listFeishuDirectoryPeers({ cfg });
    expect(peers).toEqual([
      { kind: "user", id: "ou_alice" },
      { kind: "user", id: "ou_carla" },
    ]);
  });

  it("merges groups map + groupAllowFrom into group entries", async () => {
    const groups = await listFeishuDirectoryGroups({ cfg });
    expect(groups).toEqual([
      { kind: "group", id: "chat-1" },
      { kind: "group", id: "chat-2" },
    ]);
  });

  it("falls back to static peers on live lookup failure by default", async () => {
    resolveFeishuAccountMock.mockReturnValueOnce({
      ...makeStaticAccount(),
      configured: true,
    });
    createFeishuClientMock.mockReturnValueOnce({
      contact: {
        user: {
          list: vi.fn(async () => {
            throw new Error("token expired");
          }),
        },
      },
    });

    const peers = await listFeishuDirectoryPeersLive({ cfg, query: "a" });
    expect(peers).toEqual([
      { kind: "user", id: "alice" },
      { kind: "user", id: "carla" },
    ]);
  });

  it("surfaces live peer lookup failures when fallback is disabled", async () => {
    resolveFeishuAccountMock.mockReturnValueOnce({
      ...makeStaticAccount(),
      configured: true,
    });
    createFeishuClientMock.mockReturnValueOnce({
      contact: {
        user: {
          list: vi.fn(async () => {
            throw new Error("token expired");
          }),
        },
      },
    });

    await expect(listFeishuDirectoryPeersLive({ cfg, fallbackToStatic: false })).rejects.toThrow(
      "token expired",
    );
  });

  it("surfaces live group lookup failures when fallback is disabled", async () => {
    resolveFeishuAccountMock.mockReturnValueOnce({
      ...makeStaticAccount(),
      configured: true,
    });
    createFeishuClientMock.mockReturnValueOnce({
      im: {
        chat: {
          list: vi.fn(async () => ({ code: 999, msg: "forbidden" })),
        },
      },
    });

    await expect(listFeishuDirectoryGroupsLive({ cfg, fallbackToStatic: false })).rejects.toThrow(
      "forbidden",
    );
  });
});
