import {
  EVENTS_MODULE_OPTIONS
} from "./chunk-H5NH7KPE.js";
import {
  clampEventLimit,
  decodeEventCursor,
  encodeEventCursor
} from "./chunk-UQ5EHOH2.js";
import {
  __decorateClass,
  __decorateParam
} from "./chunk-2E224ZSN.js";

// runtime/subsystems/events/event-bus.memory-backend.ts
import { Inject, Injectable, Logger, Optional } from "@nestjs/common";
function toEventSummary(event) {
  const metadata = event.metadata;
  const str = (key) => {
    const v = metadata?.[key];
    return typeof v === "string" ? v : null;
  };
  return {
    id: event.id,
    type: event.type,
    aggregateId: event.aggregateId,
    aggregateType: event.aggregateType,
    status: "processed",
    pool: str("pool"),
    direction: str("direction"),
    tier: str("tier") ?? "domain",
    rootRunId: str("rootRunId"),
    tenantId: str("tenantId"),
    occurredAt: event.occurredAt,
    processedAt: event.occurredAt
  };
}
var MemoryEventBus = class {
  logger = new Logger(MemoryEventBus.name);
  /** All events published since construction (or last clear). */
  publishedEvents = [];
  handlers = /* @__PURE__ */ new Map();
  opts;
  constructor(opts) {
    this.opts = opts ?? { backend: "memory" };
  }
  async publish(event) {
    this.assertTierRouting(event);
    this.publishedEvents.push(event);
    if (this.shouldDispatch(event)) {
      await this.dispatch(event);
    }
  }
  async publishMany(events) {
    for (const event of events) {
      await this.publish(event);
    }
  }
  async findById(eventId) {
    return this.publishedEvents.find((e) => e.id === eventId) ?? null;
  }
  subscribe(eventType, handler) {
    if (!this.handlers.has(eventType)) {
      this.handlers.set(eventType, /* @__PURE__ */ new Set());
    }
    const set = this.handlers.get(eventType);
    const h = handler;
    set.add(h);
    return () => {
      set.delete(h);
    };
  }
  // ============================================================================
  // IEventReadPort (OBS-LIST-1)
  // ============================================================================
  async listEvents(query = {}) {
    const limit = clampEventLimit(query.limit);
    const keyset = query.cursor ? decodeEventCursor(query.cursor) : null;
    const str = (e, key) => {
      const v = e.metadata?.[key];
      return typeof v === "string" ? v : null;
    };
    const matched = this.publishedEvents.filter((e) => {
      if (query.poolId && str(e, "pool") !== query.poolId) return false;
      if (query.direction && str(e, "direction") !== query.direction)
        return false;
      if (query.rootRunId && str(e, "rootRunId") !== query.rootRunId)
        return false;
      if (query.since && e.occurredAt.getTime() < query.since.getTime())
        return false;
      if (query.tenantId !== void 0) {
        const t = str(e, "tenantId");
        if (query.tenantId === null) {
          if (t !== null) return false;
        } else if (t !== query.tenantId) {
          return false;
        }
      }
      return true;
    });
    matched.sort((a, b) => {
      const dt = b.occurredAt.getTime() - a.occurredAt.getTime();
      if (dt !== 0) return dt;
      return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
    });
    const seeked = keyset ? matched.filter((e) => {
      const ct = e.occurredAt.getTime();
      const kt = keyset.occurredAt.getTime();
      if (ct < kt) return true;
      if (ct > kt) return false;
      return e.id < keyset.id;
    }) : matched;
    const hasMore = seeked.length > limit;
    const page = hasMore ? seeked.slice(0, limit) : seeked;
    const items = page.map(toEventSummary);
    const last = page[page.length - 1];
    const nextCursor = hasMore && last ? encodeEventCursor({ occurredAt: last.occurredAt, id: last.id }) : null;
    return { items, nextCursor };
  }
  /** Remove all published events and subscriptions. Useful in beforeEach. */
  clear() {
    this.publishedEvents.length = 0;
    this.handlers.clear();
  }
  /** Filter published events by `metadata.pool`. */
  publishedEventsForPool(pool) {
    return this.publishedEvents.filter((e) => e.metadata?.["pool"] === pool);
  }
  /** Filter published events by `metadata.direction`. */
  publishedEventsForDirection(direction) {
    return this.publishedEvents.filter((e) => e.metadata?.["direction"] === direction);
  }
  /**
   * Decide whether `event` should be dispatched to handlers given the
   * current pool filter.
   *
   * Semantics (mirroring `DrizzleEventBus.processBatch`):
   * - `opts.pools` undefined  → dispatch everything (no filter).
   * - `opts.pools` empty array → treated as "no filter" to match the
   *   Drizzle backend, where `pools && pools.length > 0` is the gate on
   *   the `inArray` WHERE clause. Empty arrays dispatch everything.
   * - `opts.pools` non-empty  → dispatch only when `event.metadata.pool`
   *   is in the list. Events without `metadata.pool` do NOT match — they
   *   are out of all configured pools by definition.
   */
  shouldDispatch(event) {
    const pools = this.opts.pools;
    if (!pools || pools.length === 0) return true;
    const eventPool = event.metadata?.["pool"];
    return typeof eventPool === "string" && pools.includes(eventPool);
  }
  /**
   * Mirror the `domain_events_tier_routing_check` DB CHECK at the memory
   * backend (AUDIT-1). Audit-tier events MUST have null/undefined
   * `pool` and `direction` in metadata; the bridge dispatcher relies on
   * this invariant.
   */
  assertTierRouting(event) {
    const tier = event.metadata?.["tier"];
    if (tier !== "audit") return;
    const pool = event.metadata?.["pool"];
    const direction = event.metadata?.["direction"];
    const poolIsNull = pool === null || pool === void 0;
    const directionIsNull = direction === null || direction === void 0;
    if (!poolIsNull || !directionIsNull) {
      throw new Error(
        `MemoryEventBus: tier='audit' events must have null pool and direction (got pool=${String(pool)}, direction=${String(direction)}). This mirrors the domain_events CHECK constraint.`
      );
    }
  }
  async dispatch(event) {
    const set = this.handlers.get(event.type);
    if (!set) return;
    let firstError;
    for (const handler of set) {
      try {
        await handler(event);
      } catch (err) {
        this.logger.error(
          `Handler error for event type "${event.type}" (id: ${event.id}): ${err}`
        );
        if (firstError === void 0) {
          firstError = err;
        }
      }
    }
    if (firstError !== void 0) {
      throw firstError;
    }
  }
};
MemoryEventBus = __decorateClass([
  Injectable(),
  __decorateParam(0, Optional()),
  __decorateParam(0, Inject(EVENTS_MODULE_OPTIONS))
], MemoryEventBus);

export {
  MemoryEventBus
};
//# sourceMappingURL=chunk-LQ6PYFU6.js.map