import { BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import opentelemetry, { metrics, propagation } from '@opentelemetry/api';
import * as LogsAPI from '@opentelemetry/api-logs';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator, } from '@opentelemetry/core';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { HostMetrics } from '@opentelemetry/host-metrics';
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { BatchLogRecordProcessor, LoggerProvider, SimpleLogRecordProcessor, } from '@opentelemetry/sdk-logs';
import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { ConsoleLogRecordExporter, DebugSpanExporter } from './console';
import { instrumentFetch } from './fetch';
import { createLogger, patchConsole } from './logger';
import { JSONLLogExporter, JSONLTraceExporter, JSONLMetricExporter } from './exporters';
import { telemetry as telemetryGlobal } from './globals';
import { getServiceUrls } from '@agentuity/server';
const devmodeExportInterval = 1_000; // 1 second
const productionExportInterval = 10_000; // 10 seconds
export const createResource = (config) => {
    const { name, version, orgId, projectId, deploymentId, environment, devmode, cliVersion, sdkVersion, } = config;
    return resourceFromAttributes({
        [ATTR_SERVICE_NAME]: name,
        [ATTR_SERVICE_VERSION]: version,
        '@agentuity/orgId': orgId,
        '@agentuity/projectId': projectId,
        '@agentuity/deploymentId': deploymentId,
        '@agentuity/env': environment,
        '@agentuity/devmode': devmode,
        '@agentuity/sdkVersion': sdkVersion,
        '@agentuity/cliVersion': cliVersion,
    });
};
const createLoggerProvider = ({ url, headers, resource, jsonlBasePath, useConsoleExporters, logLevel: _logLevel, }) => {
    let processor;
    let exporter;
    if (useConsoleExporters) {
        processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter(true));
    }
    else if (jsonlBasePath) {
        exporter = new JSONLLogExporter(jsonlBasePath);
        processor = new BatchLogRecordProcessor(exporter);
    }
    else if (url) {
        const otlpExporter = new OTLPLogExporter({
            url: `${url}/v1/logs`,
            headers,
            compression: CompressionAlgorithm.GZIP,
            timeoutMillis: 10_000,
        });
        exporter = otlpExporter;
        processor = new BatchLogRecordProcessor(otlpExporter);
    }
    else {
        processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter(false));
    }
    const provider = new LoggerProvider({
        resource,
        processors: [processor],
    });
    LogsAPI.logs.setGlobalLoggerProvider(provider);
    return { processor, provider, exporter };
};
/**
 * Get configuration from environment variables
 */
function getConfigFromEnv() {
    const region = process.env.AGENTUITY_REGION ?? 'usc';
    const serviceUrls = getServiceUrls(region);
    return {
        name: process.env.AGENTUITY_APP_NAME ?? 'agentuity-app',
        version: process.env.AGENTUITY_APP_VERSION ?? '1.0.0',
        url: serviceUrls.otel,
        bearerToken: process.env.AGENTUITY_OTLP_BEARER_TOKEN ?? process.env.AGENTUITY_SDK_KEY ?? '',
        orgId: process.env.AGENTUITY_CLOUD_ORG_ID ?? 'unknown',
        projectId: process.env.AGENTUITY_CLOUD_PROJECT_ID ?? 'unknown',
        deploymentId: process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID ?? 'unknown',
        environment: process.env.AGENTUITY_ENVIRONMENT ?? process.env.NODE_ENV ?? 'development',
        cliVersion: process.env.AGENTUITY_CLI_VERSION ?? 'unknown',
        sdkVersion: process.env.AGENTUITY_CLOUD_SDK_VERSION ?? 'unknown',
        devmode: process.env.AGENTUITY_SDK_DEV_MODE === 'true',
        logLevel: 'warn',
        jsonlBasePath: process.env.AGENTUITY_CLOUD_EXPORT_DIR ?? '',
        spanProcessors: [],
    };
}
/**
 * Registers and initializes Telemetry with the specified configuration.
 *
 * Idempotent: if called again (e.g. during bun --hot reload), the previous
 * instance is shut down before creating a new one.
 *
 * @param config - Optional configuration overrides (defaults from env vars)
 * @returns An object containing the tracer, logger, and shutdown function
 */
