import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { TelegramExecApprovalHandler } from "./exec-approvals-handler.js";

const baseRequest = {
  id: "9f1c7d5d-b1fb-46ef-ac45-662723b65bb7",
  request: {
    command: "npm view diver name version description",
    agentId: "main",
    sessionKey: "agent:main:telegram:group:-1003841603622:topic:928",
    turnSourceChannel: "telegram",
    turnSourceTo: "-1003841603622",
    turnSourceThreadId: "928",
    turnSourceAccountId: "default",
  },
  createdAtMs: 1000,
  expiresAtMs: 61_000,
};

function createHandler(cfg: OpenClawConfig) {
  const sendTyping = vi.fn().mockResolvedValue({ ok: true });
  const sendMessage = vi
    .fn()
    .mockResolvedValueOnce({ messageId: "m1", chatId: "-1003841603622" })
    .mockResolvedValue({ messageId: "m2", chatId: "8460800771" });
  const editReplyMarkup = vi.fn().mockResolvedValue({ ok: true });
  const handler = new TelegramExecApprovalHandler(
    {
      token: "tg-token",
      accountId: "default",
      cfg,
    },
    {
      nowMs: () => 1000,
      sendTyping,
      sendMessage,
      editReplyMarkup,
    },
  );
  return { handler, sendTyping, sendMessage, editReplyMarkup };
}

describe("TelegramExecApprovalHandler", () => {
  it("sends approval prompts to the originating telegram topic when target=channel", async () => {
    const cfg = {
      channels: {
        telegram: {
          execApprovals: {
            enabled: true,
            approvers: ["8460800771"],
            target: "channel",
          },
        },
      },
    } as OpenClawConfig;
    const { handler, sendTyping, sendMessage } = createHandler(cfg);

    await handler.handleRequested(baseRequest);

    expect(sendTyping).toHaveBeenCalledWith(
      "-1003841603622",
      expect.objectContaining({
        accountId: "default",
        messageThreadId: 928,
      }),
    );
    expect(sendMessage).toHaveBeenCalledWith(
      "-1003841603622",
      expect.stringContaining("/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-once"),
      expect.objectContaining({
        accountId: "default",
        messageThreadId: 928,
        buttons: [
          [
            {
              text: "Allow Once",
              callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-once",
            },
            {
              text: "Allow Always",
              callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-always",
            },
          ],
          [
            {
              text: "Deny",
              callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 deny",
            },
          ],
        ],
      }),
    );
  });

  it("falls back to approver DMs when channel routing is unavailable", async () => {
    const cfg = {
      channels: {
        telegram: {
          execApprovals: {
            enabled: true,
            approvers: ["111", "222"],
            target: "channel",
          },
        },
      },
    } as OpenClawConfig;
    const { handler, sendMessage } = createHandler(cfg);

    await handler.handleRequested({
      ...baseRequest,
      request: {
        ...baseRequest.request,
        turnSourceChannel: "slack",
        turnSourceTo: "U1",
        turnSourceAccountId: null,
        turnSourceThreadId: null,
      },
    });

    expect(sendMessage).toHaveBeenCalledTimes(2);
    expect(sendMessage.mock.calls.map((call) => call[0])).toEqual(["111", "222"]);
  });

  it("clears buttons from tracked approval messages when resolved", async () => {
    const cfg = {
      channels: {
        telegram: {
          execApprovals: {
            enabled: true,
            approvers: ["8460800771"],
            target: "both",
          },
        },
      },
    } as OpenClawConfig;
    const { handler, editReplyMarkup } = createHandler(cfg);

    await handler.handleRequested(baseRequest);
    await handler.handleResolved({
      id: baseRequest.id,
      decision: "allow-once",
      resolvedBy: "telegram:8460800771",
      ts: 2000,
    });

    expect(editReplyMarkup).toHaveBeenCalled();
    expect(editReplyMarkup).toHaveBeenCalledWith(
      "-1003841603622",
      "m1",
      [],
      expect.objectContaining({
        accountId: "default",
      }),
    );
  });
});
