import { createScalableCuckooFilter } from '@libp2p/utils'
import type { PeerId } from '@libp2p/interface'
import type { Filter } from '@libp2p/utils'

/**
 * Uses a Cuckoo filter to implement a mechanism for deduplicating PeerIds in a
 * way that uses a smaller amount of memory than a PeerSet.
 */
export class PeerFilter {
  private readonly filter: Filter

  constructor (size: number, errorRate?: number) {
    this.filter = createScalableCuckooFilter(size, errorRate)
  }

  has (peerId: PeerId): boolean {
    return this.filter.has(peerId.toMultihash().bytes)
  }

  add (peerId: PeerId): void {
    this.filter.add(peerId.toMultihash().bytes)
  }

  remove (peerId: PeerId): void {
    this.filter.remove?.(peerId.toMultihash().bytes)
  }
}

/**
 * Create and return a PeerFilter. This can be used by topologies to prevent
 * them receiving duplicate notifications for a peer that connects repeatedly.
 *
 * @example
 *
 * ```TypeScript
 * import { peerFilter } from '@libp2p/peer-collections'
 * import type { Registrar } from '@libp2p/interface-internal'
 *
 * const registrar: Registrar
 *
 * registrar.register('/my/protocol/1.0.0', {
 *   filter: peerFilter(),
 *   onConnect: (peerId) => {
 *     // will only be invoked for a given peerId once
 *   }
 * })
 * ```
 *
 * @param size - The maximum number of peers expected to be added to the filter
 * @param errorRate - The acceptable error rate
 */
export function peerFilter (size: number, errorRate: number = 0.001): PeerFilter {
  return new PeerFilter(size, errorRate)
}
