/**
 * @file Observability Hooks
 * OpenTelemetry integration for evaluation tracing
 */
import { logger } from "../../utils/logger.js";
/**
 * Observability hooks manager
 */
export class ObservabilityHooks {
    _handlers = new Map();
    _traceContext;
    _enabled = true;
    /**
     * Enable/disable observability
     */
    set enabled(value) {
        this._enabled = value;
    }
    get enabled() {
        return this._enabled;
    }
    /**
     * Set trace context for all events
     */
    setTraceContext(context) {
        this._traceContext = context;
    }
    /**
     * Clear trace context
     */
    clearTraceContext() {
        this._traceContext = undefined;
    }
    /**
     * Get current trace context
     */
    getTraceContext() {
        return this._traceContext;
    }
    /**
     * Register an event handler
     */
    on(event, handler) {
        let handlers = this._handlers.get(event);
        if (!handlers) {
            handlers = new Set();
            this._handlers.set(event, handlers);
        }
        handlers.add(handler);
        // Return unsubscribe function
        return () => {
            this._handlers.get(event)?.delete(handler);
        };
    }
    /**
     * Remove an event handler
     */
    off(event, handler) {
        this._handlers.get(event)?.delete(handler);
    }
    /**
     * Emit an event
     */
    async emit(event, data) {
        if (!this._enabled) {
            return;
        }
        const handlers = this._handlers.get(event);
        if (!handlers || handlers.size === 0) {
            return;
        }
        const eventData = {
            ...data,
            traceContext: this._traceContext,
        };
        const promises = [];
        for (const handler of handlers) {
            try {
                const result = handler(eventData);
                if (result instanceof Promise) {
                    promises.push(result.catch((err) => {
                        logger.error(`Event handler error for ${event}`, { error: err });
                    }));
                }
            }
            catch (error) {
                logger.error(`Event handler error for ${event}`, { error });
            }
        }
        // Wait for async handlers
        await Promise.all(promises);
    }
    /**
     * Clear all handlers
     */
    clear() {
        this._handlers.clear();
    }
    /**
     * Get handler count for an event
     */
    listenerCount(event) {
        return this._handlers.get(event)?.size ?? 0;
    }
}
/**
 * Global observability hooks instance
 */
export const observabilityHooks = new ObservabilityHooks();
/**
 * Helper: Create a console logger hook
 */
export function createConsoleLoggerHook() {
    observabilityHooks.on("scorer:start", (event) => {
        logger.info(`[SCORER] ${event.scorerName} started at ${new Date(event.timestamp).toISOString()}`);
    });
    observabilityHooks.on("scorer:end", (event) => {
        logger.info(`[SCORER] ${event.scorerName} completed: score=${event.result.score.toFixed(1)}, ` +
            `passed=${event.result.passed}, duration=${event.duration}ms`);
    });
    observabilityHooks.on("scorer:error", (event) => {
        logger.error(`[SCORER] ${event.scorerName} error: ${event.error}`);
    });
    observabilityHooks.on("pipeline:start", (event) => {
        logger.info(`[PIPELINE] ${event.pipelineName} started with ${event.scorerCount} scorers ` +
            `(correlationId: ${event.correlationId})`);
    });
    observabilityHooks.on("pipeline:end", (event) => {
        logger.info(`[PIPELINE] ${event.pipelineName} completed: overall=${event.result.overallScore.toFixed(1)}, ` +
            `passed=${event.result.passed}, duration=${event.duration}ms`);
    });
    observabilityHooks.on("pipeline:error", (event) => {
        logger.error(`[PIPELINE] ${event.pipelineName} error: ${event.error}`);
    });
}
/**
 * Helper: Create a metrics collector hook
 * Accepts the actual MetricsCollector interface from reporting/metricsCollector
 */
export function createMetricsCollectorHook(collector) {
    observabilityHooks.on("scorer:end", (event) => {
        collector.recordScorer(event.scorerId, event.scorerName, event.result);
    });
    observabilityHooks.on("pipeline:end", (event) => {
        collector.recordPipeline(event.result);
    });
}
/**
 * Create span attributes from scorer result
 */
export function scorerToSpanAttributes(result) {
    return {
        "scorer.id": result.scorerId,
        "scorer.name": result.scorerName,
        "scorer.score": result.score,
        "scorer.normalizedScore": result.normalizedScore,
        "scorer.passed": result.passed,
        "scorer.threshold": result.threshold,
        "scorer.computeTime": result.computeTime,
        ...(result.confidence !== undefined && {
            "scorer.confidence": result.confidence,
        }),
        ...(result.error && { "scorer.error": result.error }),
    };
}
/**
 * Create span attributes from pipeline result
 */
export function pipelineToSpanAttributes(result) {
    return {
        "pipeline.name": result.pipelineConfig.name ?? "unnamed",
        "pipeline.overallScore": result.overallScore,
        "pipeline.passed": result.passed,
        "pipeline.aggregationMethod": result.aggregationMethod,
        "pipeline.scorerCount": result.scores.length,
        "pipeline.totalComputeTime": result.totalComputeTime,
        "pipeline.errorCount": result.errors.length,
        "pipeline.skippedCount": result.skippedScorers.length,
        ...(result.correlationId && {
            "pipeline.correlationId": result.correlationId,
        }),
    };
}
