/**
 * Notification Integrations
 *
 * Send notifications to external services:
 * - Slack webhooks
 * - Discord webhooks
 * - Microsoft Teams
 * - Generic webhooks
 */
import { EventEmitter } from 'events';
import { logger } from '../utils/logger';
/**
 * Notification Manager
 */
export class NotificationManager extends EventEmitter {
    config;
    notificationQueue = [];
    lastNotificationTime = 0;
    notificationCount = 0;
    batchTimer = null;
    constructor(config = {}) {
        super();
        this.config = {
            slackWebhook: config.slackWebhook || '',
            discordWebhook: config.discordWebhook || '',
            teamsWebhook: config.teamsWebhook || '',
            customWebhooks: config.customWebhooks || [],
            defaultChannel: config.defaultChannel || '',
            botName: config.botName || 'Code Buddy',
            iconUrl: config.iconUrl || '',
            rateLimit: config.rateLimit ?? 30,
            batchNotifications: config.batchNotifications ?? false,
            batchInterval: config.batchInterval ?? 5000,
        };
        // Reset rate limit counter every minute
        setInterval(() => {
            this.notificationCount = 0;
        }, 60000);
    }
    /**
     * Send a notification to all configured services
     */
    async notify(notification) {
        // Rate limiting
        if (this.notificationCount >= this.config.rateLimit) {
            logger.warn('Notification rate limit exceeded');
            return;
        }
        if (this.config.batchNotifications) {
            this.queueNotification(notification);
        }
        else {
            await this.sendNotification(notification);
        }
    }
    /**
     * Queue notification for batching
     */
    queueNotification(notification) {
        this.notificationQueue.push(notification);
        if (!this.batchTimer) {
            this.batchTimer = setTimeout(() => {
                this.flushNotifications().catch((err) => {
                    logger.debug('Failed to flush notifications', { error: err instanceof Error ? err.message : String(err) });
                });
                this.batchTimer = null;
            }, this.config.batchInterval);
        }
    }
    /**
     * Flush queued notifications
     */
    async flushNotifications() {
        if (this.notificationQueue.length === 0)
            return;
        const notifications = [...this.notificationQueue];
        this.notificationQueue = [];
        // Send as a batch or individually
        if (notifications.length === 1) {
            await this.sendNotification(notifications[0]);
        }
        else {
            await this.sendBatchNotification(notifications);
        }
    }
    /**
     * Send a single notification
     */
    async sendNotification(notification) {
        const promises = [];
        if (this.config.slackWebhook) {
            promises.push(this.sendSlack(notification));
        }
        if (this.config.discordWebhook) {
            promises.push(this.sendDiscord(notification));
        }
        if (this.config.teamsWebhook) {
            promises.push(this.sendTeams(notification));
        }
        for (const webhook of this.config.customWebhooks) {
            promises.push(this.sendCustom(webhook, notification));
        }
        await Promise.allSettled(promises);
        this.notificationCount++;
        this.emit('notification', notification);
    }
    /**
     * Send batch notification
     */
    async sendBatchNotification(notifications) {
        // Create a summary notification
        const summary = {
            title: `${notifications.length} Notifications`,
            message: notifications.map(n => `• ${n.title}: ${n.message}`).join('\n'),
            level: this.getHighestLevel(notifications),
            fields: [
                { name: 'Count', value: String(notifications.length), inline: true },
                { name: 'Period', value: `${this.config.batchInterval / 1000}s`, inline: true },
            ],
        };
        await this.sendNotification(summary);
    }
    /**
     * Get highest severity level
     */
    getHighestLevel(notifications) {
        const levels = ['info', 'success', 'warning', 'error'];
        let highest = 0;
        for (const n of notifications) {
            const index = levels.indexOf(n.level);
            if (index > highest)
                highest = index;
        }
        return levels[highest];
    }
    /**
     * Send notification to Slack
     */
    async sendSlack(notification) {
        const color = this.getLevelColor(notification.level);
        const message = {
            channel: this.config.defaultChannel || undefined,
            username: this.config.botName,
            icon_url: this.config.iconUrl || undefined,
            attachments: [{
                    color,
                    title: notification.title,
                    title_link: notification.url,
                    text: notification.message,
                    fields: notification.fields?.map(f => ({
                        title: f.name,
                        value: f.value,
                        short: f.inline ?? true,
                    })),
                    footer: notification.footer,
                    ts: Math.floor((notification.timestamp || new Date()).getTime() / 1000),
                    image_url: notification.imageUrl,
                }],
        };
        await this.sendWebhook(this.config.slackWebhook, message);
    }
    /**
     * Send notification to Discord
     */
    async sendDiscord(notification) {
        const color = this.getDiscordColor(notification.level);
        const message = {
            username: this.config.botName,
            avatar_url: this.config.iconUrl || undefined,
            embeds: [{
                    title: notification.title,
                    description: notification.message,
                    color,
                    url: notification.url,
                    fields: notification.fields?.map(f => ({
                        name: f.name,
                        value: f.value,
                        inline: f.inline ?? true,
                    })),
                    footer: notification.footer ? { text: notification.footer } : undefined,
                    timestamp: (notification.timestamp || new Date()).toISOString(),
                    image: notification.imageUrl ? { url: notification.imageUrl } : undefined,
                }],
        };
        await this.sendWebhook(this.config.discordWebhook, message);
    }
    /**
     * Send notification to Microsoft Teams
     */
    async sendTeams(notification) {
        const color = this.getLevelColor(notification.level).replace('#', '');
        const message = {
            '@type': 'MessageCard',
            '@context': 'http://schema.org/extensions',
            themeColor: color,
            summary: notification.title,
            sections: [{
                    activityTitle: notification.title,
                    activitySubtitle: notification.message,
                    facts: notification.fields?.map(f => ({
                        name: f.name,
                        value: f.value,
                    })),
                    markdown: true,
                }],
            potentialAction: notification.url ? [{
                    '@type': 'OpenUri',
                    name: 'View Details',
                    targets: [{ os: 'default', uri: notification.url }],
                }] : undefined,
        };
        await this.sendWebhook(this.config.teamsWebhook, message);
    }
    /**
     * Send notification to custom webhook
     */
    async sendCustom(webhook, notification) {
        const payload = {
            title: notification.title,
            message: notification.message,
            level: notification.level,
            fields: notification.fields,
            timestamp: (notification.timestamp || new Date()).toISOString(),
            footer: notification.footer,
            url: notification.url,
            source: this.config.botName,
        };
        await this.sendWebhook(webhook, payload);
    }
    /**
     * Send webhook request
     */
    async sendWebhook(url, payload) {
        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(payload),
            });
            if (!response.ok) {
                logger.warn('Webhook request failed', { url, status: response.status });
            }
        }
        catch (error) {
            logger.error('Failed to send webhook', { url, error });
        }
    }
    /**
     * Get color for level (hex)
     */
    getLevelColor(level) {
        const colors = {
            info: '#2196F3',
            success: '#4CAF50',
            warning: '#FF9800',
            error: '#F44336',
        };
        return colors[level];
    }
    /**
     * Get Discord color (decimal)
     */
    getDiscordColor(level) {
        const colors = {
            info: 0x2196F3,
            success: 0x4CAF50,
            warning: 0xFF9800,
            error: 0xF44336,
        };
        return colors[level];
    }
    /**
     * Convenience methods for different levels
     */
    async info(title, message, fields) {
        await this.notify({ title, message, level: 'info', fields });
    }
    async success(title, message, fields) {
        await this.notify({ title, message, level: 'success', fields });
    }
    async warning(title, message, fields) {
        await this.notify({ title, message, level: 'warning', fields });
    }
    async error(title, message, fields) {
        await this.notify({ title, message, level: 'error', fields });
    }
    /**
     * Notify session events
     */
    async notifySessionStart(sessionId) {
        await this.info('Session Started', `New session: ${sessionId}`, [
            { name: 'Session ID', value: sessionId.slice(0, 8) },
            { name: 'Time', value: new Date().toLocaleTimeString() },
        ]);
    }
    async notifySessionEnd(sessionId, stats) {
        await this.success('Session Completed', `Session ${sessionId.slice(0, 8)} finished`, [
            { name: 'Messages', value: String(stats.messages), inline: true },
            { name: 'Cost', value: `$${stats.cost.toFixed(4)}`, inline: true },
            { name: 'Duration', value: `${Math.round(stats.duration / 60)}m`, inline: true },
        ]);
    }
    async notifyError(error, context) {
        await this.error('Error Occurred', error.message, [
            { name: 'Type', value: error.name, inline: true },
            { name: 'Context', value: context || 'Unknown', inline: true },
        ]);
    }
    async notifyCostThreshold(current, threshold) {
        await this.warning('Cost Threshold Reached', `Session cost: $${current.toFixed(4)}`, [
            { name: 'Current', value: `$${current.toFixed(4)}`, inline: true },
            { name: 'Threshold', value: `$${threshold.toFixed(2)}`, inline: true },
            { name: 'Percentage', value: `${((current / threshold) * 100).toFixed(0)}%`, inline: true },
        ]);
    }
    /**
     * Close notification manager
     */
    close() {
        if (this.batchTimer) {
            clearTimeout(this.batchTimer);
            this.batchTimer = null;
        }
        this.flushNotifications().catch((err) => {
            logger.debug('Failed to flush notifications on close', { error: err instanceof Error ? err.message : String(err) });
        });
    }
}
// Singleton instance
let notificationManager = null;
/**
 * Get or create notification manager
 */
export function getNotificationManager(config) {
    if (!notificationManager) {
        notificationManager = new NotificationManager(config);
    }
    return notificationManager;
}
/**
 * Quick notify helper
 */
export async function notify(notification) {
    const manager = getNotificationManager();
    await manager.notify(notification);
}
export default NotificationManager;
//# sourceMappingURL=notification-integrations.js.map