import { Attributes } from "@opentelemetry/api";
import { BackgroundDispatcher } from "@zuplo/runtime";
import { Context } from "@opentelemetry/api";
import { ExportResult } from "@opentelemetry/core";
import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
import { RequestHandlerProxy } from "@zuplo/runtime";
import { Sampler } from "@opentelemetry/sdk-trace-base";
import { Span } from "@opentelemetry/api";
import { SpanExporter } from "@opentelemetry/sdk-trace-base";
import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
import { TelemetryInitializationOptions } from "@zuplo/runtime";
import { TelemetryPlugin } from "@zuplo/runtime";
import { TextMapPropagator } from "@opentelemetry/api";

declare type AcceptTraceContextFn = (request: Request) => boolean;

/**
 * Span processor that batches spans per trace and flushes them to the given
 * `OTLPSpanExporter` (or any `SpanExporter`). This is the Zuplo-aware
 * processor used internally; it is exported so consumers can register an
 * additional span destination via `TraceConfig.spanProcessors`.
 *
 * @public
 */
export declare class BatchTraceSpanProcessor implements TraceFlushableSpanProcessor {
  readonly exporter: SpanExporter;
  private traces;
  constructor(exporter: SpanExporter);
  getTraceState(traceId: string): TraceState;
  onStart(span: Span, parentContext: Context): void;
  onEnd(span: ReadableSpan): void;
  forceFlush(traceId?: TraceId): Promise<void>;
  shutdown(): Promise<void>;
}

declare interface FetcherConfig {
  includeTraceContext?: boolean | IncludeTraceContextFn;
}

declare interface FetchHandlerConfig {
  /**
   * Whether to enable context propagation for incoming requests to `fetch`.
   * This enables or disables distributed tracing from W3C Trace Context headers.
   * @default true
   */
  acceptTraceContext?: boolean | AcceptTraceContextFn;
}

declare interface HandlerConfig {
  fetch?: FetchHandlerConfig;
}

declare type HeadSamplerConf = Sampler | ParentRatioSamplingConfig;

declare type IncludeTraceContextFn = (request: Request) => boolean;

declare interface InstrumentationOptions {
  instrumentGlobalFetch?: boolean;
}

declare interface LocalTrace {
  readonly traceId: string;
  readonly localRootSpan: ReadableSpan;
  readonly spans: ReadableSpan[];
}

/**
 * OpenTelemetry plugin for distributed tracing in Zuplo.
 * Automatically instruments HTTP requests, policies, and handlers to provide
 * comprehensive observability data.
 *
 * Spans are sent to the Zuplo-managed trace ingest by default (no
 * configuration required) and/or to any OTLP destination(s) you configure.
 *
 * @public
 * @example
 * ```typescript
 * // zuplo.runtime.ts — send traces to Zuplo (the default)
 * import { RuntimeExtensions } from "@zuplo/runtime";
 * import { OpenTelemetryPlugin } from "@zuplo/otel";
 *
 * export function runtimeInit(runtime: RuntimeExtensions) {
 *   // No arguments required — `service.name` defaults to the project name.
 *   runtime.addPlugin(new OpenTelemetryPlugin());
 * }
 * ```
 *
 * @example
 * ```typescript
 * // Name the service explicitly; still defaults to the Zuplo exporter.
 * runtime.addPlugin(
 *   new OpenTelemetryPlugin({
 *     service: { name: "my-api" },
 *   })
 * );
 * ```
 *
 * @example
 * ```typescript
 * // Fan out to Zuplo and your own collector at the same time.
 * import {
 *   OpenTelemetryPlugin,
 *   OTLPSpanExporter,
 *   BatchTraceSpanProcessor,
 *   ZuploSpanExporter,
 * } from "@zuplo/otel";
 * import { environment } from "@zuplo/runtime";
 *
 * runtime.addPlugin(
 *   new OpenTelemetryPlugin({
 *     service: { name: "my-api" },
 *     spanProcessors: [
 *       new BatchTraceSpanProcessor(
 *         new OTLPSpanExporter({ url: environment.OTLP_ENDPOINT })
 *       ),
 *       new BatchTraceSpanProcessor(new ZuploSpanExporter()),
 *     ],
 *   })
 * );
 * ```
 *
 * @example
 * ```typescript
 * // Send only to your own OTLP collector (no Zuplo ingest).
 * import { OpenTelemetryPlugin } from "@zuplo/otel";
 * import { environment } from "@zuplo/runtime";
 *
 * runtime.addPlugin(
 *   new OpenTelemetryPlugin({
 *     service: { name: "my-api" },
 *     exporter: {
 *       url: environment.OTLP_ENDPOINT,
 *       headers: { authorization: `Bearer ${environment.OTEL_TOKEN}` },
 *     },
 *   })
 * );
 * ```
 */
