import { describe, expect, it } from "vitest";
import { readFeishuContext } from "../../src/feishu/read.js";
import type { FeishuRuntime } from "../../src/feishu/client.js";

describe("readFeishuContext", () => {
  it("compacts Feishu message.get replies with parent_id, thread_id, and body.content", async () => {
    const rt = {
      client: {
        im: {
          v1: {
            message: {
              get: async () => ({
                data: {
                  items: [
                    {
                      message_id: "m-child",
                      root_id: "m-root",
                      parent_id: "m-parent",
                      thread_id: "thread-1",
                      chat_id: "chat-1",
                      msg_type: "text",
                      body: { content: JSON.stringify({ text: "parent text" }) },
                    },
                  ],
                },
              }),
              list: async () => ({ data: { items: [] } }),
            },
          },
        },
      },
      logger: { warn() {}, info() {}, error() {}, debug() {} },
    } as unknown as FeishuRuntime;

    const raw = await readFeishuContext(
      { chat_id: "chat-1", anchor: "reply", id: "m-child" },
      { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" },
      rt,
    );

    expect(JSON.parse(raw)).toMatchObject({
      message_id: "m-child",
      content: JSON.stringify({ text: "parent text" }),
      parent_id: "m-parent",
      parent_message_id: "m-parent",
      root_id: "m-root",
      thread_id: "thread-1",
    });
  });

  it("refuses a single-message result from another chat", async () => {
    const rt = runtimeWithMessages({
      get: [{ message_id: "m-other", chat_id: "chat-other", body: { content: "secret" } }],
      list: [],
    });

    const raw = await readFeishuContext(
      { chat_id: "chat-1", anchor: "trigger_message", id: "m-other" },
      { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" },
      rt,
    );

    expect(JSON.parse(raw)).toMatchObject({ error: expect.stringContaining("bound chat") });
    expect(raw).not.toContain("secret");
  });

  it("refuses an entire thread result when any item lacks the bound chat id", async () => {
    const rt = runtimeWithMessages({
      get: [],
      list: [
        { message_id: "m-allowed", chat_id: "chat-1", body: { content: "allowed" } },
        { message_id: "m-unscoped", body: { content: "must-not-leak" } },
      ],
    });

    const raw = await readFeishuContext(
      { chat_id: "chat-1", anchor: "thread", id: "thread-1" },
      { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" },
      rt,
    );

    expect(JSON.parse(raw)).toMatchObject({ error: expect.stringContaining("bound chat") });
    expect(raw).not.toContain("must-not-leak");
  });
});

function runtimeWithMessages(input: {
  get: Array<Record<string, unknown>>;
  list: Array<Record<string, unknown>>;
}): FeishuRuntime {
  return {
    client: {
      im: {
        v1: {
          message: {
            get: async () => ({ data: { items: input.get } }),
            list: async () => ({ data: { items: input.list } }),
          },
        },
      },
    },
    logger: { warn() {}, info() {}, error() {}, debug() {} },
  } as unknown as FeishuRuntime;
}
