package net.explorviz.security.services;

import com.mongodb.MongoException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import net.explorviz.security.util.CountingIdGenerator;
import net.explorviz.security.util.IdGenerator;
import net.explorviz.shared.security.model.User;
import net.explorviz.shared.security.model.roles.Role;
import org.jvnet.hk2.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.morphia.Datastore;

/**
 * Offers CRUD operations on user objects, backed by a MongoDB instance as persistence layer. Each
 * user has the following fields:
 * <ul>
 * <li>id: the unique id of the user</li>
 * <li>username: name of the user, unique</li>
 * <li>password: hashed password</li>
 * <li>roles: list of role that are assigned to the user</li>
 * </ul>
 *
 */
@Service
public class UserMongoCrudService implements MongoCrudService<User> {

  private static final Logger LOGGER = LoggerFactory.getLogger(UserMongoCrudService.class);

  private final IdGenerator<Long> idGen;

  private final Datastore datastore;


  /**
   * Creates a new {@code UserCrudMongoDb}.
   *
   */
  @Inject
  public UserMongoCrudService(final Datastore datastore) {

    this.datastore = datastore;

    final User userWithMaxId = this.datastore.createQuery(User.class).order("id").get();

    // Create a new id generator, which will count upwards beginning from the max id
    long counterInitValue = 0L;

    if (userWithMaxId != null) {
      counterInitValue = userWithMaxId.getId();
    }

    this.idGen = new CountingIdGenerator(counterInitValue);
  }


  @Override
  public List<User> getAll() {
    return this.datastore.createQuery(User.class).asList();
  }

  @Override
  public Optional<User> saveNewEntity(final User user) throws MongoException {
    // Generate an id

    user.setId(this.idGen.next());

    this.datastore.save(user);

    if (LOGGER.isInfoEnabled()) {
      LOGGER.info("Inserted new user with id " + user.getId());
    }
    return Optional.ofNullable(
        new User(user.getId(), user.getUsername(), user.getPassword(), user.getRoles()));
  }

  @Override
  public void updateEntity(final User user) throws MongoException {
    this.datastore.save(user);
  }

  @Override
  public Optional<User> getEntityById(final Long id) throws MongoException {

    final User userObject = this.datastore.get(User.class, id);

    return Optional.ofNullable(userObject);
  }

  @Override
  public void deleteEntityById(final Long id) throws MongoException {

    this.datastore.delete(User.class, id);

    if (LOGGER.isInfoEnabled()) {
      LOGGER.info("Deleted user with id " + id);
    }

  }


  @Override
  public Optional<User> findEntityByFieldValue(final String field, final Object value) {

    final User foundUser = this.datastore.createQuery(User.class).filter(field, value).get();

    return Optional.ofNullable(foundUser);
  }

  /**
   * Retrieves a list of all users, that have a specific role assigned.
   *
   * @param roleName role to search for
   * @return a list of all users, that have the given role assigned
   */
  public List<User> getUsersByRole(final String roleName) throws MongoException {

    // TODO find smarter (MongoDB based) way to check for roles

    final Role role = this.datastore.createQuery(Role.class).filter("descriptor", roleName).get();

    final List<User> listToBeReturned = new ArrayList<>();

    final List<User> userList = this.datastore.createQuery(User.class).asList();

    for (final User user : userList) {
      if (user.getRoles().contains(role)) {
        listToBeReturned.add(user);
      }
    }

    return listToBeReturned;
  }



}

@@ -148,16 +148,52 @@
 tional;%0A
+import java.util.stream.Collectors;%0A
 import j
@@ -1154,16 +1154,17 @@
 tore;%0A%0A%0A
+%0A
   /**%0A  
@@ -1902,17 +1902,16 @@
 e an id%0A
-%0A
     user
@@ -3490,38 +3490,32 @@
 yList%3C%3E();%0A%0A    
-final 
 List%3CUser%3E userL
@@ -3574,169 +3574,314 @@
 );%0A%0A
+%0A%0A
     
-for (final User user : userList) %7B%0A      if (user.getRoles().contains(role)) %7B%0A        listToBeReturned.add(user);
+userList =%0A        userList.stream()%0A            .filter(u -%3E u.getRoles().stream()%0A                .anyMatch(r -%3E r.getId().longValue() == role.getId().longValue()%0A                    && r.getDescriptor().equals(role.getDescriptor())))
 %0A      
-%7D%0A
     
-%7D%0A%0A    return listToBeReturned
+  .collect(Collectors.toList());%0A%0A    return userList
 ;%0A  

Fix bug in getUsersByRole()