/**
 * App-facing ID generation port.
 *
 * Use this when deterministic IDs matter in use-case tests or when production
 * infrastructure owns ID generation.
 */
export interface IdGeneratorPort {
  /**
   * Return the next ID.
   */
  nextId(): string;
}

/**
 * Mutable sequential ID generator used by tests and simple examples.
 */
export interface SequenceIdGeneratorPort extends IdGeneratorPort {
  /**
   * Reset the next sequence value.
   */
  reset(next?: number): void;
}

/**
 * Create an ID generator backed by `globalThis.crypto.randomUUID()`.
 *
 * The factory itself does not read from `crypto`; the returned generator's
 * `nextId()` method throws if the current runtime does not expose
 * `globalThis.crypto.randomUUID()`.
 *
 * @returns An ID generator that returns UUID strings from `nextId()`.
 */
export function createUuidIdGenerator(): IdGeneratorPort {
  return {
    nextId: () => {
      if (typeof globalThis.crypto?.randomUUID !== "function") {
        throw new Error(
          "createUuidIdGenerator requires globalThis.crypto.randomUUID(). Provide a custom IdGeneratorPort in this runtime.",
        );
      }

      return globalThis.crypto.randomUUID();
    },
  };
}

/**
 * Create a deterministic sequence ID generator for tests and examples.
 *
 * @example
 * ```ts
 * const ids = createSequenceIdGenerator({ prefix: "post", start: 10 });
 * ids.nextId(); // "post_10"
 * ids.nextId(); // "post_11"
 * ```
 *
 * @param options - Optional ID prefix and starting sequence number.
 * @returns A mutable sequence ID generator.
 */
export function createSequenceIdGenerator(
  options: { prefix?: string; start?: number } = {},
): SequenceIdGeneratorPort {
  const prefix = options.prefix ?? "id";
  const start = options.start ?? 1;
  let next = start;

  return {
    nextId: () => `${prefix}_${next++}`,
    reset: (value = start) => {
      next = value;
    },
  };
}
