import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
import type { ITracer, ISpan, SpanOpts } from "../tracer.interface";

export class OtelTracer implements ITracer {
  private readonly tracer = trace.getTracer("@repo/core-shared", "1.0.0");

  async startSpan<T>(opts: SpanOpts, fn: (span: ISpan) => Promise<T>): Promise<T> {
    const attributes: Record<string, string | number | boolean> = {
      ...(opts.attributes
        ? (Object.fromEntries(
            Object.entries(opts.attributes).filter(([, v]) => v !== null),
          ) as Record<string, string | number | boolean>)
        : {}),
      ...(opts.op ? { "span.op": opts.op } : {}),
    };

    return this.tracer.startActiveSpan(
      opts.name,
      { kind: SpanKind.INTERNAL, attributes },
      async (otelSpan) => {
        const adapter: ISpan = {
          setAttribute(key, value) {
            if (value !== null) {
              otelSpan.setAttribute(key, value as string | number | boolean);
            }
          },
          setStatus(status, message) {
            otelSpan.setStatus({
              code: status === "ok" ? SpanStatusCode.OK : SpanStatusCode.ERROR,
              message,
            });
          },
        };
        try {
          return await fn(adapter);
        } catch (err) {
          otelSpan.recordException(err as Error);
          otelSpan.setStatus({ code: SpanStatusCode.ERROR });
          throw err;
        } finally {
          otelSpan.end();
        }
      },
    );
  }
}
