const DEFAULT_HEARTBEAT_MS = 25_000;
const DEFAULT_MAX_BUFFERED_BYTES = 1_048_576;
const MAX_BUFFERED_BYTES = 2_147_483_647;
const MAX_TIMER_MS = 2_147_483_647;

/**
 * One JSON-encoded Server-Sent Event.
 */
export interface ServerSentEventMessage<TData = unknown> {
  /** JSON-serializable event payload. */
  data: TData;
  /** Optional event type consumed by `EventSource.addEventListener(...)`. */
  event?: string;
  /** Optional event identifier used by browsers for `Last-Event-ID`. */
  id?: string;
  /** Optional browser reconnection delay in milliseconds. */
  retry?: number;
}

/**
 * Controls exposed while an SSE response is active.
 */
export interface ServerSentEventStream {
  /**
   * Aborts whenever this stream closes, including response cancellation.
   * Async setup should pass it to cancellable subscription APIs.
   */
  readonly signal: AbortSignal;
  /**
   * Send a JSON-encoded event.
   *
   * Returns `false` after the stream closes, when encoding fails, or when the
   * unread byte limit would be exceeded.
   */
  send<TData>(message: ServerSentEventMessage<TData>): boolean;
  /**
   * Send an SSE comment. Comments are useful as connection heartbeats.
   *
   * Returns `false` when the stream is no longer writable or its unread byte
   * limit would be exceeded.
   */
  comment(value?: string): boolean;
  /** Close the stream and run its cleanup exactly once. */
  close(): void;
}

/** Closeable subscription returned by an SSE stream's `start` callback. */
export interface ServerSentEventSubscription {
  /** Close the associated subscription. */
  close(): Promise<void> | void;
}

/** Cleanup returned by an SSE stream's `start` callback. */
export type ServerSentEventCleanup =
  | (() => Promise<void> | void)
  | ServerSentEventSubscription;

/** Options for `createServerSentEventResponse(...)`. */
export interface ServerSentEventResponseOptions {
  /**
   * Begin producing events. Return a cleanup callback or closeable
   * subscription for resources associated with this connection. Pending
   * asynchronous setup does not block response cancellation; honor the stream
   * signal and return cleanup when setup settles.
   */
  start(
    stream: ServerSentEventStream,
  ):
    | Promise<ServerSentEventCleanup | undefined>
    | ServerSentEventCleanup
    | undefined;
  /**
   * Abort the stream with the surrounding request or application lifecycle.
   */
  signal?: AbortSignal;
  /**
   * Interval for SSE heartbeat comments. Defaults to 25 seconds. Set to
   * `false` to disable heartbeats.
   */
  heartbeatMs?: number | false;
  /**
   * Close the connection after this duration so clients can reconnect and
   * reconcile. Disabled by default.
   */
  maxLifetimeMs?: number | false;
  /**
   * Maximum bytes of unread event data held by the response stream. Defaults
   * to 1 MiB. The connection closes when one frame or the accumulated queue
   * would exceed this limit.
   */
  maxBufferedBytes?: number;
  /** Additional response headers such as CORS or `Vary`. */
  headers?: HeadersInit;
  /**
   * Observe producer, serialization, buffer, stream, or cleanup failures.
   * Expected `AbortError` rejections caused by stream closure are ignored.
   */
  onError?(error: unknown): Promise<void> | void;
}

function assertTimerOption(
  name: "heartbeatMs" | "maxLifetimeMs",
  value: number | false | undefined,
): void {
  if (
    value === false ||
    value === undefined ||
    (Number.isInteger(value) && value >= 1 && value <= MAX_TIMER_MS)
  ) {
    return;
  }

  throw new RangeError(
    `${name} must be false or an integer between 1 and ${MAX_TIMER_MS} milliseconds.`,
  );
}

function assertMaxBufferedBytes(value: number): void {
  if (Number.isInteger(value) && value >= 1 && value <= MAX_BUFFERED_BYTES) {
    return;
  }

  throw new RangeError(
    `maxBufferedBytes must be an integer between 1 and ${MAX_BUFFERED_BYTES} bytes.`,
  );
}

