/**
 * Vendor-neutral observability hook surface.
 *
 * Shade's internals never depend on `@opentelemetry/api` directly — they
 * consume an `ObservabilityHook` through which spans are started/ended.
 * `withTracer()` adapts an OTel tracer to this hook; tests use
 * `createRecorder()` for in-memory inspection.
 */

export type AttrValue = string | number | boolean;
export type Attributes = Record<string, AttrValue>;

export interface Span {
  /** Set/overwrite a single attribute on this span. */
  setAttribute(key: string, value: AttrValue): void;
  /** Bulk-set attributes. */
  setAttributes(attrs: Attributes): void;
  /** Mark the span as failed with the given error. */
  recordException(err: unknown): void;
  /** Set the span status. */
  setStatus(status: 'ok' | 'error', message?: string): void;
  /** Close the span. Idempotent. */
  end(): void;
}

export interface ObservabilityHook {
  /** Start a new span. The returned object MUST always have `.end()` called. */
  startSpan(name: string, attrs?: Attributes): Span;
}

const NOOP_SPAN: Span = {
  setAttribute: () => undefined,
  setAttributes: () => undefined,
  recordException: () => undefined,
  setStatus: () => undefined,
  end: () => undefined,
};

export const NOOP_HOOK: ObservabilityHook = {
  startSpan: () => NOOP_SPAN,
};

/** Returned by `Span` factories that get sampled-out. */
export function noopSpan(): Span {
  return NOOP_SPAN;
}
