// src/engine/mailbox/MailboxFactory.ts

import type { ITransportAdapter } from '../remoting/ITransportAdapter';
import type { IMailbox, IMailboxOptions } from './IMailbox';
import { Mailbox } from './Mailbox';

/**
 * Factory options for creating mailboxes.
 */
export interface IMailboxFactoryOptions {
  /**
   * Default options applied to all created mailboxes.
   * Individual mailbox options override these.
   */
  defaultMailboxOptions?: IMailboxOptions;
}

/**
 * Factory for creating mailboxes.
 *
 * Responsibilities:
 * - Creates mailboxes with unique addresses
 * - Prevents duplicate addresses (throws if exists)
 * - Tracks all created mailboxes for lifecycle management
 * - Provides cleanup via closeAll()
 *
 * Usage:
 * ```typescript
 * const factory = new MailboxFactory(transport);
 * const mailbox = factory.create('actor.$inbox');
 * mailbox.claim();
 * ```
 */
export class MailboxFactory {
  private readonly mailboxes = new Map<string, Mailbox>();
  private readonly defaultOptions: IMailboxOptions;
  private _totalCreated = 0;

  constructor(
    private readonly transport: ITransportAdapter,
    options: IMailboxFactoryOptions = {}
  ) {
    this.defaultOptions = options.defaultMailboxOptions ?? {};
  }

  /**
   * Create a new mailbox with the given address.
   *
   * Address convention: {mount}.$inbox
   * Example: "counter-1.$inbox"
   *
   * @param address - Unique mailbox address
   * @param options - Mailbox options (override factory defaults)
   * @returns New mailbox (not yet claimed)
   * @throws Error if address already exists
   */
  create(address: string, options?: IMailboxOptions): IMailbox {
    if (this.mailboxes.has(address)) {
      throw new Error(`[MailboxFactory] Mailbox already exists: ${address}`);
    }

    const mergedOptions: IMailboxOptions = {
      ...this.defaultOptions,
      ...options,
      backpressure: {
        ...this.defaultOptions.backpressure,
        ...options?.backpressure,
      },
    };

    const mailbox = new Mailbox(address, this.transport, mergedOptions);
    this.mailboxes.set(address, mailbox);
    this._totalCreated++;

    return mailbox;
  }

  /**
   * Get an existing mailbox by address.
   * Returns undefined if not found.
   */
  get(address: string): IMailbox | undefined {
    return this.mailboxes.get(address);
  }

  /**
   * Check if a mailbox exists at the given address.
   */
  has(address: string): boolean {
    return this.mailboxes.has(address);
  }

  /**
   * Remove and close a mailbox.
   * Safe to call if mailbox doesn't exist.
   */
  remove(address: string): void {
    const mailbox = this.mailboxes.get(address);
    if (mailbox) {
      mailbox.close();
      this.mailboxes.delete(address);
    }
  }

  /**
   * Close all mailboxes and clear the factory.
   * Called during runtime shutdown.
   */
  closeAll(): void {
    for (const mailbox of this.mailboxes.values()) {
      try {
        mailbox.close();
      } catch (err) {
        // eslint-disable-next-line no-console -- kernel mailbox factory has no actor context when closeAll cleanup fails
        console.error(`[MailboxFactory] Error closing mailbox ${mailbox.address}:`, err);
      }
    }
    this.mailboxes.clear();
  }

  /**
   * Get count of active mailboxes.
   */
  get count(): number {
    return this.mailboxes.size;
  }

  /**
   * List all mailbox addresses (for debugging/introspection).
   */
  listAddresses(): string[] {
    return [...this.mailboxes.keys()];
  }

  /**
   * Get mailbox statistics (detailed).
   */
  getStats(): {
    count: number;
    claimed: number;
    closed: number;
    totalDepth: number;
  } {
    let claimed = 0;
    let closed = 0;
    let totalDepth = 0;

    for (const mailbox of this.mailboxes.values()) {
      if (mailbox.claimed) claimed++;
      if (mailbox.closed) closed++;
      totalDepth += mailbox.depth;
    }

    return {
      count: this.mailboxes.size,
      claimed,
      closed,
      totalDepth,
    };
  }

  /**
   * Get simplified stats (for common use).
   */
  stats(): { active: number; totalCreated: number } {
    return {
      active: this.mailboxes.size,
      totalCreated: this._totalCreated,
    };
  }
}
