import '../chunk-I2KLQ2HA.js';
import { fetchEventSource } from '@microsoft/fetch-event-source';

async function fetchEventSourceWithTokenRefresh(options) {
  const {
    url,
    getToken,
    headers: additionalHeaders,
    signal,
    tokenRefreshDelayMs = 2e3,
    onopen,
    onmessage,
    onerror,
    onclose
  } = options;
  let reconnectTimeout = null;
  let isActive = true;
  let controller = new AbortController();
  let shouldReconnect = false;
  let hasConnected = false;
  const cleanup = () => {
    isActive = false;
    shouldReconnect = false;
    controller.abort();
    if (reconnectTimeout) {
      clearTimeout(reconnectTimeout);
      reconnectTimeout = null;
    }
  };
  signal.addEventListener("abort", cleanup);
  const connect = async () => {
    if (!isActive || signal.aborted) {
      return;
    }
    shouldReconnect = false;
    const token = await getToken();
    const headers = {
      ...token ? { Authorization: `Bearer ${token}` } : {},
      ...additionalHeaders
    };
    try {
      await fetchEventSource(url, {
        method: "GET",
        headers,
        signal: controller.signal,
        async onopen(response) {
          if (response.ok && response.headers.get("content-type")?.includes("text/event-stream")) {
            if (!hasConnected) {
              hasConnected = true;
            }
            if (onopen) {
              await onopen(response);
            }
            return;
          }
          if (response.status === 401) {
            shouldReconnect = true;
            hasConnected = false;
            controller.abort();
            return;
          }
          throw new Error(`Unexpected response: ${response.status}`);
        },
        onmessage(event) {
          onmessage?.(event);
        },
        onerror(error) {
          if (error instanceof Error && error.name === "AbortError") {
            throw error;
          }
          onerror?.(error);
        },
        onclose() {
          onclose?.();
        }
      });
    } catch (error) {
      if (error instanceof Error && error.name === "AbortError" && shouldReconnect) {
        if (isActive && !reconnectTimeout && !signal.aborted) {
          reconnectTimeout = setTimeout(() => {
            controller = new AbortController();
            reconnectTimeout = null;
            connect();
          }, tokenRefreshDelayMs);
        }
        return;
      }
      if (error instanceof Error && error.name === "AbortError") {
        return;
      }
      throw error;
    }
  };
  await connect();
}

// src/sse/SSEConnectionManager.ts
var SSEConnectionManager = class {
  connections = /* @__PURE__ */ new Map();
  closeGracePeriodMs;
  constructor(options = {}) {
    this.closeGracePeriodMs = options.closeGracePeriodMs ?? 5e3;
  }
  /**
   * Subscribe to an SSE endpoint
   *
   * If a connection already exists for this endpoint, reuses it.
   * Otherwise, creates a new connection.
   *
   * @param key - Unique identifier for the connection (e.g., 'notifications', 'resource-executive-agent')
   * @param subscriberId - Unique identifier for this subscriber (usually component instance)
   * @param options - SSE connection options
   * @returns Unsubscribe function to call when component unmounts
   */
  subscribe(key, subscriberId, options) {
    const existing = this.connections.get(key);
    if (existing) {
      if (existing.closeTimeout) {
        clearTimeout(existing.closeTimeout);
        existing.closeTimeout = void 0;
      }
      existing.subscribers.set(subscriberId, options);
      if (existing.lastOpenResponse) {
        void options.onopen?.(existing.lastOpenResponse);
      }
    } else {
      const controller = new AbortController();
      const subscribers = /* @__PURE__ */ new Map();
      subscribers.set(subscriberId, options);
      const connection = {
        controller,
        subscribers,
        url: options.url
      };
      this.connections.set(key, connection);
      fetchEventSourceWithTokenRefresh({
        ...options,
        signal: controller.signal,
        async onopen(response) {
          connection.lastOpenResponse = response;
          await Promise.all(
            Array.from(connection.subscribers.values()).map((subscriberOptions) => subscriberOptions.onopen?.(response))
          );
        },
        onmessage(event) {
          connection.subscribers.forEach((subscriberOptions) => subscriberOptions.onmessage?.(event));
        },
        onerror(error) {
          connection.subscribers.forEach((subscriberOptions) => subscriberOptions.onerror?.(error));
        },
        onclose() {
          connection.subscribers.forEach((subscriberOptions) => subscriberOptions.onclose?.());
        }
      });
    }
    return () => this.unsubscribe(key, subscriberId);
  }
  /**
   * Unsubscribe from an SSE endpoint
   *
   * If this is the last subscriber, closes the connection after grace period.
   */
  unsubscribe(key, subscriberId) {
    const connection = this.connections.get(key);
    if (!connection) {
      return;
    }
    connection.subscribers.delete(subscriberId);
    if (connection.subscribers.size === 0 && !connection.closeTimeout) {
      connection.closeTimeout = setTimeout(() => {
        const current = this.connections.get(key);
        if (current && current.subscribers.size === 0) {
          this.closeConnection(key);
        }
      }, this.closeGracePeriodMs);
    }
  }
  /**
   * Force close a connection and all its subscribers
   */
  closeConnection(key) {
    const connection = this.connections.get(key);
    if (connection) {
      if (connection.closeTimeout) {
        clearTimeout(connection.closeTimeout);
      }
      connection.controller.abort();
      this.connections.delete(key);
    }
  }
  /**
   * Get current connection status
   */
  getConnectionInfo() {
    const info = /* @__PURE__ */ new Map();
    this.connections.forEach((connection, key) => {
      info.set(key, {
        url: connection.url,
        subscribers: connection.subscribers.size
      });
    });
    return info;
  }
};

export { SSEConnectionManager, fetchEventSourceWithTokenRefresh };
