/**
 * Low-level devtools client and tracer wiring over the current `Socket`.
 *
 * `DevToolsClient` speaks the devtools NDJSON protocol used by the unstable
 * devtools integration. It sends span starts, span events, span completions,
 * ping messages, and metric snapshots through a socket, then exposes tracer
 * layers that forward telemetry while preserving the current tracer's behavior.
 *
 * **Mental model**
 *
 * {@link make} creates the scoped client service over the provided `Socket`;
 * {@link makeTracer} wraps the current tracer and sends each span update
 * through that client; {@link layerTracer} combines both steps for integrations
 * that already have a socket transport. The higher-level `DevTools` module
 * provides WebSocket defaults for applications.
 *
 * **Common tasks**
 *
 * - Build the client service directly with {@link make} or {@link layer}
 * - Install only the forwarding tracer with {@link makeTracer}
 * - Create the client and tracer together with {@link layerTracer}
 * - Send custom span or span-event messages through `DevToolsClient.sendUnsafe`
 *
 * **Gotchas**
 *
 * - The client is scoped because it starts background fibers for the socket
 *   stream and heartbeat.
 * - `sendUnsafe` enqueues telemetry without back pressure and should stay on
 *   the tracer hot path.
 * - This module does not create a socket transport; provide `Socket` yourself
 *   or use the higher-level `DevTools` module.
 *
 * @since 4.0.0
 */
import * as Cause from "../../Cause.js";
import * as Context from "../../Context.js";
import * as Deferred from "../../Deferred.js";
import * as Effect from "../../Effect.js";
import * as Fiber from "../../Fiber.js";
import * as Layer from "../../Layer.js";
import * as Metric from "../../Metric.js";
import * as Queue from "../../Queue.js";
import * as Schema from "../../Schema.js";
import * as Stream from "../../Stream.js";
import * as Tracer from "../../Tracer.js";
import * as Ndjson from "../encoding/Ndjson.js";
import * as Socket from "../socket/Socket.js";
import * as DevToolsSchema from "./DevToolsSchema.js";
const RequestSchema = /*#__PURE__*/Schema.toCodecJson(DevToolsSchema.Request);
const ResponseSchema = /*#__PURE__*/Schema.toCodecJson(DevToolsSchema.Response);
/**
 * Service for sending span and span-event telemetry to the Effect devtools
 * connection.
 *
 * @category services
 * @since 4.0.0
 */
export class DevToolsClient extends /*#__PURE__*/Context.Service()("effect/devtools/DevToolsClient") {}
const makeEffect = /*#__PURE__*/Effect.gen(function* () {
  const socket = yield* Socket.Socket;
  const services = yield* Effect.context();
  const requests = yield* Queue.unbounded();
  const connected = yield* Deferred.make();
  const offerMetricsSnapshot = Effect.sync(() => {
    Queue.offerUnsafe(requests, toMetricsSnapshot(services));
  });
  const handleResponse = response => {
    switch (response._tag) {
      case "MetricsRequest":
        {
          return offerMetricsSnapshot;
        }
      case "Pong":
        {
          return Effect.void;
        }
    }
  };
  const fiber = yield* Stream.fromQueue(requests).pipe(Stream.pipeThroughChannel(Ndjson.duplexSchemaString(Socket.toChannelString(socket), {
    inputSchema: RequestSchema,
    outputSchema: ResponseSchema
  })), Stream.onFirst(() => Deferred.completeWith(connected, Effect.void)), Stream.runForEach(handleResponse), Effect.forkDetach);
  yield* Effect.addFinalizer(() => offerMetricsSnapshot.pipe(Effect.andThen(Effect.flatMap(Effect.fiberId, id => Queue.failCause(requests, Cause.interrupt(id)))), Effect.andThen(Fiber.await(fiber))));
  yield* Effect.suspend(() => Queue.offer(requests, {
    _tag: "Ping"
  })).pipe(Effect.delay("3 seconds"), Effect.forever, Effect.forkScoped);
  yield* Deferred.await(connected).pipe(Effect.timeoutOption("1 second"), Effect.asVoid);
  return DevToolsClient.of({
    sendUnsafe(request) {
      Queue.offerUnsafe(requests, request);
    }
  });
});
const toMetricsSnapshot = context => ({
  _tag: "MetricsSnapshot",
  metrics: Metric.snapshotUnsafe(context)
});
/**
 * Creates a devtools client over the current `Socket`, speaking the devtools
 * NDJSON protocol, sending periodic pings, and responding to metrics snapshot
 * requests.
 *
 * **When to use**
 *
 * Use when you already have a `Socket` and need the low-level `DevToolsClient`
 * service to exchange devtools telemetry directly.
 *
 * **Details**
 *
 * The effect requires `Scope` because it starts background fibers for the socket
 * stream and heartbeat.
 *
 * **Gotchas**
 *
 * `make` creates only the client service; tracing is installed separately.
 *
 * @see {@link layer} for providing the client as a layer
 * @see {@link makeTracer} for creating a tracer after a `DevToolsClient` is available
 * @see {@link layerTracer} for creating the client from the current `Socket` and installing the tracer as a layer
 *
 * @category constructors
 * @since 4.0.0
 */
