// packages/core-shared/src/instrumentation/otel/otel-logger.ts
import { logs, SeverityNumber } from "@opentelemetry/api-logs";
import { trace } from "@opentelemetry/api";
import { isReported, markReported } from "../reported-flag";
import type { ILogger, Breadcrumb, CaptureContext } from "../logger.interface";

export class OtelLogger implements ILogger {
  private readonly logger = logs.getLogger("@repo/core-shared", "1.0.0");

  captureException(err: unknown, ctx?: CaptureContext): void {
    if (isReported(err)) return;
    markReported(err);
    const error = err instanceof Error ? err : new Error(String(err));
    this.logger.emit({
      severityNumber: SeverityNumber.ERROR,
      severityText: "ERROR",
      body: error.message,
      attributes: {
        "exception.type": error.name,
        "exception.message": error.message,
        "exception.stacktrace": error.stack ?? "",
        ...flattenTags(ctx?.tags),
        ...flattenExtras(ctx?.extras),
        ...(ctx?.fingerprint ? { "sentry.fingerprint": ctx.fingerprint.join("|") } : {}),
      },
    });
  }

  captureMessage(msg: string, level?: "info" | "warning" | "error", ctx?: CaptureContext): void {
    const severityNumber =
      level === "error"
        ? SeverityNumber.ERROR
        : level === "warning"
          ? SeverityNumber.WARN
          : SeverityNumber.INFO;
    const severityText =
      level === "error" ? "ERROR" : level === "warning" ? "WARNING" : "INFO";
    this.logger.emit({
      severityNumber,
      severityText,
      body: msg,
      attributes: { ...flattenTags(ctx?.tags), ...flattenExtras(ctx?.extras) },
    });
  }

  addBreadcrumb(b: Breadcrumb): void {
    const span = trace.getActiveSpan();
    if (!span) return;
    span.addEvent(b.message, {
      "breadcrumb.category": b.category,
      "breadcrumb.level": b.level ?? "info",
      ...(b.data ? flattenExtras(b.data) : {}),
    });
  }

  setUser(user: { id: string } | null): void {
    const span = trace.getActiveSpan();
    if (!span) return;
    span.setAttribute("user.id", user?.id ?? "");
  }
}

function flattenTags(tags?: Record<string, string>): Record<string, string> {
  if (!tags) return {};
  return Object.fromEntries(Object.entries(tags).map(([k, v]) => [`tag.${k}`, v]));
}

function flattenExtras(extras?: Record<string, unknown>): Record<string, string> {
  if (!extras) return {};
  return Object.fromEntries(
    Object.entries(extras).map(([k, v]) => [
      `extra.${k}`,
      typeof v === "string" ? v : String(v),
    ]),
  );
}
