/**
 * PortsBuilder - Mutable builder for composing ports during app initialization
 *
 * This builder is a small composition helper for tests and custom bootstrapping
 * code that wants to assemble ports incrementally.
 */

/**
 * A mutable builder around a ports object.
 * Used during app composition and provider registration.
 */
export interface PortsBuilder<Ports> {
  /**
   * The current ports object being built.
   * This is mutated internally when extend/replace are called.
   */
  ports: Ports;

  /**
   * Extend ports with a new key. If the key already exists, it's overwritten.
   *
   * Returns the same builder instance with an extended type:
   *   Ports & { [K in key]: Value }
   */
  extend<K extends string, V>(
    key: K,
    value: V,
  ): PortsBuilder<Ports & { [P in K]: V }>;

  /**
   * Replace an existing key. Does not change the type, but updates the runtime value.
   * Returns the same builder instance.
   */
  replace<K extends keyof Ports>(key: K, value: Ports[K]): PortsBuilder<Ports>;
}

/**
 * Create a new PortsBuilder from an initial ports object.
 *
 * The builder wraps a mutable object internally and provides type-safe methods
 * to extend or replace ports. This is used during application composition
 * helpers that want to modify ports before passing them to a server.
 *
 * @example
 * ```ts
 * const initialPorts = definePorts({ db: dbAdapter });
 * const builder = createPortsBuilder(initialPorts);
 *
 * // Provider extends with cache
 * builder.extend("cache", cacheAdapter);
 *
 * // Final ports includes both db and cache
 * const finalPorts = builder.ports;
 * ```
 */
export function createPortsBuilder<Ports>(
  initialPorts: Ports,
): PortsBuilder<Ports> {
  // Keep a mutable object internally
  // biome-ignore lint/suspicious/noExplicitAny: internal mutable state needs any to accept arbitrary port extensions
  const state: { ports: any } = {
    ports: { ...initialPorts },
  };

  const builder: PortsBuilder<Ports> = {
    get ports() {
      // Could freeze in dev, but keep simple
      return state.ports as Ports;
    },
    extend<K extends string, V>(
      key: K,
      value: V,
    ): PortsBuilder<Ports & { [P in K]: V }> {
      state.ports[key] = value;
      return builder as unknown as PortsBuilder<Ports & { [P in K]: V }>;
    },
    replace<K extends keyof Ports>(
      key: K,
      value: Ports[K],
    ): PortsBuilder<Ports> {
      state.ports[key] = value;
      return builder;
    },
  };

  return builder;
}

/**
 * Extract the Ports type from a PortsBuilder
 *
 * @example
 * ```ts
 * type MyPorts = PortsOf<typeof builder>;
 * ```
 */
export type PortsOf<PB> = PB extends PortsBuilder<infer P> ? P : never;