export function registerTelemetry(config) {
    // Shut down previous instance if this is a hot reload
    const previous = telemetryGlobal.get();
    if (previous) {
        previous.shutdown().catch(() => { });
    }
    // Merge provided config with env defaults
    const envConfig = getConfigFromEnv();
    const mergedConfig = {
        name: config?.name ?? envConfig.name,
        version: config?.version ?? envConfig.version,
        url: config?.url ?? envConfig.url,
        bearerToken: config?.bearerToken ?? envConfig.bearerToken,
        orgId: config?.orgId ?? envConfig.orgId,
        projectId: config?.projectId ?? envConfig.projectId,
        deploymentId: config?.deploymentId ?? envConfig.deploymentId,
        environment: config?.environment ?? envConfig.environment,
        cliVersion: config?.cliVersion ?? envConfig.cliVersion,
        sdkVersion: config?.sdkVersion ?? envConfig.sdkVersion,
        devmode: config?.devmode ?? envConfig.devmode,
        logLevel: config?.logLevel ?? envConfig.logLevel,
        jsonlBasePath: config?.jsonlBasePath ?? envConfig.jsonlBasePath,
        spanProcessors: config?.spanProcessors ?? envConfig.spanProcessors,
    };
    const { url, name, version, bearerToken, environment, orgId, projectId, deploymentId, devmode, logLevel, jsonlBasePath, spanProcessors, } = mergedConfig;
    let headers;
    if (bearerToken) {
        headers = { Authorization: `Bearer ${bearerToken}` };
    }
    // Use console debug exporters for local debugging
    const useConsoleExporters = process.env.AGENTUITY_DEBUG_OTEL_CONSOLE === 'true';
    const resource = createResource(mergedConfig);
    const loggerProvider = createLoggerProvider({
        url,
        headers,
        resource,
        logLevel,
        jsonlBasePath: jsonlBasePath || undefined,
        useConsoleExporters,
    });
    const attrs = {
        '@agentuity/orgId': orgId,
        '@agentuity/projectId': projectId,
        '@agentuity/deploymentId': deploymentId,
        '@agentuity/env': environment,
        '@agentuity/devmode': devmode,
        '@agentuity/language': 'javascript',
    };
    const logger = createLogger(!!url, attrs, logLevel);
    // Don't patch console if using console exporters (avoid double logging)
    if (!useConsoleExporters) {
        patchConsole(!!url, attrs, logLevel);
    }
    // Build trace exporter (OTLP or JSONL)
    const traceExporter = jsonlBasePath
        ? new JSONLTraceExporter(jsonlBasePath)
        : url
            ? new OTLPTraceExporter({
                url: `${url}/v1/traces`,
                headers,
                keepAlive: true,
                compression: CompressionAlgorithm.GZIP,
            })
            : undefined;
    // Build metric exporter (OTLP or JSONL)
    const metricExporter = jsonlBasePath
        ? new JSONLMetricExporter(jsonlBasePath)
        : url
            ? new OTLPMetricExporter({
                url: `${url}/v1/metrics`,
                headers,
                keepAlive: true,
                compression: CompressionAlgorithm.GZIP,
            })
            : undefined;
    // Create span processors
    const allSpanProcessors = [];
    if (traceExporter) {
        allSpanProcessors.push(new BatchSpanProcessor(traceExporter));
    }
    if (useConsoleExporters) {
        allSpanProcessors.push(new SimpleSpanProcessor(new DebugSpanExporter()));
    }
    // Add custom span processors
    allSpanProcessors.push(...spanProcessors);
    // Create metric readers
    const sdkMetricReader = metricExporter
        ? new PeriodicExportingMetricReader({
            exporter: metricExporter,
            exportTimeoutMillis: devmode ? devmodeExportInterval : productionExportInterval,
            exportIntervalMillis: devmode ? devmodeExportInterval : productionExportInterval,
        })
        : undefined;
    const hostMetricReader = metricExporter
        ? new PeriodicExportingMetricReader({
            exporter: metricExporter,
            exportTimeoutMillis: devmode ? devmodeExportInterval : productionExportInterval,
            exportIntervalMillis: devmode ? devmodeExportInterval : productionExportInterval,
        })
        : undefined;
    const meterProvider = hostMetricReader
        ? new MeterProvider({ resource, readers: [hostMetricReader] })
        : undefined;
    if (meterProvider) {
        metrics.setGlobalMeterProvider(meterProvider);
    }
    const hostMetrics = meterProvider ? new HostMetrics({ meterProvider }) : undefined;
    let running = false;
    let instrumentationSDK;
    if (url || useConsoleExporters) {
        const propagator = new CompositePropagator({
            propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()],
        });
        propagation.setGlobalPropagator(propagator);
        instrumentFetch();
        instrumentationSDK = new NodeSDK({
            logRecordProcessor: loggerProvider.processor,
            metricReader: sdkMetricReader,
            instrumentations: [getNodeAutoInstrumentations()],
            resource,
            textMapPropagator: propagator,
            spanProcessors: allSpanProcessors,
        });
        instrumentationSDK.start();
        hostMetrics?.start();
        logger.debug('Telemetry configured successfully');
        logger.debug('Sending telemetry data to %s', url);
        running = true;
    }
    const tracer = opentelemetry.trace.getTracer(name, version);
    const meter = metrics.getMeter(name, version);
    const shutdown = async () => {
        if (running) {
            running = false;
            logger.debug('shutting down OpenTelemetry');
            await loggerProvider.provider
                .forceFlush()
                .catch((e) => logger.warn('error in forceFlush. %s', e));
            await loggerProvider.exporter
                ?.shutdown()
                .catch((e) => !devmode && logger.warn('error in shutdown of exporter. %s', e));
            await instrumentationSDK
                ?.shutdown()
                .catch((e) => !devmode && logger.warn('error in shutdown of instrumentation. %s', e));
            logger.debug('shut down OpenTelemetry');
        }
    };
    if (url && bearerToken) {
        logger.info('connected to Agentuity Agent Cloud');
    }
    const instance = { tracer, meter, logger, shutdown };
    telemetryGlobal.set(instance);
    return instance;
}
/**
 * Alias for registerTelemetry (shorter name)
 */
export const register = registerTelemetry;
/**
 * Get the current telemetry instance (or undefined if not initialized)
 */
export function getTelemetry() {
    return telemetryGlobal.get();
}
/**
 * Ensure telemetry is initialized (auto-init from env vars if needed)
 */
export function ensureInitialized() {
    let instance = telemetryGlobal.get();
    if (!instance) {
        instance = registerTelemetry();
    }
    return instance;
}
//# sourceMappingURL=telemetry.js.map