export declare class OpenTelemetryPlugin extends TelemetryPlugin {
  private config;
  constructor(config?: TraceConfig);
  instrument(options: TelemetryInitializationOptions): {
    requestHandlerProxy: RequestHandlerProxy;
  };
}

/**
 * Configuration for an OTLP/HTTP exporter.
 *
 * Only the HTTP(S) protocol is supported. Use `headers` to pass
 * authentication (e.g. `{ authorization: "Bearer <token>" }`).
 *
 * @public
 */
export declare interface OTLPExporterConfig {
  url: string;
  headers?: Record<string, string>;
}

/**
 * Exports spans to an OTLP/HTTP endpoint as OTLP JSON. Pair with a
 * `BatchTraceSpanProcessor` and pass it via `TraceConfig.spanProcessors`
 * to send spans to an additional destination.
 *
 * @public
 */
export declare class OTLPSpanExporter implements SpanExporter {
  private headers;
  private url;
  constructor(config: OTLPExporterConfig);
  export(
    spans: ReadableSpan[],
    resultCallback: (result: ExportResult) => void
  ): void;
  shutdown(): Promise<void>;
  forceFlush?(): Promise<void>;
  dispatchFunction: (spans: ReadableSpan[]) => Promise<void>;
  batcher: BackgroundDispatcher<ReadableSpan>;
}

declare interface ParentRatioSamplingConfig {
  acceptRemote?: boolean;
  ratio: number;
}

declare type PostProcessorFn = (spans: ReadableSpan[]) => ReadableSpan[];

/* Excluded from this release type: RequestHandlerProxy */

declare interface SamplingConfig<HS extends HeadSamplerConf = HeadSamplerConf> {
  headSampler?: HS;
  tailSampler?: TailSampleFn;
}

declare interface ServiceConfig {
  name: string;
  namespace?: string;
  version?: string;
}

declare type TailSampleFn = (traceInfo: LocalTrace) => boolean;

/* Excluded from this release type: TelemetryInitializationOptions */

/**
 * Trace configuration with both log and trace exporter configurations.
 * The logUrl should be the URL for the logs OTel endpoint. The traceUrl
 * should be the URL for the traces OTel endpoint.
 *
 * Headers can be used for providing authentication information to the endpoint.
 *
 * Note that we only support HTTP(s) for transmitting OTel data.
 *
 * @public
 */
declare interface TraceAndLogConfigExporter extends TraceConfigBase {
  logUrl: string;
  traceUrl: string;
  headers: Record<string, string>;
}

/**
 * Trace configuration with both log and trace processors.
 *
 * Note that the BatchLogRecordProcessor is currently not compatible with
 * the Zuplo OTel library. The default log processor created by the
 * TraceAndLogConfigExporter is preferred over using this configuration.
 * @public
 */
declare interface TraceAndLogConfigProcessors extends TraceConfigBase {
  spanProcessors: SpanProcessor | SpanProcessor[];
  logProcessors: LogRecordProcessor | LogRecordProcessor[];
}

/**
 * Configuration for OpenTelemetry tracing in Zuplo.
 * Can be configured with either an explicit exporter or custom span processors.
 *
 * @public
 * @example
 * ```typescript
 * // Using OTLP exporter
 * const config: TraceConfig = {
 *   exporter: {
 *     url: "https://otel-collector.example.com/v1/traces",
 *     headers: { "api-key": "secret" }
 *   },
 *   service: {
 *     name: "my-api",
 *     version: "1.0.0"
 *   },
 *   // Optional post processor to modify spans before export
 *   postProcessor: (spans) => {
 *     for (const span of spans) {
 *       (
 *         span as unknown as { attributes: Record<string, unknown> }
 *       ).attributes["post.processed"] = true;
 *     }
 *     return spans;
 *   },
 *   sampling: {
 *     headSampler: {
 *       ratio: 0.1 // Sample 10% of requests
 *     }
 *   }
 * };
 *
 * // Using log and trace exporter. Note that logging is only enabled
 * // when *specifically* configured with its own endpoint.
 * const config: TraceConfig = {
 *   logUrl: "https://otel-collector.example.com/v1/logs",
 *   traceUrl: "https://otel-collector.example.com/v1/traces",
 *   headers: { "api-key": "secret" },
 *   service: {
 *     name: "my-api",
 *     version: "1.0.0"
 *   },
 *   sampling: {
 *     headSampler: {
 *       ratio: 0.1 // Sample 10% of requests
 *     }
 *   }
 * };
 *
 * // Using custom span processors
 * const config: TraceConfig = {
 *   spanProcessors: [
 *     new SimpleSpanProcessor(new ConsoleSpanExporter()),
 *     new BatchSpanProcessor(customExporter)
 *   ],
 *   service: {
 *     name: "my-api",
 *     namespace: "production"
 *   }
 * };
 * ```
 */