export const make = /*#__PURE__*/makeEffect.pipe(/*#__PURE__*/Effect.annotateLogs({
  module: "DevTools",
  service: "Client"
}));
/**
 * Layer that provides `DevToolsClient` using the current `Socket`.
 *
 * **When to use**
 *
 * Use to provide the low-level `DevToolsClient` service over an existing
 * `Socket` for custom devtools integrations that send telemetry through the
 * client directly.
 *
 * **Details**
 *
 * It delegates to `make`, so it speaks the devtools NDJSON protocol over the
 * provided `Socket`, sends periodic pings, responds to metrics snapshot
 * requests, and finalizes its background fibers when the layer scope closes.
 *
 * **Gotchas**
 *
 * This layer only provides the client. It does not install the devtools tracer
 * by itself.
 *
 * @see {@link make} for constructing the client as a scoped effect instead of a layer
 * @see {@link layerTracer} for a higher-level layer that creates the client and installs the devtools tracer
 *
 * @category layers
 * @since 4.0.0
 */
export const layer = /*#__PURE__*/Layer.effect(DevToolsClient, make);
const makeTracerEffect = /*#__PURE__*/Effect.gen(function* () {
  const client = yield* DevToolsClient;
  const currentTracer = yield* Effect.tracer;
  return Tracer.make({
    span(options) {
      const span = currentTracer.span(options);
      client.sendUnsafe(span);
      const oldEvent = span.event;
      span.event = function (name, startTime, attributes) {
        client.sendUnsafe({
          _tag: "SpanEvent",
          traceId: span.traceId,
          spanId: span.spanId,
          name,
          startTime,
          attributes
        });
        return oldEvent.call(this, name, startTime, attributes);
      };
      const oldEnd = span.end;
      span.end = function (endTime, exit) {
        oldEnd.call(this, endTime, exit);
        client.sendUnsafe(span);
      };
      return span;
    },
    context: currentTracer.context
  });
});
/**
 * Creates a tracer that delegates to the current tracer while sending span
 * starts, span events, and span ends to `DevToolsClient`.
 *
 * @category constructors
 * @since 4.0.0
 */
export const makeTracer = /*#__PURE__*/makeTracerEffect.pipe(/*#__PURE__*/Effect.annotateLogs({
  module: "DevTools",
  service: "Tracer"
}));
/**
 * Layer that creates a `DevToolsClient` from the current `Socket` and installs
 * the devtools tracer.
 *
 * @category layers
 * @since 4.0.0
 */
export const layerTracer = /*#__PURE__*/Layer.effect(Tracer.Tracer, makeTracer).pipe(/*#__PURE__*/Layer.provide(layer));
//# sourceMappingURL=DevToolsClient.js.map