/**
 * Client-side inbox event emitter.
 *
 * The high-level `Inbox` orchestrator emits structural events — message
 * queued, delivered, polled, decrypted — so apps can drive UI badges,
 * push hooks, or telemetry without polling internal state.
 */

export interface InboxClientEventMap {
  /**
   * A new ciphertext entry was added to the outgoing queue. The push-hook
   * mentioned in the V3.6 spec dispatches off this event.
   */
  'inbox.message_queued': {
    recipientAddress: string;
    msgId: string;
    bytes: number;
    ttlSeconds: number;
  };
  /** Server confirmed a queued blob landed. */
  'inbox.message_delivered': {
    recipientAddress: string;
    msgId: string;
    idempotent: boolean;
  };
  /** Delivery attempt failed; will retry on next flush. */
  'inbox.message_failed': {
    recipientAddress: string;
    msgId: string;
    attempts: number;
    error: string;
  };
  /** A poll cycle completed and pulled `count` blobs. */
  'inbox.poll_completed': { ownAddress: string; count: number; cursor: number };
  /** Caller successfully decrypted and acked a blob. */
  'inbox.message_received': {
    senderHint: string | null;
    msgId: string;
  };
  /** Caller failed to decrypt — typically tampering or stale ratchet. */
  'inbox.message_decrypt_failed': {
    msgId: string;
    error: string;
  };
}

export type InboxClientEventName = keyof InboxClientEventMap;
export type InboxClientEvent = {
  [K in InboxClientEventName]: { name: K; data: InboxClientEventMap[K]; timestamp: number };
}[InboxClientEventName];

export type InboxClientListener = (e: InboxClientEvent) => void;

export class InboxClientEvents {
  private readonly listeners = new Set<InboxClientListener>();

  on(listener: InboxClientListener): () => void {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }

  emit<K extends InboxClientEventName>(name: K, data: InboxClientEventMap[K]): void {
    const event = { name, data, timestamp: Date.now() } as InboxClientEvent;
    for (const l of this.listeners) {
      try {
        l(event);
      } catch (err) {
        console.error('[Shade] Inbox client listener threw:', err);
      }
    }
  }
}
