import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
export class MetricsService {
    meterProvider = null;
    meter = null;
    collectorUrl;
    serviceName;
    serviceVersion;
    debug;
    customerId;
    tenantId;
    environment;
    exportIntervalMillis;
    metricReader = null;
    // Business Metrics - Counters
    detectionCounter = null;
    errorCounter = null;
    modelLoadCounter = null;
    userInteractionCounter = null;
    // Business Metrics - Histograms (for latencies and sizes)
    detectionLatencyHistogram = null;
    modelLoadLatencyHistogram = null;
    imageSizeHistogram = null;
    // Business Metrics - Gauges (for current state)
    activeSessionsGauge = null;
    memoryUsageGauge = null;
    // State tracking
    activeSessions = 0;
    gaugeValues = new Map();
    constructor(config = {}) {
        this.collectorUrl = config.collectorUrl || 'https://collector.jaak.ai/v1/metrics';
        this.serviceName = config.serviceName || 'jaak-visage-webcomponent';
        this.serviceVersion = config.serviceVersion || '1.0.0';
        this.debug = config.debug || false;
        this.customerId = config.customerId;
        this.tenantId = config.tenantId;
        this.environment = config.environment || 'production';
        this.exportIntervalMillis = config.exportIntervalMillis || 60000; // Export every 60 seconds
    }
    initialize() {
        try {
            // Create OTLP metrics exporter
            const exporter = new OTLPMetricExporter({
                url: this.collectorUrl,
                headers: {
                    'Content-Type': 'application/json',
                },
            });
            // Create periodic exporting metric reader
            this.metricReader = new PeriodicExportingMetricReader({
                exporter: exporter,
                exportIntervalMillis: this.exportIntervalMillis,
                exportTimeoutMillis: 30000,
            });
            // Create meter provider
            this.meterProvider = new MeterProvider({
                readers: [this.metricReader],
            });
            // Get meter
            this.meter = this.meterProvider.getMeter(this.serviceName, this.serviceVersion);
            // Initialize business metrics
            this.initializeMetrics();
            if (this.debug) {
                console.log('[MetricsService] Initialized successfully', {
                    collectorUrl: this.collectorUrl,
                    serviceName: this.serviceName,
                    serviceVersion: this.serviceVersion,
                    environment: this.environment,
                    customerId: this.customerId,
                    tenantId: this.tenantId,
                    exportIntervalMillis: this.exportIntervalMillis,
                });
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to initialize:', error);
            // Don't throw - metrics should fail silently in production
        }
    }
    initializeMetrics() {
        if (!this.meter)
            return;
        const baseAttributes = this.getBaseAttributes();
        try {
            // COUNTERS - Track events and totals
            // Total detections performed
            this.detectionCounter = this.meter.createCounter('jaak.detections.total', {
                description: 'Total number of face detections',
                unit: '1',
            });
            // Total errors
            this.errorCounter = this.meter.createCounter('jaak.errors.total', {
                description: 'Total number of errors',
                unit: '1',
            });
            // Model loads
            this.modelLoadCounter = this.meter.createCounter('jaak.model.loads.total', {
                description: 'Total number of model loads',
                unit: '1',
            });
            // User interactions
            this.userInteractionCounter = this.meter.createCounter('jaak.user.interactions.total', {
                description: 'Total user interactions (clicks, etc.)',
                unit: '1',
            });
            // HISTOGRAMS - Track distributions of latencies and sizes
            // Detection latency (time from start to completion)
            this.detectionLatencyHistogram = this.meter.createHistogram('jaak.detection.duration', {
                description: 'Duration of detection operations',
                unit: 'ms',
            });
            // Model load latency
            this.modelLoadLatencyHistogram = this.meter.createHistogram('jaak.model.load.duration', {
                description: 'Duration of model loading',
                unit: 'ms',
            });
            // Image sizes
            this.imageSizeHistogram = this.meter.createHistogram('jaak.image.size', {
                description: 'Size of processed images',
                unit: 'bytes',
            });
            // GAUGES - Track current state values
            // Active detection sessions
            this.activeSessionsGauge = this.meter.createObservableGauge('jaak.sessions.active', {
                description: 'Number of active detection sessions',
                unit: '1',
            });
            this.activeSessionsGauge.addCallback((observableResult) => {
                observableResult.observe(this.activeSessions, baseAttributes);
            });
            // Memory usage (if available)
            if ('memory' in performance) {
                this.memoryUsageGauge = this.meter.createObservableGauge('jaak.memory.usage', {
                    description: 'JavaScript heap memory usage',
                    unit: 'bytes',
                });
                this.memoryUsageGauge.addCallback((observableResult) => {
                    const memInfo = performance.memory;
                    if (memInfo) {
                        observableResult.observe(memInfo.usedJSHeapSize, baseAttributes);
                    }
                });
            }
            if (this.debug) {
                console.log('[MetricsService] Business metrics initialized');
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to initialize metrics:', error);
        }
    }
    getBaseAttributes() {
        const attributes = {
            'service.name': this.serviceName,
            'service.version': this.serviceVersion,
            'deployment.environment': this.environment,
        };
        if (this.customerId) {
            attributes['customer.id'] = this.customerId;
        }
        if (this.tenantId) {
            attributes['tenant.id'] = this.tenantId;
        }
        return attributes;
    }
    incrementCounter(name, value = 1, attributes) {
        try {
            const baseAttrs = this.getBaseAttributes();
            const allAttrs = { ...baseAttrs, ...attributes };
            // Use predefined counters or create ad-hoc
            switch (name) {
                case 'detections':
                    this.detectionCounter?.add(value, allAttrs);
                    break;
                case 'errors':
                    this.errorCounter?.add(value, allAttrs);
                    break;
                case 'model_loads':
                    this.modelLoadCounter?.add(value, allAttrs);
                    break;
                case 'user_interactions':
                    this.userInteractionCounter?.add(value, allAttrs);
                    break;
                default:
                    // Create ad-hoc counter if needed
                    if (this.meter) {
                        const counter = this.meter.createCounter(`jaak.${name}.total`, {
                            description: `Total ${name}`,
                            unit: '1',
                        });
                        counter.add(value, allAttrs);
                    }
            }
            if (this.debug) {
                console.log(`[MetricsService] Counter incremented: ${name} += ${value}`, attributes);
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to increment counter:', error);
        }
    }
    recordHistogram(name, value, attributes) {
        try {
            const baseAttrs = this.getBaseAttributes();
            const allAttrs = { ...baseAttrs, ...attributes };
            // Use predefined histograms
            switch (name) {
                case 'detection_duration':
                    this.detectionLatencyHistogram?.record(value, allAttrs);
                    break;
                case 'model_load_duration':
                    this.modelLoadLatencyHistogram?.record(value, allAttrs);
                    break;
                case 'image_size':
                    this.imageSizeHistogram?.record(value, allAttrs);
                    break;
                default:
                    // Create ad-hoc histogram if needed
                    if (this.meter) {
                        const histogram = this.meter.createHistogram(`jaak.${name}`, {
                            description: `Distribution of ${name}`,
                            unit: 'ms',
                        });
                        histogram.record(value, allAttrs);
                    }
            }
            if (this.debug) {
                console.log(`[MetricsService] Histogram recorded: ${name} = ${value}`, attributes);
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to record histogram:', error);
        }
    }
    setGauge(name, value, attributes) {
        try {
            // Store gauge value for observable callbacks
            this.gaugeValues.set(name, value);
            // Update known gauges
            if (name === 'active_sessions') {
                this.activeSessions = value;
            }
            if (this.debug) {
                console.log(`[MetricsService] Gauge set: ${name} = ${value}`, attributes);
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to set gauge:', error);
        }
    }
    async measureDuration(name, fn, attributes) {
        const startTime = performance.now();
        try {
            const result = await fn();
            const duration = performance.now() - startTime;
            this.recordHistogram(name, duration, attributes);
            return result;
        }
        catch (error) {
            const duration = performance.now() - startTime;
            this.recordHistogram(name, duration, { ...attributes, error: true });
            throw error;
        }
    }
    async flush() {
        try {
            if (this.metricReader) {
                await this.metricReader.forceFlush();
            }
            if (this.debug) {
                console.log('[MetricsService] Metrics flushed');
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to flush metrics:', error);
        }
    }
    async cleanup() {
        try {
            if (this.metricReader) {
                await this.metricReader.forceFlush();
                await this.metricReader.shutdown();
            }
            if (this.meterProvider) {
                await this.meterProvider.shutdown();
            }
            if (this.debug) {
                console.log('[MetricsService] Cleaned up successfully');
            }
        }
        catch (error) {
            console.error('[MetricsService] Failed to cleanup:', error);
        }
    }
    // Helper methods for common business metrics
    /**
     * Record a successful face detection
     */
    recordDetection(facesDetected, durationMs) {
        this.incrementCounter('detections', 1, { faces_detected: facesDetected });
        this.recordHistogram('detection_duration', durationMs, { faces_detected: facesDetected });
    }
    /**
     * Record an error
     */
    recordError(errorType, operation) {
        this.incrementCounter('errors', 1, { error_type: errorType, operation });
    }
    /**
     * Record model load performance
     */
    recordModelLoad(modelType, durationMs, cached) {
        this.incrementCounter('model_loads', 1, { model_type: modelType, cached });
        this.recordHistogram('model_load_duration', durationMs, { model_type: modelType, cached });
    }
    /**
     * Record image size
     */
    recordImageSize(sizeBytes) {
        this.recordHistogram('image_size', sizeBytes);
    }
    /**
     * Update active sessions count
     */
    updateActiveSessions(count) {
        this.setGauge('active_sessions', count);
    }
    /**
     * Record user interaction
     */
    recordUserInteraction(interactionType) {
        this.incrementCounter('user_interactions', 1, { interaction_type: interactionType });
    }
}
//# sourceMappingURL=MetricsService.js.map
