/**
 * Webhook Integration
 *
 * Support for external webhooks:
 * - Event notifications
 * - Custom endpoints
 * - Retry logic
 * - Payload signing
 */
import * as https from 'https';
import * as http from 'http';
import * as crypto from 'crypto';
import fs from 'fs-extra';
import * as path from 'path';
import * as os from 'os';
const DEFAULT_WEBHOOK_CONFIG = {
    enabled: true,
    retryCount: 3,
    retryDelay: 1000,
    timeout: 10000,
};
/**
 * Webhook Manager
 */
export class WebhookManager {
    webhooks = new Map();
    deliveryHistory = [];
    configPath;
    maxHistorySize = 100;
    constructor(configPath) {
        this.configPath = configPath || path.join(os.homedir(), '.codebuddy', 'webhooks.json');
        this.loadConfig();
    }
    /**
     * Register a webhook
     */
    register(config) {
        const id = `webhook-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        const webhook = {
            ...DEFAULT_WEBHOOK_CONFIG,
            ...config,
            id,
        };
        this.webhooks.set(id, webhook);
        this.saveConfig();
        return id;
    }
    /**
     * Update a webhook
     */
    update(id, updates) {
        const webhook = this.webhooks.get(id);
        if (!webhook)
            return false;
        Object.assign(webhook, updates);
        this.saveConfig();
        return true;
    }
    /**
     * Remove a webhook
     */
    remove(id) {
        const result = this.webhooks.delete(id);
        if (result)
            this.saveConfig();
        return result;
    }
    /**
     * Get webhook by ID
     */
    get(id) {
        return this.webhooks.get(id);
    }
    /**
     * Get all webhooks
     */
    getAll() {
        return Array.from(this.webhooks.values());
    }
    /**
     * Get webhooks for an event
     */
    getForEvent(event) {
        return this.getAll().filter(w => w.enabled && w.events.includes(event));
    }
    /**
     * Emit an event to all registered webhooks
     */
    async emit(event, data, sessionId) {
        const webhooks = this.getForEvent(event);
        const deliveries = [];
        const payload = {
            event,
            timestamp: new Date().toISOString(),
            data,
            sessionId,
        };
        for (const webhook of webhooks) {
            const delivery = await this.deliver(webhook, payload);
            deliveries.push(delivery);
            this.addToHistory(delivery);
        }
        return deliveries;
    }
    /**
     * Deliver payload to a webhook
     */
    async deliver(webhook, payload) {
        const delivery = {
            id: `delivery-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
            webhookId: webhook.id,
            event: payload.event,
            payload,
            status: 'pending',
            attempts: 0,
        };
        const retryCount = webhook.retryCount || 3;
        const retryDelay = webhook.retryDelay || 1000;
        for (let attempt = 0; attempt < retryCount; attempt++) {
            delivery.attempts++;
            delivery.lastAttempt = new Date();
            try {
                const response = await this.sendRequest(webhook, payload);
                delivery.status = 'success';
                delivery.response = response;
                break;
            }
            catch (error) {
                delivery.error = error instanceof Error ? error.message : 'Unknown error';
                if (attempt < retryCount - 1) {
                    await this.delay(retryDelay * Math.pow(2, attempt)); // Exponential backoff
                }
                else {
                    delivery.status = 'failed';
                }
            }
        }
        return delivery;
    }
    /**
     * Send HTTP request
     */
    sendRequest(webhook, payload) {
        return new Promise((resolve, reject) => {
            const body = JSON.stringify(payload);
            const url = new URL(webhook.url);
            const isHttps = url.protocol === 'https:';
            const headers = {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(body).toString(),
                'User-Agent': 'CodeBuddy-Webhook/1.0',
                ...webhook.headers,
            };
            // Add signature if secret is configured
            if (webhook.secret) {
                const signature = this.signPayload(body, webhook.secret);
                headers['X-Webhook-Signature'] = signature;
            }
            const options = {
                hostname: url.hostname,
                port: url.port || (isHttps ? 443 : 80),
                path: url.pathname + url.search,
                method: 'POST',
                headers,
                timeout: webhook.timeout || 10000,
            };
            const req = (isHttps ? https : http).request(options, (res) => {
                let responseBody = '';
                res.on('data', (chunk) => { responseBody += chunk; });
                res.on('end', () => {
                    if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
                        resolve({ statusCode: res.statusCode, body: responseBody });
                    }
                    else {
                        reject(new Error(`HTTP ${res.statusCode}: ${responseBody.slice(0, 200)}`));
                    }
                });
            });
            req.on('error', reject);
            req.on('timeout', () => {
                req.destroy();
                reject(new Error('Request timeout'));
            });
            req.write(body);
            req.end();
        });
    }
    /**
     * Sign payload with secret
     */
    signPayload(payload, secret) {
        return crypto
            .createHmac('sha256', secret)
            .update(payload)
            .digest('hex');
    }
    /**
     * Verify webhook signature
     */
    static verifySignature(payload, signature, secret) {
        const expected = crypto
            .createHmac('sha256', secret)
            .update(payload)
            .digest('hex');
        return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
    }
    /**
     * Get delivery history
     */
    getHistory(webhookId) {
        if (webhookId) {
            return this.deliveryHistory.filter(d => d.webhookId === webhookId);
        }
        return [...this.deliveryHistory];
    }
    /**
     * Get failed deliveries
     */
    getFailedDeliveries() {
        return this.deliveryHistory.filter(d => d.status === 'failed');
    }
    /**
     * Retry a failed delivery
     */
    async retry(deliveryId) {
        const delivery = this.deliveryHistory.find(d => d.id === deliveryId);
        if (!delivery || delivery.status !== 'failed')
            return null;
        const webhook = this.webhooks.get(delivery.webhookId);
        if (!webhook)
            return null;
        const newDelivery = await this.deliver(webhook, delivery.payload);
        this.addToHistory(newDelivery);
        return newDelivery;
    }
    /**
     * Clear history
     */
    clearHistory() {
        this.deliveryHistory = [];
    }
    /**
     * Test a webhook
     */
    async test(id) {
        const webhook = this.webhooks.get(id);
        if (!webhook)
            return null;
        const payload = {
            event: 'session.start',
            timestamp: new Date().toISOString(),
            data: { test: true, message: 'This is a test webhook delivery' },
        };
        const delivery = await this.deliver(webhook, payload);
        this.addToHistory(delivery);
        return delivery;
    }
    /**
     * Format webhook list for display
     */
    formatWebhooks() {
        const webhooks = this.getAll();
        if (webhooks.length === 0) {
            return 'No webhooks configured.';
        }
        const lines = [
            '',
            '═══════════════════════════════════════════════════════════',
            '              WEBHOOKS',
            '═══════════════════════════════════════════════════════════',
            '',
        ];
        for (const webhook of webhooks) {
            const status = webhook.enabled ? '[ON] ' : '[OFF]';
            lines.push(`${status} ${webhook.name}`);
            lines.push(`      URL: ${webhook.url}`);
            lines.push(`      Events: ${webhook.events.join(', ')}`);
            lines.push('');
        }
        lines.push('═══════════════════════════════════════════════════════════');
        return lines.join('\n');
    }
    /**
     * Add to history
     */
    addToHistory(delivery) {
        this.deliveryHistory.push(delivery);
        // Trim history
        if (this.deliveryHistory.length > this.maxHistorySize) {
            this.deliveryHistory = this.deliveryHistory.slice(-this.maxHistorySize);
        }
    }
    /**
     * Delay helper
     */
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    /**
     * Load config from file
     */
    loadConfig() {
        try {
            if (fs.existsSync(this.configPath)) {
                const data = fs.readJsonSync(this.configPath);
                if (Array.isArray(data)) {
                    for (const webhook of data) {
                        this.webhooks.set(webhook.id, webhook);
                    }
                }
            }
        }
        catch {
            // Start with empty webhooks
        }
    }
    /**
     * Save config to file
     */
    saveConfig() {
        try {
            fs.ensureDirSync(path.dirname(this.configPath));
            const webhooks = Array.from(this.webhooks.values());
            fs.writeJsonSync(this.configPath, webhooks, { spaces: 2 });
        }
        catch {
            // Ignore save errors
        }
    }
}
// Singleton instance
let webhookManager = null;
/**
 * Get or create webhook manager
 */
export function getWebhookManager() {
    if (!webhookManager) {
        webhookManager = new WebhookManager();
    }
    return webhookManager;
}
/**
 * Emit a webhook event
 */
export async function emitWebhook(event, data, sessionId) {
    await getWebhookManager().emit(event, data, sessionId);
}
export default WebhookManager;
//# sourceMappingURL=webhooks.js.map