function message(msg: string, actual: unknown, ...types: Array<string | null>) {
  types = types.filter(Boolean)
  if (types.length > 2) {
    const last = types.pop()
    msg += `one of type ${types.join(', ')}, or ${last}.`
  } else if (types.length === 2) {
    msg += `one of type ${types[0]} or ${types[1]}.`
  } else {
    msg += `of type ${types[0]}.`
  }

  if (actual == null) {
    msg += ` Received ${actual}`
  } else if (typeof actual === 'function' && actual.name) {
    msg += ` Received function ${actual.name}`
  } else if (typeof actual === 'object' && actual != null) {
    if (actual.constructor?.name) {
      msg += ` Received an instance of ${actual.constructor.name}`
    }
  }

  return msg
}

export const invalidKeyInput = (actual: unknown, ...types: string[]) =>
  message('Key must be ', actual, ...types)

export const withAlg = (alg: string, actual: unknown, ...types: Array<string | null>) =>
  message(`Key for the ${alg} algorithm must be `, actual, ...types)
