import type { OpenClawConfig } from "openclaw/plugin-sdk/matrix";
import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
  sendMessageMatrix: vi.fn(),
  sendPollMatrix: vi.fn(),
}));

vi.mock("./matrix/send.js", () => ({
  sendMessageMatrix: mocks.sendMessageMatrix,
  sendPollMatrix: mocks.sendPollMatrix,
}));

vi.mock("./runtime.js", () => ({
  getMatrixRuntime: () => ({
    channel: {
      text: {
        chunkMarkdownText: (text: string) => [text],
      },
    },
  }),
}));

import { matrixOutbound } from "./outbound.js";

describe("matrixOutbound cfg threading", () => {
  beforeEach(() => {
    mocks.sendMessageMatrix.mockReset();
    mocks.sendPollMatrix.mockReset();
    mocks.sendMessageMatrix.mockResolvedValue({ messageId: "evt-1", roomId: "!room:example" });
    mocks.sendPollMatrix.mockResolvedValue({ eventId: "$poll", roomId: "!room:example" });
  });

  it("passes resolved cfg to sendMessageMatrix for text sends", async () => {
    const cfg = {
      channels: {
        matrix: {
          accessToken: "resolved-token",
        },
      },
    } as OpenClawConfig;

    await matrixOutbound.sendText!({
      cfg,
      to: "room:!room:example",
      text: "hello",
      accountId: "default",
      threadId: "$thread",
      replyToId: "$reply",
    });

    expect(mocks.sendMessageMatrix).toHaveBeenCalledWith(
      "room:!room:example",
      "hello",
      expect.objectContaining({
        cfg,
        accountId: "default",
        threadId: "$thread",
        replyToId: "$reply",
      }),
    );
  });

  it("passes resolved cfg to sendMessageMatrix for media sends", async () => {
    const cfg = {
      channels: {
        matrix: {
          accessToken: "resolved-token",
        },
      },
    } as OpenClawConfig;

    await matrixOutbound.sendMedia!({
      cfg,
      to: "room:!room:example",
      text: "caption",
      mediaUrl: "file:///tmp/cat.png",
      accountId: "default",
    });

    expect(mocks.sendMessageMatrix).toHaveBeenCalledWith(
      "room:!room:example",
      "caption",
      expect.objectContaining({
        cfg,
        mediaUrl: "file:///tmp/cat.png",
      }),
    );
  });

  it("passes resolved cfg through injected deps.sendMatrix", async () => {
    const cfg = {
      channels: {
        matrix: {
          accessToken: "resolved-token",
        },
      },
    } as OpenClawConfig;
    const sendMatrix = vi.fn(async () => ({
      messageId: "evt-injected",
      roomId: "!room:example",
    }));

    await matrixOutbound.sendText!({
      cfg,
      to: "room:!room:example",
      text: "hello via deps",
      deps: { sendMatrix },
      accountId: "default",
      threadId: "$thread",
      replyToId: "$reply",
    });

    expect(sendMatrix).toHaveBeenCalledWith(
      "room:!room:example",
      "hello via deps",
      expect.objectContaining({
        cfg,
        accountId: "default",
        threadId: "$thread",
        replyToId: "$reply",
      }),
    );
  });

  it("passes resolved cfg to sendPollMatrix", async () => {
    const cfg = {
      channels: {
        matrix: {
          accessToken: "resolved-token",
        },
      },
    } as OpenClawConfig;

    await matrixOutbound.sendPoll!({
      cfg,
      to: "room:!room:example",
      poll: {
        question: "Snack?",
        options: ["Pizza", "Sushi"],
      },
      accountId: "default",
      threadId: "$thread",
    });

    expect(mocks.sendPollMatrix).toHaveBeenCalledWith(
      "room:!room:example",
      expect.objectContaining({
        question: "Snack?",
        options: ["Pizza", "Sushi"],
      }),
      expect.objectContaining({
        cfg,
        accountId: "default",
        threadId: "$thread",
      }),
    );
  });
});
