import { NodeSDK, metrics } from "@opentelemetry/sdk-node";
import { afterAll, assert, describe, expect, test } from "vitest";
import buildNodeInstrumentation from "../lib/instrumentation.node.js";

import {
  BatchLogRecordProcessor,
  SimpleLogRecordProcessor,
} from "@opentelemetry/sdk-logs";
import {
  BatchSpanProcessor,
  SimpleSpanProcessor,
} from "@opentelemetry/sdk-trace-base";
import buildHttpExporters from "../lib/exporter/http.js";
import { NodeSDKConfig } from "../lib/index.js";
import { EnrichLogProcessor } from "../lib/processor/enrich-logger-processor.js";
import { EnrichSpanProcessor } from "../lib/processor/enrich-span-processor.js";
import { PIIExporterDecorator } from "../lib/exporter/pii-exporter-decorator.js";

describe("verify config settings", () => {
  const commonConfig = {
    collectorUrl: "https://testurl.com",
    serviceName: "test",
  };

  test("grpc config", async () => {
    const config: NodeSDKConfig = {
      ...commonConfig,
      protocol: "grpc",
      collectorMode: "batch",
      diagLogLevel: "NONE",
    };

    const sdk: NodeSDK | undefined = await buildNodeInstrumentation(config);

    assert.ok(sdk);

    const _configuration = sdk["_configuration"];
    assert.equal(_configuration.serviceName, commonConfig.serviceName);

    const logs = _configuration.logRecordProcessors;

    assert.equal(logs.length, 1);
    assert.ok(logs[logs.length - 1] instanceof BatchLogRecordProcessor);

    const spans = _configuration.spanProcessors;

    assert.equal(spans.length, 1);
    assert.ok(spans[0] instanceof BatchSpanProcessor);

    assert.ok(
      _configuration.metricReader instanceof
        metrics.PeriodicExportingMetricReader,
    );
  });

  test("http config", async () => {
    const config: NodeSDKConfig = {
      ...commonConfig,
      protocol: "http",
      diagLogLevel: "NONE",
      spanAttributes: {
        name: "custom-value",
      },
    };

    const sdk: NodeSDK | undefined = await buildNodeInstrumentation(config);
    assert.ok(sdk);

    const _configuration = sdk["_configuration"];
    assert.equal(_configuration.serviceName, commonConfig.serviceName);

    const logs = _configuration.logRecordProcessors;

    assert.equal(logs.length, 2);
    assert.ok(logs[0] instanceof EnrichLogProcessor);
    assert.ok(logs[1] instanceof BatchLogRecordProcessor);

    const spans = _configuration.spanProcessors;

    assert.equal(spans.length, 2);
    assert.ok(spans[0] instanceof BatchSpanProcessor);
    assert.ok(spans[1] instanceof EnrichSpanProcessor);

    assert.ok(
      _configuration.metricReader instanceof
        metrics.PeriodicExportingMetricReader,
    );
  });

  test("console - console config", async () => {
    const config: NodeSDKConfig = {
      ...commonConfig,
      protocol: "console",
      diagLogLevel: "NONE",
      spanAttributes: {
        name: "custom-name",
      },
    };

    const sdk: NodeSDK = await buildNodeInstrumentation(config)!;
    assert.ok(sdk);

    const _configuration = sdk["_configuration"];
    assert.equal(_configuration.serviceName, commonConfig.serviceName);

    const logs = _configuration.logRecordProcessors;

    // verify simple log processor for instant console logging
    assert.equal(logs.length, 2);
    assert.ok(logs[0] instanceof EnrichLogProcessor);
    assert.ok(logs[1] instanceof SimpleLogRecordProcessor);

    const spans = _configuration.spanProcessors;

    assert.equal(spans.length, 2);
    // verify simple span for instant console logging
    assert.ok(spans[0] instanceof SimpleSpanProcessor);
    assert.ok(spans[1] instanceof EnrichSpanProcessor);

    assert.ok(
      _configuration.metricReader instanceof
        metrics.PeriodicExportingMetricReader,
    );
  });

  test("single log sending config", async () => {
    const config: NodeSDKConfig = {
      ...commonConfig,
      protocol: "grpc",
      diagLogLevel: "NONE",
      collectorMode: "single",
    };

    const sdk: NodeSDK | undefined = await buildNodeInstrumentation(config);

    assert.ok(sdk);

    const _configuration = sdk["_configuration"];

    const logRecordProcessors = _configuration.logRecordProcessors;
    assert.equal(logRecordProcessors.length, 1);
    assert.ok(
      logRecordProcessors[logRecordProcessors.length - 1] instanceof
        SimpleLogRecordProcessor,
    );
  });

  test("pii exporter decorator injected by default", async () => {
    const config: NodeSDKConfig = {
      ...commonConfig,
      protocol: "grpc",
      diagLogLevel: "NONE",
      collectorMode: "single",
      detection: {},
    };

    const sdk: NodeSDK | undefined = await buildNodeInstrumentation(config);

    assert.ok(sdk);

    const _configuration = sdk["_configuration"];

    const logRecordProcessors = _configuration.logRecordProcessors;
    assert.equal(logRecordProcessors.length, 1);
    assert.ok(
      logRecordProcessors[logRecordProcessors.length - 1] instanceof
        SimpleLogRecordProcessor,
    );
    assert.ok(
      logRecordProcessors[logRecordProcessors.length - 1][
        "_exporter"
      ] instanceof PIIExporterDecorator,
    );
  });

  test("check if clear base endpoint final slash", () => {
    const config: NodeSDKConfig = {
      collectorUrl: "http://example.com/",
    };

    buildHttpExporters(config);

    expect(config.collectorUrl).toBe("http://example.com");
  });

  afterAll(() => {
    // shutdown every instrumentation
    process.emit("SIGTERM");
  });
});