function assertSingleLine(name: "event" | "id", value: string): void {
  if (value.includes("\r") || value.includes("\n")) {
    throw new TypeError(`SSE ${name} must not contain line breaks.`);
  }
  if (name === "id" && value.includes("\0")) {
    throw new TypeError("SSE id must not contain null characters.");
  }
}

function isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {
  return (
    value !== null &&
    (typeof value === "object" || typeof value === "function") &&
    "then" in value &&
    typeof value.then === "function"
  );
}

function isAbortError(error: unknown): boolean {
  try {
    return (
      typeof error === "object" &&
      error !== null &&
      "name" in error &&
      error.name === "AbortError"
    );
  } catch {
    return false;
  }
}

function encodeMessage<TData>(message: ServerSentEventMessage<TData>): string {
  if (message.event !== undefined) {
    assertSingleLine("event", message.event);
  }
  if (message.id !== undefined) {
    assertSingleLine("id", message.id);
  }
  if (
    message.retry !== undefined &&
    (!Number.isInteger(message.retry) ||
      message.retry < 0 ||
      message.retry > MAX_TIMER_MS)
  ) {
    throw new RangeError(
      `SSE retry must be an integer between 0 and ${MAX_TIMER_MS} milliseconds.`,
    );
  }

  const data = JSON.stringify(message.data);
  if (data === undefined) {
    throw new TypeError("SSE data must be JSON-serializable.");
  }

  const fields: string[] = [];
  if (message.event !== undefined) fields.push(`event: ${message.event}`);
  if (message.id !== undefined) fields.push(`id: ${message.id}`);
  if (message.retry !== undefined) fields.push(`retry: ${message.retry}`);
  fields.push(`data: ${data}`);
  return `${fields.join("\n")}\n\n`;
}

function encodeComment(value: string): string {
  return `${value
    .split(/\r\n|\r|\n/)
    .map((line) => `: ${line}`)
    .join("\n")}\n\n`;
}

function createResponseHeaders(init: HeadersInit | undefined): Headers {
  const headers = new Headers(init);
  headers.set("Content-Type", "text/event-stream; charset=utf-8");
  headers.set("Cache-Control", "no-store, no-transform");
  headers.set("X-Accel-Buffering", "no");
  headers.delete("Connection");
  headers.delete("Content-Length");
  headers.delete("Transfer-Encoding");
  return headers;
}

/**
 * Create a portable Fetch `Response` that safely manages a Server-Sent Events
 * stream.
 *
 * The helper owns SSE framing, JSON encoding, heartbeats, abort handling,
 * bounded unread buffering, maximum lifetime, and cleanup. Authentication,
 * replay, authorization, connection limits, and application reconciliation
 * remain the caller's responsibility.
 */
