import { Span } from '@opentelemetry/api';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { B3Propagator } from '@opentelemetry/propagator-b3';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';
import { ICreateSpan, IInitTracer } from '../types';

export const initTracer = ({ serviceName, exporterUrl, attributes }: IInitTracer) => {
  const exporter = new OTLPTraceExporter({
    url: exporterUrl,
  });
  const provider = new WebTracerProvider({
    resource: new Resource({
      [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
    }),
  });
  provider.addSpanProcessor(new BatchSpanProcessor(exporter));
  provider.register({
    contextManager: new ZoneContextManager(),
    propagator: new B3Propagator(),
  });

  const customAttributesDocumentLoad = (span: Span) => {
    span.setAttribute('http.path', attributes.path);
    span.setAttribute('user.uuid', attributes.uuid);
  };
  
  registerInstrumentations({
    instrumentations: [
      getWebAutoInstrumentations({
        '@opentelemetry/instrumentation-document-load': {
          applyCustomAttributesOnSpan: {
            documentLoad: customAttributesDocumentLoad,
          },
        },
        '@opentelemetry/instrumentation-xml-http-request': {
          propagateTraceHeaderCorsUrls: [/.+/g],
          clearTimingResources: true,
        },
        '@opentelemetry/instrumentation-fetch': {
          propagateTraceHeaderCorsUrls: [/.+/g],
          clearTimingResources: true,
        },
      }),
    ],
  });

  return provider.getTracer(serviceName);
};

export const createSpan = ({ tracer, name, func = () => {}, attributes, events }: ICreateSpan) => {
  return new Promise<void>((resolve, reject) => {
    tracer.startActiveSpan(name, async (span: Span) => {
      try {
        if (span.isRecording() && attributes) {
          for (const key in attributes) {
            if (attributes.hasOwnProperty(key)) {
              const value = attributes[key];
              span.setAttribute(key, value);
            }
          }
        }

        if (events) {
          span.addEvent(events.name, events.keyValue);
        }

        func();
        span.end();
        resolve();
      } catch (error) {
        reject(error);
      }
    });
  });
};