/**
 * OpenTelemetry SDK bootstrap for the BFF (per ADR-0012).
 *
 * IMPORTANT — module load order
 * ─────────────────────────────
 * This file must be `import`ed (or `require`d) BEFORE any other module
 * that creates HTTP clients, DB clients, web frameworks, etc. The
 * auto-instrumentations work by patching modules at require time; they
 * miss anything already loaded into Node's module cache. In `main.ts`
 * we therefore have:
 *
 *     import './observability/tracing';   // first line, no other imports above it
 *     // ... rest of the bootstrap ...
 *
 * Anything else above that line will be silently un-instrumented.
 *
 * What this enables, out of the box
 * ─────────────────────────────────
 * - HTTP, Express, NestJS request spans (incoming and outgoing)
 * - PostgreSQL spans via Prisma (the underlying `pg` driver)
 * - Redis (`ioredis`) spans — wired in advance for ADR-0010 / ADR-0014
 * - Pino log records auto-decorated with the active `trace_id`/`span_id`
 *   so log lines correlate with traces in the backend
 *
 * Sampling is 100% at the app per ADR-0012; tail sampling is delegated
 * to the OTel Collector. Transport is OTLP HTTP/Protobuf (`port 4318`)
 * to match the .env.example default and avoid the gRPC binary
 * dependency (`@grpc/grpc-js`).
 */

import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';

const serviceName = process.env['OTEL_SERVICE_NAME'] ?? 'portal-bff';
const serviceVersion = process.env['OTEL_SERVICE_VERSION'] ?? 'dev';
const otlpEndpoint =
  process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ?? 'http://localhost:4318/v1/traces';

const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: serviceName,
    [ATTR_SERVICE_VERSION]: serviceVersion,
  }),
  traceExporter: new OTLPTraceExporter({ url: otlpEndpoint }),
  instrumentations: [
    new HttpInstrumentation(),
    new ExpressInstrumentation(),
    new NestInstrumentation(),
    new PgInstrumentation(),
    new IORedisInstrumentation(),
    new PinoInstrumentation(),
  ],
});

sdk.start();

// Graceful shutdown on container-stop signals so in-flight spans get a
// chance to flush to the collector. The 2s timeout is generous in dev
// and fast enough not to block container teardown in prod.
const shutdown = (signal: string): void => {
  sdk
    .shutdown()
    .catch((err) => {
      // Best-effort cleanup — log to stderr because Pino may itself
      // be in the middle of shutting down.
      process.stderr.write(`OpenTelemetry shutdown error on ${signal}: ${String(err)}\n`);
    })
    .finally(() => process.exit(0));
};

process.once('SIGTERM', () => shutdown('SIGTERM'));
process.once('SIGINT', () => shutdown('SIGINT'));
