import { Type } from "@sinclair/typebox";
import {
  createUnionActionGate,
  listTokenSourcedAccounts,
} from "openclaw/plugin-sdk/channel-actions";
import type {
  ChannelMessageActionAdapter,
  ChannelMessageActionName,
  ChannelMessageToolDiscovery,
} from "openclaw/plugin-sdk/channel-contract";
import type { DiscordActionConfig } from "openclaw/plugin-sdk/config-runtime";
import { createDiscordActionGate, listEnabledDiscordAccounts } from "./accounts.js";
import { handleDiscordMessageAction } from "./actions/handle-action.js";
import { createDiscordMessageToolComponentsSchema } from "./message-tool-schema.js";

function resolveDiscordActionDiscovery(cfg: Parameters<typeof listEnabledDiscordAccounts>[0]) {
  const accounts = listTokenSourcedAccounts(listEnabledDiscordAccounts(cfg));
  if (accounts.length === 0) {
    return null;
  }
  const unionGate = createUnionActionGate(accounts, (account) =>
    createDiscordActionGate({
      cfg,
      accountId: account.accountId,
    }),
  );
  return {
    isEnabled: (key: keyof DiscordActionConfig, defaultValue = true) =>
      unionGate(key, defaultValue),
  };
}

function describeDiscordMessageTool({
  cfg,
}: Parameters<
  NonNullable<ChannelMessageActionAdapter["describeMessageTool"]>
>[0]): ChannelMessageToolDiscovery {
  const discovery = resolveDiscordActionDiscovery(cfg);
  if (!discovery) {
    return {
      actions: [],
      capabilities: [],
      schema: null,
    };
  }
  const actions = new Set<ChannelMessageActionName>(["send"]);
  if (discovery.isEnabled("polls")) {
    actions.add("poll");
  }
  if (discovery.isEnabled("reactions")) {
    actions.add("react");
    actions.add("reactions");
    actions.add("emoji-list");
  }
  if (discovery.isEnabled("messages")) {
    actions.add("read");
    actions.add("edit");
    actions.add("delete");
  }
  if (discovery.isEnabled("pins")) {
    actions.add("pin");
    actions.add("unpin");
    actions.add("list-pins");
  }
  if (discovery.isEnabled("permissions")) {
    actions.add("permissions");
  }
  if (discovery.isEnabled("threads")) {
    actions.add("thread-create");
    actions.add("thread-list");
    actions.add("thread-reply");
  }
  if (discovery.isEnabled("search")) {
    actions.add("search");
  }
  if (discovery.isEnabled("stickers")) {
    actions.add("sticker");
  }
  if (discovery.isEnabled("memberInfo")) {
    actions.add("member-info");
  }
  if (discovery.isEnabled("roleInfo")) {
    actions.add("role-info");
  }
  if (discovery.isEnabled("emojiUploads")) {
    actions.add("emoji-upload");
  }
  if (discovery.isEnabled("stickerUploads")) {
    actions.add("sticker-upload");
  }
  if (discovery.isEnabled("roles", false)) {
    actions.add("role-add");
    actions.add("role-remove");
  }
  if (discovery.isEnabled("channelInfo")) {
    actions.add("channel-info");
    actions.add("channel-list");
  }
  if (discovery.isEnabled("channels")) {
    actions.add("channel-create");
    actions.add("channel-edit");
    actions.add("channel-delete");
    actions.add("channel-move");
    actions.add("category-create");
    actions.add("category-edit");
    actions.add("category-delete");
  }
  if (discovery.isEnabled("voiceStatus")) {
    actions.add("voice-status");
  }
  if (discovery.isEnabled("events")) {
    actions.add("event-list");
    actions.add("event-create");
  }
  if (discovery.isEnabled("moderation", false)) {
    actions.add("timeout");
    actions.add("kick");
    actions.add("ban");
  }
  if (discovery.isEnabled("presence", false)) {
    actions.add("set-presence");
  }
  return {
    actions: Array.from(actions),
    capabilities: ["interactive", "components"],
    schema: {
      properties: {
        components: Type.Optional(createDiscordMessageToolComponentsSchema()),
      },
    },
  };
}

export const discordMessageActions: ChannelMessageActionAdapter = {
  describeMessageTool: describeDiscordMessageTool,
  extractToolSend: ({ args }) => {
    const action = typeof args.action === "string" ? args.action.trim() : "";
    if (action === "sendMessage") {
      const to = typeof args.to === "string" ? args.to : undefined;
      return to ? { to } : null;
    }
    if (action === "threadReply") {
      const channelId = typeof args.channelId === "string" ? args.channelId.trim() : "";
      return channelId ? { to: `channel:${channelId}` } : null;
    }
    return null;
  },
  handleAction: async ({
    action,
    params,
    cfg,
    accountId,
    requesterSenderId,
    toolContext,
    mediaLocalRoots,
  }) => {
    return await handleDiscordMessageAction({
      action,
      params,
      cfg,
      accountId,
      requesterSenderId,
      toolContext,
      mediaLocalRoots,
    });
  },
};
