/**
 * Abstract base class for all observability exporters
 * Follows NeuroLink's factory pattern with a unified exporter interface
 */
import { logger } from "../../utils/logger.js";
/**
 * Abstract base class for all observability exporters
 * Provides common functionality: buffering, flush intervals, health checks, retry logic
 */
export class BaseExporter {
    name;
    config;
    initialized = false;
    buffer = [];
    maxBufferSize;
    retries;
    flushInterval = null;
    lastExportTime = 0;
    constructor(name, config) {
        this.name = name;
        this.config = config;
        this.maxBufferSize = config.maxBufferSize ?? 100;
        this.retries = config.retries ?? 3;
    }
    /**
     * Ping the exporter's backend to verify connectivity
     * Override this in subclasses to provide backend-specific health check
     */
    async ping() {
        // Default implementation does nothing
        // Subclasses should override this to make an actual API call
    }
    /**
     * Buffer a span for batch export
     * Triggers flush if buffer is full
     */
    bufferSpan(span) {
        this.buffer.push(span);
        if (this.buffer.length >= this.maxBufferSize) {
            // Use void to explicitly ignore the promise
            void this.flush();
        }
    }
    /**
     * Start automatic flush interval
     * @param intervalMs - Interval in milliseconds between flushes
     */
    startFlushInterval(intervalMs) {
        if (this.flushInterval) {
            clearInterval(this.flushInterval);
        }
        this.flushInterval = setInterval(() => {
            this.flush().catch((error) => {
                logger.warn(`[${this.name}] Periodic flush failed`, {
                    error: error instanceof Error ? error.message : String(error),
                });
            });
        }, intervalMs);
    }
    /**
     * Stop the automatic flush interval
     */
    stopFlushInterval() {
        if (this.flushInterval) {
            clearInterval(this.flushInterval);
            this.flushInterval = null;
        }
    }
    /**
     * Get exporter name
     */
    getName() {
        return this.name;
    }
    /**
     * Check if exporter is initialized
     */
    isInitialized() {
        return this.initialized;
    }
    /**
     * Get number of pending spans in buffer
     */
    getPendingCount() {
        return this.buffer.length;
    }
    /**
     * Get last export timestamp
     */
    getLastExportTime() {
        return this.lastExportTime;
    }
    /**
     * Create a standard export result for success
     */
    createSuccessResult(exportedCount, durationMs) {
        this.lastExportTime = Date.now();
        return {
            success: true,
            exportedCount,
            failedCount: 0,
            durationMs,
        };
    }
    /**
     * Create a standard export result for failure
     */
    createFailureResult(spanIds, error, durationMs, retryable = true) {
        return {
            success: false,
            exportedCount: 0,
            failedCount: spanIds.length,
            errors: spanIds.map((spanId) => ({
                spanId,
                error,
                retryable,
            })),
            durationMs,
        };
    }
    /**
     * Create a standard health status
     */
    createHealthStatus(healthy, errors) {
        return {
            healthy,
            name: this.name,
            pendingSpans: this.buffer.length,
            lastExportTime: this.lastExportTime || undefined,
            errors,
        };
    }
    /**
     * Execute an operation with exponential backoff retry
     * @param operation - The async operation to execute
     * @param operationName - Name for logging purposes
     * @returns The result of the operation
     * @throws The last error if all retries fail
     */
    async withRetry(operation, operationName) {
        const maxRetries = this.retries;
        const baseDelay = 1000;
        let lastError;
        for (let attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                return await operation();
            }
            catch (error) {
                lastError = error;
                if (attempt < maxRetries) {
                    const delay = baseDelay * 2 ** attempt;
                    logger.warn(`[${this.name}] ${operationName} failed, retrying in ${delay}ms`, {
                        attempt: attempt + 1,
                        maxRetries,
                        error: lastError.message,
                    });
                    await new Promise((resolve) => setTimeout(resolve, delay));
                }
            }
        }
        throw lastError;
    }
}
/**
 * No-op exporter for when observability is disabled
 * Provides zero-overhead behavior
 */
export class NoOpExporter extends BaseExporter {
    constructor() {
        super("noop", { enabled: false });
    }
    async initialize() {
        this.initialized = true;
    }
    async exportSpan(_span) {
        return this.createSuccessResult(0, 0);
    }
    async exportBatch(_spans) {
        return this.createSuccessResult(0, 0);
    }
    async flush() {
        // No-op
    }
    async shutdown() {
        this.initialized = false;
    }
    async healthCheck() {
        return this.createHealthStatus(true);
    }
}
//# sourceMappingURL=baseExporter.js.map