import type {
  Attributes,
  AttributeValue,
  Exception,
  Link,
  Span,
  SpanContext,
  SpanStatus,
  TimeInput,
} from "@opentelemetry/api";

import { mask } from "./maskingRules";
import type { TraceConfig } from "./types";

/**
 * A wrapper around the OpenTelemetry {@link Span} interface that masks sensitive information based on the passed in {@link TraceConfig}.
 */
export class OISpan implements Span {
  private readonly span: Span;
  private readonly config: TraceConfig;

  constructor({ span, config }: { span: Span; config: TraceConfig }) {
    this.span = span;
    this.config = config;
  }

  setAttribute(key: string, value: AttributeValue): this {
    const maskedValue = mask({ config: this.config, key, value });
    if (maskedValue != null) {
      this.span.setAttribute(key, maskedValue);
    }
    return this;
  }

  setAttributes(attributes: Attributes): this {
    const maskedAttributes = Object.entries(attributes).reduce((maskedAttributes, [key, value]) => {
      maskedAttributes[key] = mask({ config: this.config, key, value });
      return maskedAttributes;
    }, {} as Attributes);
    this.span.setAttributes(maskedAttributes);
    return this;
  }

  addEvent(
    name: string,
    attributesOrStartTime?: Attributes | TimeInput,
    startTime?: TimeInput,
  ): this {
    this.span.addEvent(name, attributesOrStartTime, startTime);
    return this;
  }

  setStatus(status: SpanStatus): this {
    this.span.setStatus(status);
    return this;
  }

  updateName(name: string): this {
    this.span.updateName(name);
    return this;
  }

  end(endTime?: TimeInput): void {
    this.span.end(endTime);
  }

  isRecording(): boolean {
    return this.span.isRecording();
  }

  recordException(exception: Exception, time?: TimeInput): void {
    this.span.recordException(exception, time);
  }

  spanContext(): SpanContext {
    return this.span.spanContext();
  }

  addLink(link: Link): this {
    this.span.addLink(link);
    return this;
  }

  addLinks(links: Link[]): this {
    this.span.addLinks(links);
    return this;
  }
}
