// Local type aliases matching the contracts in @repo/core-events.
// Kept inline to avoid a build-graph cycle between core-testing and core-events
// (mirrors the recording-tracer / recording-logger pattern).
import type { z } from "zod";

type EventDescriptor<TName extends string, TSchema extends z.ZodType> = {
  readonly name: TName;
  readonly schema: TSchema;
};

type EventHandler<T> = (event: T) => Promise<void>;

interface IEventBus {
  publish<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    payload: T,
  ): Promise<void>;
  subscribe<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    consumerFeature: string,
    handler: EventHandler<T>,
  ): void;
}

export class RecordingEventBus implements IEventBus {
  readonly published: { name: string; payload: unknown }[] = [];
  private readonly handlers = new Map<string, EventHandler<unknown>[]>();

  async publish<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    payload: T,
  ): Promise<void> {
    descriptor.schema.parse(payload);
    this.published.push({ name: descriptor.name, payload });
    for (const h of this.handlers.get(descriptor.name) ?? []) await h(payload);
  }

  subscribe<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    _consumerFeature: string,
    handler: EventHandler<T>,
  ): void {
    const arr = this.handlers.get(descriptor.name) ?? [];
    arr.push(handler as EventHandler<unknown>);
    this.handlers.set(descriptor.name, arr);
  }
}
