import type {
  EventPayloadDef,
  EventPublishOptions,
  InferEventPayload as InferContractEventPayload,
  StandardSchema,
} from "../events/index.js";
import type {
  JobDef as ContractJobDef,
  InferJobPayload as InferContractJobPayload,
  JobDispatchOptions,
} from "../jobs/index.js";

/**
 * Represents a defined Domain Event with name and payload schema.
 * This is a minimal structural interface that ports need - event
 * declarations from `defineEvent` in @beignet/core/events satisfy it.
 */
export interface DomainEventDef<
  Name extends string = string,
  Payload extends StandardSchema = StandardSchema,
> extends EventPayloadDef<Name, Payload> {}

/**
 * Infer the payload type from a DomainEventDef.
 */
export type InferEventPayload<E extends DomainEventDef> =
  InferContractEventPayload<E>;

/**
 * Represents a job definition with a typed payload schema.
 *
 * Job dispatchers use Beignet's first-class job definitions. Inline
 * dispatchers can run the handler directly;
 * durable dispatchers can ignore it and enqueue the job name plus parsed
 * payload.
 */
export type JobDef<
  Name extends string = string,
  Payload extends StandardSchema = StandardSchema,
  Ctx = unknown,
> = ContractJobDef<Name, Payload, Ctx>;

/**
 * Infer the payload type from a JobDef.
 */
export type InferJobPayload<J extends JobDef> = InferContractJobPayload<J>;

/**
 * An EventBus port for publishing and subscribing to domain events.
 *
 * This interface defines a framework-agnostic contract for event-driven
 * communication within your application.
 *
 * @example
 * ```ts
 * import { createMemoryEventBus } from "@beignet/provider-event-bus-memory";
 *
 * const eventBus = createMemoryEventBus();
 *
 * // Subscribe to an event
 * const unsubscribe = eventBus.subscribe(UserRegistered, (payload) => {
 *   console.log(`User registered: ${payload.email}`);
 * });
 *
 * // Publish an event
 * await eventBus.publish(UserRegistered, { userId: "123", email: "test@example.com" });
 *
 * // Unsubscribe when done
 * unsubscribe();
 * ```
 */
export interface EventBusPort {
  /**
   * Publish a domain event with a typed payload.
   */
  publish<E extends DomainEventDef>(
    event: E,
    payload: InferEventPayload<E>,
    options?: EventPublishOptions,
  ): Promise<void> | void;

  /**
   * Subscribe to a domain event. Returns an unsubscribe function.
   */
  subscribe<E extends DomainEventDef>(
    event: E,
    handler: (
      payload: InferEventPayload<E>,
      options?: EventPublishOptions,
    ) => Promise<void> | void,
  ): () => void;
}

/**
 * A port for dispatching explicit background jobs.
 *
 * Jobs represent work to do, not facts that happened. Implementations may run
 * inline in tests, enqueue into a durable worker, or call an external job
 * system.
 */
export interface JobDispatcherPort {
  dispatch<J extends JobDef>(
    job: J,
    payload: InferJobPayload<J>,
    options?: JobDispatchOptions,
  ): Promise<void> | void;
}
