// Slack plugin module implements blocks render behavior.
import type { Block, KnownBlock } from "@slack/web-api";
import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
import {
  reduceInteractiveReply,
  resolveMessagePresentationControlValue,
} from "openclaw/plugin-sdk/interactive-runtime";
import type {
  InteractiveReply,
  MessagePresentation,
  MessagePresentationButtonsBlock,
  MessagePresentationSelectBlock,
} from "openclaw/plugin-sdk/interactive-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { SLACK_REPLY_BUTTON_ACTION_ID, SLACK_REPLY_SELECT_ACTION_ID } from "./reply-action-ids.js";
import { truncateSlackText } from "./truncate.js";

const SLACK_SECTION_TEXT_MAX = 3000;
const SLACK_PLAIN_TEXT_MAX = 75;
const SLACK_OPTION_VALUE_MAX = 150;
const SLACK_BUTTON_VALUE_MAX = 2000;
const SLACK_BUTTON_URL_MAX = 3000;
const SLACK_STATIC_SELECT_OPTIONS_MAX = 100;
const SLACK_ACTION_BLOCK_ELEMENTS_MAX = 25;

export type SlackBlock = Block | KnownBlock;

type SlackInteractiveBlockRenderOptions = {
  buttonIndexOffset?: number;
  selectIndexOffset?: number;
};

