import type { z } from "zod";
import type { IJobQueue } from "@repo/core-shared/jobs";
import type { EventDescriptor } from "./event-descriptor";
import type { EventHandler, IEventBus } from "./event-bus.interface";

/**
 * Production-grade bus: for each subscriber, enqueues one Payload task per
 * `publish()` call. Subscribers register with their consumer-feature name so
 * fan-out tasks are named deterministically: `__events.<event>.<consumer>`.
 * The actual handler invocation happens inside Payload's job runner — see the
 * matching task config generated by `gen event consume` (Task 39).
 */
export class PayloadJobsEventBus implements IEventBus {
  private readonly subscribers = new Map<string, string[]>();

  constructor(private readonly queue: IJobQueue) {}

  async publish<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    payload: T,
  ): Promise<void> {
    descriptor.schema.parse(payload);
    const consumers = this.subscribers.get(descriptor.name) ?? [];
    await Promise.all(
      consumers.map((consumerFeature) =>
        this.queue.enqueue(
          `__events.${descriptor.name}.${consumerFeature}`,
          payload,
        ),
      ),
    );
  }

  subscribe<T>(
    descriptor: EventDescriptor<string, z.ZodType<T>>,
    consumerFeature: string,
    _handler: EventHandler<T>,
  ): void {
    const arr = this.subscribers.get(descriptor.name) ?? [];
    if (!arr.includes(consumerFeature)) arr.push(consumerFeature);
    this.subscribers.set(descriptor.name, arr);
  }
}
