import type { NodeSDK } from "@opentelemetry/sdk-node";
import { test, describe, assert, vi, expect } from "vitest";

import buildNodeInstrumentation from "../lib/instrumentation.node.js";
import { instrumentNode } from "../index.js";

describe("validation config: should return without breaking the execution", () => {
  test("should call buildNodeInstrumentation without config and skip instrumentation", async () => {
    const consoleWarnSpy = vi
      .spyOn(console, "warn")
      .mockImplementation(vi.fn());

    await instrumentNode();

    expect(consoleWarnSpy).toHaveBeenCalled();
    expect(consoleWarnSpy).toHaveBeenCalledWith(
      "observability config not set. Skipping NodeJS OpenTelemetry instrumentation.",
    );
  });

  test("node instrumentation: url undefined", async () => {
    let sdk: NodeSDK | undefined = undefined;

    const consoleWarnSpy = vi
      .spyOn(console, "warn")
      .mockImplementation(vi.fn());
    const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(vi.fn());

    await expect(
      buildNodeInstrumentation({
        collectorUrl: undefined,
      }).then((result) => {
        sdk = result;
      }),
    ).resolves.not.toThrowError();

    assert.equal(sdk, undefined);

    expect(consoleWarnSpy).toHaveBeenCalled();
    expect(consoleWarnSpy).toHaveBeenCalledWith(
      "collectorUrl not set. Skipping NodeJS OpenTelemetry instrumentation.",
    );
    expect(consoleLogSpy).not.toHaveBeenCalled();
  });

  test("node instrumentation: invalid url", async () => {
    let sdk: NodeSDK | undefined = undefined;

    const consoleErrorSpy = vi
      .spyOn(console, "error")
      .mockImplementation(vi.fn());
    const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(vi.fn());

    await expect(
      buildNodeInstrumentation({
        collectorUrl: "notavalidURL",
      }).then((result) => {
        sdk = result;
      }),
    ).resolves.not.toThrowError();

    assert.equal(sdk, undefined);

    expect(consoleErrorSpy).toHaveBeenCalled();
    expect(consoleErrorSpy).toHaveBeenCalledWith(
      "collectorUrl does not use a valid format. Skipping NodeJS OpenTelemetry instrumentation.",
    );
    expect(consoleLogSpy).not.toHaveBeenCalled();
  });

  test("node instrumentation: verify no instrumentation if exception occurs", async () => {
    let sdk: NodeSDK | undefined = undefined;

    const consoleErrorSpy = vi
      .spyOn(console, "error")
      .mockImplementation(vi.fn());
    const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(vi.fn());

    vi.mock("@opentelemetry/auto-instrumentations-node", () => ({
      getNodeAutoInstrumentations: vi.fn(() => {
        throw new Error("Wanted error");
      }),
    }));

    await expect(
      buildNodeInstrumentation({
        collectorUrl: "https://testurl.com",
        serviceName: "test",
      }).then((result) => {
        sdk = result;
      }),
    ).resolves.not.toThrowError();

    assert.equal(sdk, undefined);

    expect(consoleErrorSpy).toHaveBeenCalled();
    expect(consoleErrorSpy).toHaveBeenCalledWith(
      "Error starting NodeJS OpenTelemetry instrumentation:",
      new Error("Wanted error"),
    );
    expect(consoleLogSpy).not.toHaveBeenCalled();
  });
});
