import { ServiceBubble } from '../../../types/service-bubble-class.js';
import { CredentialType, decodeCredentialPayload, } from '@bubblelab/shared-schemas';
import { DiscordParamsSchema, DiscordResultSchema, } from './discord.schema.js';
const DISCORD_API_BASE = 'https://discord.com/api/v10';
/** Map user-friendly channel type to Discord's integer type */
const CHANNEL_TYPE_MAP = {
    text: 0,
    voice: 2,
    category: 4,
    announcement: 5,
    forum: 15,
};
/**
 * Discord Service Bubble
 *
 * Comprehensive Discord integration for messaging, channel management,
 * threads, reactions, and guild operations via the Discord REST API (v10).
 */
export class DiscordBubble extends ServiceBubble {
    static type = 'service';
    static service = 'discord';
    static authType = 'oauth';
    static bubbleName = 'discord';
    static schema = DiscordParamsSchema;
    static resultSchema = DiscordResultSchema;
    static shortDescription = 'Discord integration for messaging, channels, threads, and server management';
    static longDescription = `
    Comprehensive Discord integration via the REST API (v10).

    Features:
    - Send, edit, delete, and pin messages in channels
    - List and read message history
    - Add emoji reactions to messages
    - List, create, and delete channels
    - Create and list threads
    - List guild/server members
    - Get guild/server information

    Security Features:
    - OAuth 2.0 authentication with Discord
    - Guild-scoped access with bot permissions
    - Secure credential handling with base64-encoded payloads
  `;
    static alias = '';
    constructor(params = {
        operation: 'list_channels',
    }, context) {
        super(params, context);
    }
    async testCredential() {
        const creds = this.parseCredentials();
        if (!creds) {
            throw new Error('Discord credentials are required');
        }
        // Try Bot auth first (runtime credential with bot token from env)
        const botResponse = await fetch(`${DISCORD_API_BASE}/users/@me`, {
            headers: {
                Authorization: `Bot ${creds.botToken}`,
                Accept: 'application/json',
            },
        });
        if (botResponse.ok)
            return true;
        // At validation time, the credential is a raw OAuth access token
        // (not yet encoded with bot token by credential-helper), so try Bearer auth
        if (botResponse.status === 401) {
            const bearerResponse = await fetch(`${DISCORD_API_BASE}/users/@me`, {
                headers: {
                    Authorization: `Bearer ${creds.botToken}`,
                    Accept: 'application/json',
                },
            });
            if (bearerResponse.ok)
                return true;
            const text = await bearerResponse.text();
            throw new Error(`Discord API error (${bearerResponse.status}): ${text}`);
        }
        const text = await botResponse.text();
        throw new Error(`Discord API error (${botResponse.status}): ${text}`);
    }
    parseCredentials() {
        const { credentials } = this.params;
        if (!credentials || typeof credentials !== 'object') {
            return null;
        }
        const raw = credentials[CredentialType.DISCORD_CRED];
        if (!raw) {
            return null;
        }
        try {
            const parsed = decodeCredentialPayload(raw);
            // Prefer botToken (injected by credential-helper from env)
            const token = parsed.botToken || parsed.accessToken;
            if (token) {
                return {
                    botToken: token,
                    guildId: parsed.guildId || '',
                };
            }
        }
        catch {
            // If decoding fails, treat the raw value as a token
        }
        return { botToken: raw, guildId: '' };
    }
    chooseCredential() {
        const creds = this.parseCredentials();
        return creds?.botToken;
    }
    /** Resolve guild ID from params or credential metadata. */
    getGuildId(paramsGuildId) {
        if (paramsGuildId)
            return paramsGuildId;
        const creds = this.parseCredentials();
        if (creds?.guildId)
            return creds.guildId;
        throw new Error('Guild ID is required. Provide it in the guild_id parameter or ensure your Discord credential has guild metadata.');
    }
    // ─── API Request Helper ─────────────────────────────────────────────
    async discordRequest(endpoint, method = 'GET', body) {
        const creds = this.parseCredentials();
        if (!creds) {
            throw new Error('Discord credentials are required');
        }
        const url = `${DISCORD_API_BASE}${endpoint}`;
        const init = {
            method,
            headers: {
                Authorization: `Bot ${creds.botToken}`,
                'Content-Type': 'application/json',
                Accept: 'application/json',
            },
        };
        if (body && method !== 'GET') {
            init.body = JSON.stringify(body);
        }
        const response = await fetch(url, init);
        // 204 No Content (e.g., reactions, pins, deletes)
        if (response.status === 204) {
            return null;
        }
        const text = await response.text();
        if (!response.ok) {
            let errorMessage = `Discord API error (${response.status})`;
            try {
                const errorData = JSON.parse(text);
                if (errorData.message) {
                    errorMessage += `: ${errorData.message}`;
                }
                if (errorData.code) {
                    errorMessage += ` [code: ${errorData.code}]`;
                }
            }
            catch {
                errorMessage += `: ${text}`;
            }
            throw new Error(errorMessage);
        }
        return text ? JSON.parse(text) : null;
    }
    // ─── Main Action ────────────────────────────────────────────────────
    async performAction() {
        const { operation } = this.params;
        try {
            const result = await (async () => {
                const p = this.params;
                switch (operation) {
                    case 'send_message':
                        return await this.sendMessage(p);
                    case 'list_messages':
                        return await this.listMessages(p);
                    case 'edit_message':
                        return await this.editMessage(p);
                    case 'delete_message':
                        return await this.deleteMessage(p);
                    case 'pin_message':
                        return await this.pinMessage(p);
                    case 'add_reaction':
                        return await this.addReaction(p);
                    case 'list_channels':
                        return await this.listChannels(p);
                    case 'create_channel':
                        return await this.createChannel(p);
                    case 'delete_channel':
                        return await this.deleteChannel(p);
                    case 'create_thread':
                        return await this.createThread(p);
                    case 'list_threads':
                        return await this.listThreads(p);
                    case 'list_members':
                        return await this.listMembers(p);
                    case 'get_guild':
                        return await this.getGuild(p);
                    default:
                        throw new Error(`Unknown operation: ${operation}`);
                }
            })();
            return result;
        }
        catch (error) {
            return {
                operation,
                success: false,
                error: error instanceof Error ? error.message : String(error),
            };
        }
    }
    // ─── Message Operations ─────────────────────────────────────────────
    async sendMessage(params) {
        const body = {};
        if (params.content)
            body.content = params.content;
        if (params.embeds)
            body.embeds = params.embeds;
        if (params.tts)
            body.tts = params.tts;
        if (params.reply_to) {
            body.message_reference = { message_id: params.reply_to };
        }
        const message = await this.discordRequest(`/channels/${params.channel_id}/messages`, 'POST', body);
        return {
            operation: 'send_message',
            success: true,
            message: message,
            error: '',
        };
    }
    async listMessages(params) {
        const queryParams = new URLSearchParams();
        if (params.limit)
            queryParams.set('limit', String(params.limit));
        if (params.before)
            queryParams.set('before', params.before);
        if (params.after)
            queryParams.set('after', params.after);
        const qs = queryParams.toString();
        const messages = await this.discordRequest(`/channels/${params.channel_id}/messages${qs ? `?${qs}` : ''}`);
        return {
            operation: 'list_messages',
            success: true,
            messages: messages,
            error: '',
        };
    }
    async editMessage(params) {
        const body = {};
        if (params.content !== undefined)
            body.content = params.content;
        if (params.embeds)
            body.embeds = params.embeds;
        const message = await this.discordRequest(`/channels/${params.channel_id}/messages/${params.message_id}`, 'PATCH', body);
        return {
            operation: 'edit_message',
            success: true,
            message: message,
            error: '',
        };
    }
    async deleteMessage(params) {
        await this.discordRequest(`/channels/${params.channel_id}/messages/${params.message_id}`, 'DELETE');
        return {
            operation: 'delete_message',
            success: true,
            error: '',
        };
    }
    async pinMessage(params) {
        await this.discordRequest(`/channels/${params.channel_id}/pins/${params.message_id}`, 'PUT');
        return {
            operation: 'pin_message',
            success: true,
            error: '',
        };
    }
    // ─── Reaction Operations ────────────────────────────────────────────
    async addReaction(params) {
        const encodedEmoji = encodeURIComponent(params.emoji);
        await this.discordRequest(`/channels/${params.channel_id}/messages/${params.message_id}/reactions/${encodedEmoji}/@me`, 'PUT');
        return {
            operation: 'add_reaction',
            success: true,
            error: '',
        };
    }
    // ─── Channel Operations ─────────────────────────────────────────────
    async listChannels(params) {
        const guildId = this.getGuildId(params.guild_id);
        const channels = await this.discordRequest(`/guilds/${guildId}/channels`);
        return {
            operation: 'list_channels',
            success: true,
            channels: channels,
            error: '',
        };
    }
    async createChannel(params) {
        const guildId = this.getGuildId(params.guild_id);
        const body = {
            name: params.name,
            type: CHANNEL_TYPE_MAP[params.type] ?? 0,
        };
        if (params.topic)
            body.topic = params.topic;
        if (params.parent_id)
            body.parent_id = params.parent_id;
        if (params.nsfw !== undefined)
            body.nsfw = params.nsfw;
        const channel = await this.discordRequest(`/guilds/${guildId}/channels`, 'POST', body);
        return {
            operation: 'create_channel',
            success: true,
            channel: channel,
            error: '',
        };
    }
    async deleteChannel(params) {
        await this.discordRequest(`/channels/${params.channel_id}`, 'DELETE');
        return {
            operation: 'delete_channel',
            success: true,
            error: '',
        };
    }
    // ─── Thread Operations ──────────────────────────────────────────────
    async createThread(params) {
        const endpoint = params.message_id
            ? `/channels/${params.channel_id}/messages/${params.message_id}/threads`
            : `/channels/${params.channel_id}/threads`;
        const body = {
            name: params.name,
            auto_archive_duration: Number(params.auto_archive_duration),
        };
        // Standalone threads (without a message) need type 11 (public thread)
        if (!params.message_id) {
            body.type = 11;
        }
        const thread = await this.discordRequest(endpoint, 'POST', body);
        return {
            operation: 'create_thread',
            success: true,
            thread: thread,
            error: '',
        };
    }
    async listThreads(params) {
        const guildId = this.getGuildId(params.guild_id);
        const result = (await this.discordRequest(`/guilds/${guildId}/threads/active`));
        return {
            operation: 'list_threads',
            success: true,
            threads: result?.threads || [],
            error: '',
        };
    }
    // ─── Member Operations ──────────────────────────────────────────────
    async listMembers(params) {
        const guildId = this.getGuildId(params.guild_id);
        const queryParams = new URLSearchParams();
        if (params.limit)
            queryParams.set('limit', String(params.limit));
        if (params.after)
            queryParams.set('after', params.after);
        const qs = queryParams.toString();
        const members = await this.discordRequest(`/guilds/${guildId}/members${qs ? `?${qs}` : ''}`);
        return {
            operation: 'list_members',
            success: true,
            members: members,
            error: '',
        };
    }
    // ─── Guild Operations ───────────────────────────────────────────────
    async getGuild(params) {
        const guildId = this.getGuildId(params.guild_id);
        const guild = await this.discordRequest(`/guilds/${guildId}`);
        return {
            operation: 'get_guild',
            success: true,
            guild: guild,
            error: '',
        };
    }
}
//# sourceMappingURL=discord.js.map