export declare type TraceConfig =
  | TraceConfigBase
  | TraceConfigExporter
  | TraceConfigSpanProcessors
  | TraceAndLogConfigExporter
  | TraceAndLogConfigProcessors;

/**
 * Base configuration for OpenTelemetry tracing
 * @public
 */
declare interface TraceConfigBase {
  /**
   * Service identity for the emitted spans. Optional — when omitted,
   * `service.name` defaults to the Zuplo project name.
   */
  service?: ServiceConfig;
  handlers?: HandlerConfig;
  fetch?: FetcherConfig;
  postProcessor?: PostProcessorFn;
  sampling?: SamplingConfig;
  propagator?: TextMapPropagator;
  instrumentation?: InstrumentationOptions;
  /**
   * Additional OpenTelemetry Resource attributes to attach to every span
   * emitted by this provider. Merged on top of the Zuplo-supplied resource
   * (service / cloud / build identifiers), so any key set here overrides the
   * default of the same name.
   *
   * Use this to tag spans with the attributes a downstream sink expects — for
   * example, when fanning out to a second OTLP destination that keys ingestion
   * on `zuplo.account_id` / `zuplo.project_id`:
   *
   * @example
   * ```typescript
   * const config: TraceConfig = {
   *   service: { name: "my-api" },
   *   spanProcessors: [
   *     new BatchTraceSpanProcessor(new OTLPSpanExporter(byoExporterConfig)),
   *     new BatchTraceSpanProcessor(new OTLPSpanExporter(zuploExporterConfig)),
   *   ],
   *   resourceAttributes: {
   *     "zuplo.account_id": "acme",
   *     "zuplo.project_id": "my-api",
   *   },
   * };
   * ```
   */
  resourceAttributes?: Attributes;
}

/**
 * Trace configuration with an explicit exporter for traces. The `exporter` may
 * be an {@link OTLPExporterConfig} (which is wrapped in a batching span
 * processor for you) or any OpenTelemetry `SpanExporter` instance — for
 * example `new ZuploSpanExporter()`.
 * @public
 */
declare interface TraceConfigExporter extends TraceConfigBase {
  exporter: OTLPExporterConfig | SpanExporter;
}

/**
 * Trace configuration with span processors
 * @public
 */
declare interface TraceConfigSpanProcessors extends TraceConfigBase {
  spanProcessors: SpanProcessor | SpanProcessor[];
}

declare type TraceFlushableSpanProcessor = SpanProcessor & {
  forceFlush: (traceId?: string) => Promise<void>;
};

declare type TraceId = string;

declare class TraceState {
  private unexportedSpans;
  private inprogressSpans;
  private exporter;
  private exportPromises;
  private localRootSpan?;
  private traceDecision?;
  isFlushed: boolean;
  constructor(exporter: SpanExporter);
  addSpan(span: Span): void;
  hasFinished(): boolean;
  endSpan(span: ReadableSpan): Promise<void>;
  sample(): void;
  flush(): Promise<void>;
  private isSpanInProgress;
  private exportSpans;
}

/**
 * Span exporter that ships spans to the Zuplo-managed trace ingest, where they
 * become queryable in the Zuplo portal. Add it like any other exporter — wrap
 * it in a {@link BatchTraceSpanProcessor} (or pass it as `exporter`) — to send
 * traces to Zuplo alongside any of your own destinations:
 *
 * @public
 * @example
 * ```typescript
 * new OpenTelemetryPlugin({
 *   service: { name: "my-api" },
 *   exporter: new ZuploSpanExporter(),
 * });
 * ```
 *
 * The ingest endpoint and credentials are resolved from the Zuplo runtime; no
 * configuration is required. If managed ingest is not available in the current
 * environment, the exporter throws a `ConfigurationError`.
 */
export declare class ZuploSpanExporter implements SpanExporter {
  private delegate;
  private resolveDelegate;
  export(
    spans: ReadableSpan[],
    resultCallback: (result: ExportResult) => void
  ): void;
  shutdown(): Promise<void>;
}

export {};
