import { createReadStream } from "node:fs";
import { SlackChannelAdapter, } from "../shared/channel-adapter.js";
import { redactText } from "../../core/redaction.js";
import { slackRateLimiter } from "./slack-rate-limit.js";
const SLACK_TEXT_LIMIT = 40000;
const SLACK_SAFE_TEXT_LIMIT = 3500;
export const SLACK_ACTION_PREFIX = "nr:";
export class SlackBotChannelRuntime {
    client;
    id = "slack";
    label = "Slack";
    capabilities = new SlackChannelAdapter().capabilities;
    constructor(client) {
        this.client = client;
    }
    describe() {
        return new SlackChannelAdapter().describe();
    }
    async sendMessage(context, message) {
        const chunks = splitSlackMessage(slackMessageText(message));
        let firstTs = "";
        for (const [index, chunk] of chunks.entries()) {
            const result = await slackRateLimiter.run(slackBucket(context), "sendMessage", () => this.client.chat.postMessage({
                channel: context.chatId,
                thread_ts: message.threadId ?? context.topicId,
                text: chunk,
                mrkdwn: true,
                blocks: slackBlocks({ ...message, fallbackText: chunk, text: chunk }, index === chunks.length - 1),
                unfurl_links: false,
                unfurl_media: false,
            }));
            firstTs ||= result.ts ?? "";
        }
        return { messageId: firstTs };
    }
    async editMessage(context, messageId, message) {
        const text = trimSlackMessage(slackMessageText(message));
        await slackRateLimiter.run(slackBucket(context), "editMessage", () => this.client.chat.update({
            channel: context.chatId,
            ts: messageId,
            text,
            blocks: slackBlocks({ ...message, fallbackText: text, text }, true),
            unfurl_links: false,
            unfurl_media: false,
        })).catch(async () => {
            await this.sendMessage(context, message);
        });
    }
    async sendTyping(context) {
        await slackRateLimiter.run(slackBucket(context), "typing", async () => {
            await this.client.assistant?.threads?.setStatus?.({
                channel_id: context.chatId,
                thread_ts: context.topicId,
                status: "Working...",
            });
        }).catch(() => { });
    }
    async sendFile(context, file) {
        const result = await slackRateLimiter.run(slackBucket(context), "sendFile", () => this.client.files.uploadV2({
            channel_id: context.chatId,
            thread_ts: file.threadId ?? context.topicId,
            file: createReadStream(file.localPath),
            filename: file.name,
            title: file.name,
            initial_comment: file.caption ? trimSlackMessage(redactText(file.caption)) : undefined,
        }));
        return { messageId: result.files?.[0]?.id ?? "" };
    }
}
export function slackMessageText(message) {
    const text = message.fallbackText?.trim() || stripHtml(message.text).trim() || ".";
    return text.length <= SLACK_TEXT_LIMIT ? text : `${text.slice(0, SLACK_TEXT_LIMIT - 1)}…`;
}
export function slackActionId(action) {
    const raw = `${SLACK_ACTION_PREFIX}${action}`;
    return raw.length <= 255 ? raw : `${SLACK_ACTION_PREFIX}${action.slice(0, 255 - SLACK_ACTION_PREFIX.length)}`;
}
export function actionFromSlackActionId(actionId) {
    return actionId.startsWith(SLACK_ACTION_PREFIX) ? actionId.slice(SLACK_ACTION_PREFIX.length) : null;
}
export function splitSlackMessage(text) {
    const normalized = text || ".";
    if (normalized.length <= SLACK_SAFE_TEXT_LIMIT) {
        return [normalized];
    }
    const chunks = [];
    let remaining = normalized;
    while (remaining.length > 0) {
        const slice = remaining.slice(0, SLACK_SAFE_TEXT_LIMIT);
        const breakAt = Math.max(slice.lastIndexOf("\n"), slice.lastIndexOf(" "));
        const length = breakAt > 400 ? breakAt : SLACK_SAFE_TEXT_LIMIT;
        chunks.push(remaining.slice(0, length).trimEnd() || ".");
        remaining = remaining.slice(length).trimStart();
    }
    return chunks;
}
export function trimSlackMessage(text) {
    return text.length <= SLACK_SAFE_TEXT_LIMIT ? text : `${text.slice(0, SLACK_SAFE_TEXT_LIMIT - 1)}…`;
}
export function slackBlocks(message, includeButtons = true) {
    const blocks = [
        {
            type: "section",
            text: {
                type: "mrkdwn",
                text: trimSlackMessage(slackMessageText(message)),
            },
        },
    ];
    if (includeButtons && message.buttons?.length) {
        blocks.push(...message.buttons.slice(0, 5).map((row) => ({
            type: "actions",
            elements: row.slice(0, 5).map((button) => slackButton(button)),
        })));
    }
    return blocks;
}
function slackButton(button) {
    return {
        type: "button",
        text: {
            type: "plain_text",
            text: trimButtonLabel(button.label),
            emoji: true,
        },
        action_id: slackActionId(button.action),
        value: button.action.slice(0, 2000),
    };
}
function slackBucket(context) {
    return context.topicId ?? context.chatId;
}
function stripHtml(text) {
    return text
        .replace(/<br\s*\/?>/gi, "\n")
        .replace(/<\/(p|div|li|pre)>/gi, "\n")
        .replace(/<[^>]+>/g, "")
        .replace(/&lt;/g, "<")
        .replace(/&gt;/g, ">")
        .replace(/&amp;/g, "&")
        .replace(/&quot;/g, "\"")
        .replace(/&#39;/g, "'");
}
function trimButtonLabel(label) {
    const trimmed = label.trim() || "Action";
    return trimmed.length <= 75 ? trimmed : trimmed.slice(0, 75);
}