function buildSlackReplyButtonActionId(buttonIndex: number, choiceIndex: number): string {
  return `${SLACK_REPLY_BUTTON_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
}

function buildSlackReplySelectActionId(selectIndex: number): string {
  return `${SLACK_REPLY_SELECT_ACTION_ID}:${String(selectIndex)}`;
}

function resolveSlackButtonStyle(
  style: "primary" | "secondary" | "success" | "danger" | undefined,
) {
  if (style === "primary" || style === "danger") {
    return style;
  }
  if (style === "success") {
    return "primary";
  }
  return undefined;
}

function resolveSlackControlValue(control: {
  action?: { type: "command"; command: string } | { type: "callback"; value: string };
  value?: string;
}): string | undefined {
  if (control.action?.type === "command") {
    const command = normalizeOptionalString(control.action.command);
    if (command && parseExecApprovalCommandText(command)) {
      return command;
    }
    const legacyValue = normalizeOptionalString(control.value);
    return legacyValue && parseExecApprovalCommandText(legacyValue) ? legacyValue : undefined;
  }
  return resolveMessagePresentationControlValue(control);
}

function isWithinSlackLimit(value: string, maxLength: number): boolean {
  return value.length <= maxLength;
}

function isRenderableSlackOption(option: {
  label: string;
  value: string | undefined;
}): option is { label: string; value: string } {
  return option.value !== undefined && isWithinSlackLimit(option.value, SLACK_OPTION_VALUE_MAX);
}

function readSlackBlockId(block: SlackBlock): string | undefined {
  const value = (block as { block_id?: unknown }).block_id;
  return typeof value === "string" ? value : undefined;
}

function readSlackOpenClawBlockIndex(blockId: string, prefix: string): number | undefined {
  if (!blockId.startsWith(prefix)) {
    return undefined;
  }
  const value = Number.parseInt(blockId.slice(prefix.length), 10);
  return Number.isSafeInteger(value) && value > 0 ? value : undefined;
}

/** Resolve existing OpenClaw Block Kit indexes so appended controls keep stable unique IDs. */
export function resolveSlackInteractiveBlockOffsets(
  blocks?: readonly SlackBlock[],
): SlackInteractiveBlockRenderOptions {
  let buttonIndexOffset = 0;
  let selectIndexOffset = 0;
  for (const block of blocks ?? []) {
    const blockId = readSlackBlockId(block);
    if (!blockId) {
      continue;
    }
    buttonIndexOffset = Math.max(
      buttonIndexOffset,
      readSlackOpenClawBlockIndex(blockId, "openclaw_reply_buttons_") ?? 0,
    );
    selectIndexOffset = Math.max(
      selectIndexOffset,
      readSlackOpenClawBlockIndex(blockId, "openclaw_reply_select_") ?? 0,
    );
  }
  return { buttonIndexOffset, selectIndexOffset };
}

/**
 * @deprecated Use buildSlackPresentationBlocks with MessagePresentation.
 */
export function buildSlackInteractiveBlocks(
  interactive?: InteractiveReply,
  options: SlackInteractiveBlockRenderOptions = {},
): SlackBlock[] {
  const initialState = {
    blocks: [] as SlackBlock[],
    buttonIndex: options.buttonIndexOffset ?? 0,
    selectIndex: options.selectIndexOffset ?? 0,
  };
  return reduceInteractiveReply(interactive, initialState, (state, block) => {
    if (block.type === "text") {
      const trimmed = block.text.trim();
      if (!trimmed) {
        return state;
      }
      state.blocks.push({
        type: "section",
        text: {
          type: "mrkdwn",
          text: truncateSlackText(trimmed, SLACK_SECTION_TEXT_MAX),
        },
      });
      return state;
    }
    if (block.type === "buttons") {
      const elements = block.buttons
        .flatMap((button, choiceIndex) => {
          const callbackData = resolveSlackControlValue(button);
          const value =
            callbackData && isWithinSlackLimit(callbackData, SLACK_BUTTON_VALUE_MAX)
              ? callbackData
              : undefined;
          const url =
            button.url && isWithinSlackLimit(button.url, SLACK_BUTTON_URL_MAX)
              ? button.url
              : undefined;
          if (!value && !url) {
            return [];
          }
          const style = resolveSlackButtonStyle(button.style);
          return [
            {
              type: "button" as const,
              action_id: buildSlackReplyButtonActionId(state.buttonIndex + 1, choiceIndex),
              text: {
                type: "plain_text" as const,
                text: truncateSlackText(button.label, SLACK_PLAIN_TEXT_MAX),
                emoji: true,
              },
              ...(value ? { value } : {}),
              ...(url ? { url } : {}),
              ...(style ? { style } : {}),
            },
          ];
        })
        .slice(0, SLACK_ACTION_BLOCK_ELEMENTS_MAX);
      if (elements.length === 0) {
        return state;
      }
      state.blocks.push({
        type: "actions",
        block_id: `openclaw_reply_buttons_${++state.buttonIndex}`,
        elements,
      });
      return state;
    }
    const optionsLocal = block.options
      .map((option) => ({
        label: option.label,
        value: resolveSlackControlValue(option),
      }))
      .filter(isRenderableSlackOption)
      .slice(0, SLACK_STATIC_SELECT_OPTIONS_MAX);
    if (optionsLocal.length === 0) {
      return state;
    }
    state.blocks.push({
      type: "actions",
      block_id: `openclaw_reply_select_${++state.selectIndex}`,
      elements: [
        {
          type: "static_select",
          action_id: buildSlackReplySelectActionId(state.selectIndex),
          placeholder: {
            type: "plain_text",
            text: truncateSlackText(
              normalizeOptionalString(block.placeholder) ?? "Choose an option",
              SLACK_PLAIN_TEXT_MAX,
            ),
            emoji: true,
          },
          options: optionsLocal.map((option, _choiceIndex) => ({
            text: {
              type: "plain_text",
              text: truncateSlackText(option.label, SLACK_PLAIN_TEXT_MAX),
              emoji: true,
            },
            value: option.value,
          })),
        },
      ],
    });
    return state;
  }).blocks;
}

/** Render portable presentation blocks as Slack Block Kit blocks. */
export function buildSlackPresentationBlocks(
  presentation?: MessagePresentation,
  options: SlackInteractiveBlockRenderOptions = {},
): SlackBlock[] {
  if (!presentation) {
    return [];
  }
  const blocks: SlackBlock[] = [];
  if (presentation.title) {
    blocks.push({
      type: "header",
      text: {
        type: "plain_text",
        text: truncateSlackText(presentation.title, 150),
        emoji: true,
      },
    });
  }
  for (const block of presentation.blocks) {
    if (block.type === "text" || block.type === "context") {
      const text = block.text.trim();
      if (!text) {
        continue;
      }
      if (block.type === "context") {
        blocks.push({
          type: "context",
          elements: [{ type: "mrkdwn", text: truncateSlackText(text, SLACK_SECTION_TEXT_MAX) }],
        });
      } else {
        blocks.push({
          type: "section",
          text: { type: "mrkdwn", text: truncateSlackText(text, SLACK_SECTION_TEXT_MAX) },
        });
      }
      continue;
    }
    if (block.type === "divider") {
      blocks.push({ type: "divider" });
    }
  }
  let buttonIndex = options.buttonIndexOffset ?? 0;
  let selectIndex = options.selectIndexOffset ?? 0;
  for (const block of presentation.blocks) {
    if (block.type === "buttons") {
      const rendered = buildSlackPresentationButtonBlock(block, buttonIndex + 1);
      if (rendered) {
        buttonIndex += 1;
        blocks.push(rendered);
      }
      continue;
    }
    if (block.type === "select") {
      const rendered = buildSlackPresentationSelectBlock(block, selectIndex + 1);
      if (rendered) {
        selectIndex += 1;
        blocks.push(rendered);
      }
    }
  }
  return blocks;
}

function buildSlackPresentationButtonBlock(
  block: MessagePresentationButtonsBlock,
  buttonIndex: number,
): SlackBlock | undefined {
  const elements = block.buttons
    .flatMap((button, choiceIndex) => {
      const callbackData = resolveSlackControlValue(button);
      const value =
        callbackData && isWithinSlackLimit(callbackData, SLACK_BUTTON_VALUE_MAX)
          ? callbackData
          : undefined;
      const url =
        button.url && isWithinSlackLimit(button.url, SLACK_BUTTON_URL_MAX) ? button.url : undefined;
      if (!value && !url) {
        return [];
      }
      const style = resolveSlackButtonStyle(button.style);
      return [
        {
          type: "button" as const,
          action_id: buildSlackReplyButtonActionId(buttonIndex, choiceIndex),
          text: {
            type: "plain_text" as const,
            text: truncateSlackText(button.label, SLACK_PLAIN_TEXT_MAX),
            emoji: true,
          },
          ...(value ? { value } : {}),
          ...(url ? { url } : {}),
          ...(style ? { style } : {}),
        },
      ];
    })
    .slice(0, SLACK_ACTION_BLOCK_ELEMENTS_MAX);
  return elements.length > 0
    ? {
        type: "actions",
        block_id: `openclaw_reply_buttons_${buttonIndex}`,
        elements,
      }
    : undefined;
}

function buildSlackPresentationSelectBlock(
  block: MessagePresentationSelectBlock,
  selectIndex: number,
): SlackBlock | undefined {
  const options = block.options
    .map((option) => ({
      label: option.label,
      value: resolveSlackControlValue(option),
    }))
    .filter(isRenderableSlackOption)
    .slice(0, SLACK_STATIC_SELECT_OPTIONS_MAX);
  return options.length > 0
    ? {
        type: "actions",
        block_id: `openclaw_reply_select_${selectIndex}`,
        elements: [
          {
            type: "static_select",
            action_id: buildSlackReplySelectActionId(selectIndex),
            placeholder: {
              type: "plain_text",
              text: truncateSlackText(
                normalizeOptionalString(block.placeholder) ?? "Choose an option",
                SLACK_PLAIN_TEXT_MAX,
              ),
              emoji: true,
            },
            options: options.map((option) => ({
              text: {
                type: "plain_text",
                text: truncateSlackText(option.label, SLACK_PLAIN_TEXT_MAX),
                emoji: true,
              },
              value: option.value,
            })),
          },
        ],
      }
    : undefined;
}