export function createServerSentEventResponse(
  options: ServerSentEventResponseOptions,
): Response {
  const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
  const maxBufferedBytes =
    options.maxBufferedBytes ?? DEFAULT_MAX_BUFFERED_BYTES;
  assertTimerOption("heartbeatMs", heartbeatMs);
  assertTimerOption("maxLifetimeMs", options.maxLifetimeMs);
  assertMaxBufferedBytes(maxBufferedBytes);
  const headers = createResponseHeaders(options.headers);

  const encoder = new TextEncoder();
  const lifecycleController = new AbortController();
  let cleanup: ServerSentEventCleanup | undefined;
  let cleanupPromise: Promise<void> | undefined;
  let closed = false;
  let heartbeatTimer: ReturnType<typeof setInterval> | undefined;
  let lifetimeTimer: ReturnType<typeof setTimeout> | undefined;
  let abortListener: (() => void) | undefined;
  let closeStream: (() => void) | undefined;

  const reportError = (error: unknown): void => {
    if (!options.onError) return;
    void Promise.resolve()
      .then(() => options.onError?.(error))
      .catch(() => undefined);
  };

  const runCleanup = (): Promise<void> => {
    if (cleanupPromise) return cleanupPromise;
    if (!cleanup) return Promise.resolve();
    const activeCleanup = cleanup;
    cleanupPromise = Promise.resolve()
      .then(() =>
        typeof activeCleanup === "function"
          ? activeCleanup()
          : activeCleanup.close(),
      )
      .catch(reportError);
    return cleanupPromise;
  };

  const stream = new ReadableStream<Uint8Array>(
    {
      start(controller) {
        const clearLifecycle = () => {
          if (heartbeatTimer !== undefined) clearInterval(heartbeatTimer);
          if (lifetimeTimer !== undefined) clearTimeout(lifetimeTimer);
          if (abortListener) {
            options.signal?.removeEventListener("abort", abortListener);
          }
        };

        const close = () => {
          if (closed) return;
          closed = true;
          clearLifecycle();
          lifecycleController.abort();
          void runCleanup();
          try {
            controller.close();
          } catch {
            // Cancellation may already have detached the stream controller.
          }
        };
        closeStream = close;

        const enqueue = (value: string): boolean => {
          if (closed) return false;
          try {
            const chunk = encoder.encode(value);
            const availableBytes = controller.desiredSize;
            if (availableBytes === null || chunk.byteLength > availableBytes) {
              reportError(
                new RangeError(
                  `SSE buffer limit of ${maxBufferedBytes} bytes exceeded.`,
                ),
              );
              close();
              return false;
            }
            controller.enqueue(chunk);
            return true;
          } catch (error) {
            reportError(error);
            close();
            return false;
          }
        };

        const controls: ServerSentEventStream = {
          signal: lifecycleController.signal,
          send(message) {
            if (closed) return false;
            try {
              return enqueue(encodeMessage(message));
            } catch (error) {
              reportError(error);
              close();
              return false;
            }
          },
          comment(value = "") {
            if (closed) return false;
            try {
              return enqueue(encodeComment(value));
            } catch (error) {
              reportError(error);
              close();
              return false;
            }
          },
          close,
        };

        if (options.signal?.aborted) {
          close();
          return;
        }

        abortListener = close;
        options.signal?.addEventListener("abort", abortListener, {
          once: true,
        });

        if (heartbeatMs !== false) {
          heartbeatTimer = setInterval(() => {
            controls.comment("heartbeat");
          }, heartbeatMs);
        }
        if (
          options.maxLifetimeMs !== undefined &&
          options.maxLifetimeMs !== false
        ) {
          lifetimeTimer = setTimeout(close, options.maxLifetimeMs);
        }

        const registerCleanup = (
          resolvedCleanup: ServerSentEventCleanup | undefined,
        ): void => {
          try {
            if (
              typeof resolvedCleanup === "function" ||
              (typeof resolvedCleanup === "object" &&
                resolvedCleanup !== null &&
                "close" in resolvedCleanup &&
                typeof resolvedCleanup.close === "function")
            ) {
              cleanup = resolvedCleanup;
              if (closed) void runCleanup();
            } else if (resolvedCleanup !== undefined) {
              reportError(
                new TypeError(
                  "SSE start must return a cleanup function, a closeable subscription, or nothing.",
                ),
              );
              close();
            }
          } catch (error) {
            reportError(error);
            close();
          }
        };

        const handleStartError = (error: unknown): void => {
          if (!(closed && isAbortError(error))) reportError(error);
          close();
        };

        try {
          const result = options.start(controls);
          if (isPromiseLike<ServerSentEventCleanup | undefined>(result)) {
            void Promise.resolve(result).then(
              registerCleanup,
              handleStartError,
            );
          } else {
            registerCleanup(result);
          }
        } catch (error) {
          handleStartError(error);
        }
      },
      cancel() {
        closeStream?.();
        return runCleanup();
      },
    },
    {
      highWaterMark: maxBufferedBytes,
      size: (chunk) => chunk?.byteLength ?? 0,
    },
  );

  return new Response(stream, {
    status: 200,
    headers,
  });
}
