/**
 * Custom SSEServerTransport that supports session recreation.
 *
 * When recreating a session from Redis (e.g., in serverless environments
 * or after client reconnection), we need to restore the transport's state
 * including the event ID counter for proper SSE reconnection support.
 *
 * This class extends our custom SSEServerTransport to expose a public API
 * for session recreation, maintaining the event ID sequence across reconnections.
 */
import type { ServerResponse } from 'http';

import { SSEServerTransport, type SSEServerTransportOptions } from './base-sse-transport';

export interface RecreateableSSEServerTransportOptions extends SSEServerTransportOptions {
  /**
   * Initial event ID counter value for session recreation.
   * Use this when recreating a session to restore the event ID sequence.
   */
  initialEventId?: number;
}

/**
 * SSEServerTransport with session recreation support.
 *
 * This is a drop-in replacement for SSEServerTransport that adds the ability
 * to recreate a session with preserved state. This is essential for:
 * - Serverless environments where the transport may be evicted from memory
 * - Client reconnection scenarios where event ID continuity is required
 * - Distributed systems where sessions are stored in Redis
 *
 * It extends SSEServerTransport to maintain full compatibility while
 * adding public methods to set initialization state and restore event IDs.
 */
export class RecreateableSSEServerTransport extends SSEServerTransport {
  private _isRecreatedSession = false;

  constructor(endpoint: string, res: ServerResponse, options?: RecreateableSSEServerTransportOptions) {
    super(endpoint, res, options);

    // If initialEventId is provided and valid, restore the event counter
    if (options?.initialEventId !== undefined && this.isValidEventId(options.initialEventId)) {
      this.setEventIdCounter(options.initialEventId);
      this._isRecreatedSession = true;
    }
  }

  /**
   * Validates that an event ID is a valid non-negative integer.
   * Protects against negative values, NaN, Infinity, and non-integer values.
   */
  private isValidEventId(eventId: number): boolean {
    return Number.isInteger(eventId) && eventId >= 0 && eventId <= Number.MAX_SAFE_INTEGER;
  }

  /**
   * Returns whether this is a recreated session.
   */
  get isRecreatedSession(): boolean {
    return this._isRecreatedSession;
  }

  /**
   * Returns the current event ID counter value.
   * Alias for lastEventId for consistency with the recreation API.
   */
  get eventIdCounter(): number {
    return this.lastEventId;
  }

  /**
   * Sets the event ID counter for session recreation.
   * Use this when recreating a session from stored state to maintain
   * event ID continuity for SSE reconnection support.
   *
   * @param eventId - The event ID to restore (must be a non-negative integer)
   */
  override setEventIdCounter(eventId: number): void {
    // Security: Validate event ID to prevent invalid values
    if (!this.isValidEventId(eventId)) {
      console.warn(
        `[RecreateableSSEServerTransport] Invalid eventId: ${eventId}. ` + `Must be a non-negative integer. Ignoring.`,
      );
      return;
    }
    // Routes through the base class's protected setter instead of an `as any`
    // cast — keeps the type system honest and survives upstream refactors of
    // the private field.
    super.setEventIdCounter(eventId);
  }

  /**
   * Sets the transport to a recreated session state.
   * Use this when recreating a transport from a stored session.
   *
   * @param sessionId - The session ID (for verification, should match constructor)
   * @param lastEventId - The last event ID that was sent to the client
   */
  setSessionState(sessionId: string, lastEventId?: number): void {
    // Verify session ID matches (or set it if the transport allows)
    if (this.sessionId !== sessionId) {
      // Session IDs are credentials — log only a short prefix.
      const redact = (id: string): string => `${id.slice(0, 8)}…`;
      console.warn(
        `RecreateableSSEServerTransport: session ID mismatch. ` +
          `Expected ${redact(this.sessionId)}, got ${redact(sessionId)}. Using constructor value.`,
      );
    }

    if (lastEventId !== undefined) {
      this.setEventIdCounter(lastEventId);
    }

    this._isRecreatedSession = true;
  }
}
