/**
 * SIEM Connector Base
 *
 * Abstract base class and interface for SIEM connectors providing:
 * - Connection lifecycle management
 * - Retry logic with exponential backoff
 * - Health checks
 * - Common utilities
 *
 * @packageDocumentation
 * @module security/siem/connector
 */
import { createLogger } from '../../common/logger.js';
const logger = createLogger({ component: 'siem-connector' });
// =============================================================================
// Abstract Base Connector
// =============================================================================
/**
 * Abstract base class for SIEM connectors
 *
 * Provides common functionality:
 * - Retry logic with exponential backoff
 * - Connection state management
 * - Logging
 * - Error handling
 */
export class BaseSIEMConnector {
    name;
    timeout;
    maxRetries;
    retryDelayMs;
    maxRetryDelayMs;
    batchSize;
    connected = false;
    logger;
    constructor(config) {
        this.name = config.name;
        this.timeout = config.timeout ?? 30000;
        this.maxRetries = config.maxRetries ?? 3;
        this.retryDelayMs = config.retryDelayMs ?? 1000;
        this.maxRetryDelayMs = config.maxRetryDelayMs ?? 30000;
        this.batchSize = config.batchSize ?? 100;
        this.logger = createLogger({ component: `siem-${this.name}` });
    }
    /**
     * Connect to the SIEM system
     */
    async connect() {
        if (this.connected) {
            this.logger.debug({}, 'Already connected');
            return;
        }
        try {
            await this.doConnect();
            this.connected = true;
            this.logger.info({ connector: this.name }, 'Connected to SIEM');
        }
        catch (error) {
            this.logger.error({ error, connector: this.name }, 'Failed to connect to SIEM');
            throw error;
        }
    }
    /**
     * Disconnect from the SIEM system
     */
    async disconnect() {
        if (!this.connected) {
            return;
        }
        try {
            await this.doDisconnect();
            this.connected = false;
            this.logger.info({ connector: this.name }, 'Disconnected from SIEM');
        }
        catch (error) {
            this.logger.error({ error, connector: this.name }, 'Error during disconnect');
            this.connected = false;
        }
    }
    /**
     * Send events to the SIEM system with retry logic
     */
    async send(events) {
        if (events.length === 0) {
            return {
                success: true,
                eventsSent: 0,
                eventsFailed: 0,
                durationMs: 0,
            };
        }
        const startTime = Date.now();
        let lastError = null;
        let attempt = 0;
        while (attempt <= this.maxRetries) {
            try {
                const result = await this.doSend(events);
                result.durationMs = Date.now() - startTime;
                if (result.success) {
                    this.logger.debug({
                        connector: this.name,
                        eventsSent: result.eventsSent,
                        durationMs: result.durationMs,
                    }, 'Events sent successfully');
                }
                return result;
            }
            catch (error) {
                lastError = error instanceof Error ? error : new Error(String(error));
                attempt++;
                if (attempt <= this.maxRetries) {
                    const delay = this.calculateBackoff(attempt);
                    this.logger.warn({
                        connector: this.name,
                        error: lastError.message,
                        attempt,
                        maxRetries: this.maxRetries,
                        retryDelayMs: delay,
                    }, 'Send failed, retrying');
                    await this.sleep(delay);
                }
            }
        }
        const durationMs = Date.now() - startTime;
        this.logger.error({
            connector: this.name,
            error: lastError?.message,
            attempts: attempt,
            eventsCount: events.length,
            durationMs,
        }, 'Failed to send events after all retries');
        return {
            success: false,
            eventsSent: 0,
            eventsFailed: events.length,
            error: lastError?.message ?? 'Unknown error',
            durationMs,
            failedEventIds: events.map((e) => e.id),
        };
    }
    /**
     * Health check with timeout
     */
    async healthCheck() {
        try {
            const timeoutPromise = new Promise((_, reject) => {
                setTimeout(() => reject(new Error('Health check timeout')), this.timeout);
            });
            const checkPromise = this.doHealthCheck();
            return await Promise.race([checkPromise, timeoutPromise]);
        }
        catch (error) {
            this.logger.warn({ connector: this.name, error: error.message }, 'Health check failed');
            return false;
        }
    }
    /**
     * Check if connected
     */
    isConnected() {
        return this.connected;
    }
    // ===========================================================================
    // Protected Utility Methods
    // ===========================================================================
    /**
     * Calculate exponential backoff delay
     */
    calculateBackoff(attempt) {
        const delay = this.retryDelayMs * Math.pow(2, attempt - 1);
        // Add jitter (up to 20%)
        const jitter = delay * 0.2 * Math.random();
        return Math.min(delay + jitter, this.maxRetryDelayMs);
    }
    /**
     * Sleep for specified milliseconds
     */
    sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    /**
     * Make HTTP request with timeout
     */
    async httpRequest(options) {
        const { url, method = 'POST', headers = {}, body, timeout = this.timeout, } = options;
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);
        try {
            const requestInit = {
                method,
                headers: {
                    'Content-Type': 'application/json',
                    ...headers,
                },
                signal: controller.signal,
            };
            if (body !== undefined) {
                requestInit.body = typeof body === 'string' ? body : JSON.stringify(body);
            }
            const response = await fetch(url, requestInit);
            const text = await response.text();
            let data;
            try {
                data = JSON.parse(text);
            }
            catch {
                data = text;
            }
            return {
                status: response.status,
                ok: response.ok,
                data,
                text,
            };
        }
        finally {
            clearTimeout(timeoutId);
        }
    }
    /**
     * Split events into batches
     */
    splitIntoBatches(events) {
        const batches = [];
        for (let i = 0; i < events.length; i += this.batchSize) {
            batches.push(events.slice(i, i + this.batchSize));
        }
        return batches;
    }
}
/**
 * Registry of connector factories
 */
const connectorFactories = new Map();
/**
 * Register a connector factory
 */
export function registerConnectorFactory(type, factory) {
    connectorFactories.set(type, factory);
    logger.debug({ type }, 'Registered SIEM connector factory');
}
/**
 * Get a connector factory by type
 */
export function getConnectorFactory(type) {
    return connectorFactories.get(type);
}
/**
 * Get all registered connector types
 */
export function getRegisteredConnectorTypes() {
    return Array.from(connectorFactories.keys());
}
//# sourceMappingURL=connector.js.map