import { describe, expect, it, vi } from "vitest";
import {
  _buildMarkdownPostRows,
  buildOutboundPayload,
  patchTextMessage,
  sendTextMessage,
  type FeishuRuntime,
} from "../../src/feishu/client.js";

describe("Feishu markdown outbound payloads", () => {
  it("uses text messages for plain text", () => {
    const payload = buildOutboundPayload("hello from cph");

    expect(payload.msgType).toBe("text");
    expect(JSON.parse(payload.content)).toEqual({ text: "hello from cph" });
  });

  it("keeps fenced code blocks in separate post rows", () => {
    const content = "Before\n```ts\nconst x = 1;\n```\nAfter";

    expect(_buildMarkdownPostRows(content)).toEqual([
      [{ tag: "md", text: "Before\n" }],
      [{ tag: "md", text: "```ts\nconst x = 1;\n```" }],
      [{ tag: "md", text: "\nAfter" }],
    ]);
  });

  it("forces markdown tables to plain text", () => {
    const content = "| A | B |\n|---|---|\n| 1 | 2 |";
    const payload = buildOutboundPayload(content);

    expect(payload.msgType).toBe("text");
    expect(JSON.parse(payload.content)).toEqual({ text: content });
  });

  it("uses post messages for markdown headers, bold text, and lists", () => {
    const content = "# Title\n\nThis is **important**.\n\n- first\n- second";
    const payload = buildOutboundPayload(content);

    expect(payload.msgType).toBe("post");
    expect(JSON.parse(payload.content)).toEqual({
      zh_cn: {
        content: [[{ tag: "md", text: content }]],
      },
    });
  });

  it.each([
    ["header", "# Heading"],
    ["list", "- item"],
    ["code block", "```ts\nconst x = 1;\n```"],
    ["inline code", "Use `code` here"],
    ["bold", "**bold**"],
    ["italic", "_italic_"],
    ["strikethrough", "~~removed~~"],
    ["link", "[docs](https://example.com)"],
    ["blockquote", "> quoted"],
  ])("detects markdown %s syntax", (_name, content) => {
    expect(buildOutboundPayload(content).msgType).toBe("post");
  });

  it("falls back from rejected post payloads to stripped plain text", async () => {
    const messageCreate = vi
      .fn()
      .mockRejectedValueOnce(new Error("content format of the post type is incorrect"))
      .mockResolvedValueOnce({ data: { message_id: "message-1" } });
    const rt = mockRuntime({ messageCreate });

    await expect(sendTextMessage(rt, "chat-1", "# Title\n\n**bold** and `code`")).resolves.toBe("message-1");

    expect(messageCreate).toHaveBeenCalledTimes(2);
    expect(messageCreate).toHaveBeenNthCalledWith(1, {
      params: { receive_id_type: "chat_id" },
      data: {
        receive_id: "chat-1",
        msg_type: "post",
        content: JSON.stringify({
          zh_cn: {
            content: [[{ tag: "md", text: "# Title\n\n**bold** and `code`" }]],
          },
        }),
      },
    });
    expect(messageCreate).toHaveBeenNthCalledWith(2, {
      params: { receive_id_type: "chat_id" },
      data: {
        receive_id: "chat-1",
        msg_type: "text",
        content: JSON.stringify({ text: "Title\n\nbold and code" }),
      },
    });
  });

  it("patches markdown post rows as separate interactive card elements", async () => {
    const patch = vi.fn(async () => ({}));
    const rt = mockRuntime({ patch });

    await patchTextMessage(rt, "message-1", "Before\n```ts\nconst x = 1;\n```\nAfter");

    expect(patch).toHaveBeenCalledWith({
      path: { message_id: "message-1" },
      params: { msg_type: "interactive" },
      data: {
        content: JSON.stringify({
          elements: [
            { tag: "div", text: { tag: "lark_md", content: "Before\n" } },
            { tag: "div", text: { tag: "lark_md", content: "```ts\nconst x = 1;\n```" } },
            { tag: "div", text: { tag: "lark_md", content: "\nAfter" } },
          ],
        }),
      },
    });
  });
});

function mockRuntime(options: {
  readonly messageCreate?: (payload: unknown) => Promise<unknown>;
  readonly patch?: (payload: unknown) => Promise<unknown>;
}): FeishuRuntime {
  return {
    client: {
      im: {
        v1: {
          message: {
            create: options.messageCreate ?? vi.fn(async () => ({ data: { message_id: "message-1" } })),
            patch: options.patch ?? vi.fn(async () => ({})),
          },
        },
      },
    } as unknown as FeishuRuntime["client"],
    logger: {
      info() {},
      warn() {},
      error() {},
      debug() {},
      fatal() {},
      child() { return this; },
      level: "silent",
    } as unknown as FeishuRuntime["logger"],
  };
}
