// engine/observability/otel-tracer.ts — OTel-backed Tracer implementation (SPEC-165)
// Wraps @opentelemetry/api via dynamic import (optional peer dependency).
// Falls back to NoopTracer if the package is not installed.
import { NoopTracer } from './noop-tracer.js';
// ---------------------------------------------------------------------------
// OtelSpanHandle — wraps a live OTel span
// ---------------------------------------------------------------------------
class OtelSpanHandle {
    span;
    statusCodes;
    ended = false;
    constructor(span, statusCodes) {
        this.span = span;
        this.statusCodes = statusCodes;
    }
    setAttribute(key, value) {
        if (this.ended) {
            return;
        }
        this.span.setAttribute(key, value);
    }
    recordError(error) {
        if (this.ended) {
            return;
        }
        const err = typeof error === 'string' ? { message: error } : error;
        this.span.recordException(err);
        this.span.setStatus({ code: this.statusCodes.ERROR, message: err.message });
    }
    end(status) {
        if (this.ended) {
            return;
        }
        this.ended = true;
        if (status === 'ok') {
            this.span.setStatus({ code: this.statusCodes.OK });
        }
        else if (status === 'error') {
            this.span.setStatus({ code: this.statusCodes.ERROR });
        }
        this.span.end();
    }
}
// ---------------------------------------------------------------------------
// OtelTracer — real tracer backed by @opentelemetry/api
// ---------------------------------------------------------------------------
/**
 * OTel-backed tracer. Requires @opentelemetry/api to be installed at runtime.
 * If the package is missing, falls back to NoopTracer silently.
 */
export class OtelTracer {
    inner;
    constructor(inner) {
        this.inner = inner;
    }
    /**
     * Factory method — performs dynamic import of @opentelemetry/api.
     * Returns NoopTracer if the package is unavailable.
     */
    static async create(config) {
        let api;
        try {
            // Dynamic import — optional peer dep, zero hard coupling
            const moduleName = '@opentelemetry/api';
            const module = (await import(moduleName));
            api = module;
        }
        catch {
            // @opentelemetry/api not installed — graceful fallback
            return new NoopTracer();
        }
        if (!config.enabled || !config.endpoint) {
            return new NoopTracer();
        }
        const apiTracer = api.trace.getTracer(config.serviceName, config.serviceVersion);
        const statusCodes = api.SpanStatusCode;
        const realTracer = {
            startSpan(name, attributes) {
                const span = apiTracer.startSpan(name, { attributes });
                const handle = new OtelSpanHandle(span, statusCodes);
                if (attributes) {
                    for (const [k, v] of Object.entries(attributes)) {
                        handle.setAttribute(k, v);
                    }
                }
                return handle;
            },
            startChildSpan(name, parent, attributes) {
                // LIMITATION: @opentelemetry/api context propagation requires async context
                // (AsyncLocalStorage / AsyncResource) which is not wired here.
                // Instead we record a `parent.span.name` attribute so traces can be linked
                // in the backend via attribute-based queries.
                // Full W3C trace-context propagation is tracked as future work.
                const enrichedAttributes = {
                    ...attributes,
                    ...(parent !== null ? { 'parent.linked': true } : {}),
                };
                const span = apiTracer.startSpan(name, { attributes: enrichedAttributes });
                const handle = new OtelSpanHandle(span, statusCodes);
                for (const [k, v] of Object.entries(enrichedAttributes)) {
                    handle.setAttribute(k, v);
                }
                return handle;
            },
            recordCounter(name, value = 1, attributes) {
                // @opentelemetry/sdk-metrics is not a hard dependency.
                // Record as a short-lived span so the metric is captured in the trace backend.
                const span = apiTracer.startSpan(`counter:${name}`, {
                    attributes: { 'metric.value': value, ...attributes },
                });
                span.end();
            },
            recordHistogram(name, value, attributes) {
                // Same approach — histogram observation recorded as a short-lived span.
                const span = apiTracer.startSpan(`histogram:${name}`, {
                    attributes: { 'metric.value': value, ...attributes },
                });
                span.end();
            },
        };
        return new OtelTracer(realTracer);
    }
    startSpan(name, attributes) {
        return this.inner.startSpan(name, attributes);
    }
    startChildSpan(name, parent, attributes) {
        return this.inner.startChildSpan(name, parent, attributes);
    }
    recordCounter(name, value, attributes) {
        this.inner.recordCounter(name, value, attributes);
    }
    recordHistogram(name, value, attributes) {
        this.inner.recordHistogram(name, value, attributes);
    }
}
//# sourceMappingURL=otel-tracer.js.map