/**
 * OpenTelemetry traces bootstrap (PR-28 / HARDENING_PLAN P3-5, May 2026).
 *
 * Lazy-initialised, opt-in via OTEL_TRACES_ENABLED=true. When enabled,
 * the SDK auto-instruments HTTP (incoming + outgoing), Prisma queries,
 * and Node http.fetch — those are the spans that matter for an uptime
 * monitor's hot paths.
 *
 * Exporter is OTLP/HTTP (the cross-vendor wire format). Operators
 * point it at any compatible backend by setting:
 *   OTEL_EXPORTER_OTLP_ENDPOINT  e.g. https://api.honeycomb.io
 *   OTEL_EXPORTER_OTLP_HEADERS   e.g. "x-honeycomb-team=KEY,x-honeycomb-dataset=uptime"
 *
 * Same OTLP collector works for Tempo (Grafana), Jaeger, Honeycomb,
 * Datadog OTLP, etc. — no vendor lock-in.
 *
 * Called from instrumentation.ts so it runs once at server boot.
 *
 * AUDIT-14 (2026-05-23): the SDK imports are dynamic, performed
 * inside startOtel() *after* the OTEL_TRACES_ENABLED check. Previously
 * the @opentelemetry/sdk-node + auto-instrumentations-node modules
 * were static imports at the top of this file — they resolved into
 * memory (~10-30 MB resident) on every boot regardless of whether
 * tracing was enabled, defeating the lazy-init intent.
 */

// NodeSDK's type used for the public signature only — imported lazily
// inside startOtel(). The bare-type import is erased at runtime so it
// does NOT pull in the module graph.
import type { NodeSDK } from '@opentelemetry/sdk-node';

let started = false;
let sdk: NodeSDK | undefined;

export function isOtelEnabled(): boolean {
    return process.env.OTEL_TRACES_ENABLED === 'true' || process.env.OTEL_TRACES_ENABLED === '1';
}

function parseHeaders(raw: string | undefined): Record<string, string> {
    if (!raw) return {};
    const out: Record<string, string> = {};
    for (const pair of raw.split(',')) {
        const i = pair.indexOf('=');
        if (i < 0) continue;
        out[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();
    }
    return out;
}

/**
 * Bring up the OTel SDK. Safe to call multiple times — subsequent
 * calls no-op. Returns the SDK instance for callers that want to
 * shutdown gracefully (the worker uses this on SIGTERM).
 *
 * The full SDK module graph is dynamically imported here so the
 * 5+ @opentelemetry/* packages stay unloaded when tracing is off.
 */
export async function startOtel(): Promise<NodeSDK | undefined> {
    if (started) return sdk;
    if (!isOtelEnabled()) return undefined;

    // Dynamic imports: only resolved when tracing is actually enabled.
    const [
        { NodeSDK },
        { OTLPTraceExporter },
        { getNodeAutoInstrumentations },
        { resourceFromAttributes },
        { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION },
    ] = await Promise.all([
        import('@opentelemetry/sdk-node'),
        import('@opentelemetry/exporter-trace-otlp-http'),
        import('@opentelemetry/auto-instrumentations-node'),
        import('@opentelemetry/resources'),
        import('@opentelemetry/semantic-conventions'),
    ]);

    const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT
        ?? 'http://localhost:4318/v1/traces';
    const headers = parseHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS);
    const serviceName = process.env.OTEL_SERVICE_NAME ?? 'uptime-sentinel';
    // App version is read from package.json at build time; fall back to "0.0.0".
    const serviceVersion = process.env.OTEL_SERVICE_VERSION ?? '0.0.0';

    sdk = new NodeSDK({
        resource: resourceFromAttributes({
            [ATTR_SERVICE_NAME]: serviceName,
            [ATTR_SERVICE_VERSION]: serviceVersion,
            'deployment.environment': process.env.NODE_ENV ?? 'development',
        }),
        traceExporter: new OTLPTraceExporter({
            url: endpoint.endsWith('/v1/traces') ? endpoint : `${endpoint.replace(/\/$/, '')}/v1/traces`,
            headers,
        }),
        instrumentations: [
            getNodeAutoInstrumentations({
                // The fs instrumentation is noisy and not useful for
                // a network-bound app; disable to keep traces focused
                // on what an operator actually needs to see.
                '@opentelemetry/instrumentation-fs': { enabled: false },
            }),
        ],
    });

    sdk.start();
    started = true;
    // eslint-disable-next-line no-console
    console.log(`[OTel] traces enabled (service=${serviceName} endpoint=${endpoint})`);
    return sdk;
}

export async function shutdownOtel(): Promise<void> {
    if (!sdk) return;
    try {
        await sdk.shutdown();
    } catch (err) {
        // eslint-disable-next-line no-console
        console.error('[OTel] shutdown failed:', err);
    }
}
