import type { MessageHandler, Transport, TransportMeta } from './base';

/**
 * Exact-match in-memory broker.
 * This models the "no wildcards on backbone" constraint.
 */
export class InMemoryExactBroker implements Transport {
  private subs: Map<string, Set<MessageHandler>> = new Map();

  publish(topic: string, payload: string, meta?: TransportMeta): void {
    const handlers = this.subs.get(topic);
    if (!handlers) return;
    // Snapshot to avoid mutation during dispatch.
    [...handlers].forEach((h) => h(topic, payload, meta));
  }

  subscribe(topic: string, handler: MessageHandler): () => void {
    const set = this.subs.get(topic) ?? new Set<MessageHandler>();
    set.add(handler);
    this.subs.set(topic, set);
    return () => {
      const cur = this.subs.get(topic);
      if (!cur) return;
      cur.delete(handler);
      if (cur.size === 0) this.subs.delete(topic);
    };
  }
}

// ===== Optional: filter broker for local buses =====

type FilterSub = { filter: string; handler: MessageHandler };

export function topicMatchesFilter(topic: string, filter: string): boolean {
  const t = topic.split('/');
  const f = filter.split('/');

  for (let i = 0; i < f.length; i++) {
    const fp = f[i];
    if (fp === '#') return true;
    if (i >= t.length) return false;
    if (fp === '+') continue;
    if (fp !== t[i]) return false;
  }
  return t.length === f.length;
}

/**
 * Wildcard filter broker (supports + and #).
 * Useful for local buses if you decide to allow wildcard fanout internally.
 */
export class InMemoryFilterBroker implements Transport {
  private subs: FilterSub[] = [];
  private taps: MessageHandler[] = [];

  publish(topic: string, payload: string, meta?: TransportMeta): void {
    let matched = false;
    for (const s of [...this.subs]) {
      if (topicMatchesFilter(topic, s.filter)) {
        matched = true;
        s.handler(topic, payload, meta);
      }
    }
    // Notify taps (unconditional observers) after subscriber dispatch
    for (const t of this.taps) t(topic, payload, meta);
  }

  subscribe(topicFilter: string, handler: MessageHandler): () => void {
    const sub: FilterSub = { filter: topicFilter, handler };
    this.subs.push(sub);
    return () => {
      this.subs = this.subs.filter((s) => s !== sub);
    };
  }

  /** Observe all messages unconditionally. Returns unsubscribe function. */
  tap(handler: MessageHandler): () => void {
    this.taps.push(handler);
    return () => { this.taps = this.taps.filter(t => t !== handler); };
  }
}
