content: package com.konsol.beatstream.service;
  
  import com.konsol.beatstream.config.Constants;
  import com.konsol.beatstream.domain.Authority;
  import com.konsol.beatstream.domain.User;
  import com.konsol.beatstream.repository.AuthorityRepository;
  import com.konsol.beatstream.repository.UserRepository;
  import com.konsol.beatstream.security.AuthoritiesConstants;
  import com.konsol.beatstream.security.SecurityUtils;
  import com.konsol.beatstream.service.dto.AdminUserDTO;
  import com.konsol.beatstream.service.dto.UserDTO;
  import java.time.Instant;
  import java.time.temporal.ChronoUnit;
  import java.util.*;
  import java.util.stream.Collectors;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.cache.CacheManager;
  import org.springframework.data.domain.Page;
  import org.springframework.data.domain.Pageable;
  import org.springframework.scheduling.annotation.Scheduled;
  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.security.crypto.password.PasswordEncoder;
  import org.springframework.stereotype.Service;
  import tech.jhipster.security.RandomUtil;
  
  /**
   * Service class for managing users.
   */
  @Service
  public class UserService {
  
      private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
  
      private final UserRepository userRepository;
  
      private final PasswordEncoder passwordEncoder;
  
      private final AuthorityRepository authorityRepository;
  
      private final CacheManager cacheManager;
  
      public UserService(
          UserRepository userRepository,
          PasswordEncoder passwordEncoder,
          AuthorityRepository authorityRepository,
          CacheManager cacheManager
      ) {
          this.userRepository = userRepository;
          this.passwordEncoder = passwordEncoder;
          this.authorityRepository = authorityRepository;
          this.cacheManager = cacheManager;
      }
  
      public Optional<User> activateRegistration(String key) {
          LOG.debug("Activating user for activation key {}", key);
          return userRepository
              .findOneByActivationKey(key)
              .map(user -> {
                  // activate given user for the registration key.
                  user.setActivated(true);
                  user.setActivationKey(null);
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  LOG.debug("Activated user: {}", user);
                  return user;
              });
      }
  
      public Optional<User> completePasswordReset(String newPassword, String key) {
          LOG.debug("Reset user password for reset key {}", key);
          return userRepository
              .findOneByResetKey(key)
              .filter(user -> user.getResetDate().isAfter(Instant.now().minus(1, ChronoUnit.DAYS)))
              .map(user -> {
                  user.setPassword(passwordEncoder.encode(newPassword));
                  user.setResetKey(null);
                  user.setResetDate(null);
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  return user;
              });
      }
  
      public Optional<User> requestPasswordReset(String mail) {
          return userRepository
              .findOneByEmailIgnoreCase(mail)
              .filter(User::isActivated)
              .map(user -> {
                  user.setResetKey(RandomUtil.generateResetKey());
                  user.setResetDate(Instant.now());
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  return user;
              });
      }
  
      public User registerUser(AdminUserDTO userDTO, String password) {
          userRepository
              .findOneByLogin(userDTO.getLogin().toLowerCase())
              .ifPresent(existingUser -> {
                  boolean removed = removeNonActivatedUser(existingUser);
                  if (!removed) {
                      throw new UsernameAlreadyUsedException();
                  }
              });
          userRepository
              .findOneByEmailIgnoreCase(userDTO.getEmail())
              .ifPresent(existingUser -> {
                  boolean removed = removeNonActivatedUser(existingUser);
                  if (!removed) {
                      throw new EmailAlreadyUsedException();
                  }
              });
          User newUser = new User();
          String encryptedPassword = passwordEncoder.encode(password);
          newUser.setLogin(userDTO.getLogin().toLowerCase());
          // new user gets initially a generated password
          newUser.setPassword(encryptedPassword);
          newUser.setFirstName(userDTO.getFirstName());
          newUser.setLastName(userDTO.getLastName());
          if (userDTO.getEmail() != null) {
              newUser.setEmail(userDTO.getEmail().toLowerCase());
          }
          newUser.setImageUrl(userDTO.getImageUrl());
          newUser.setLangKey(userDTO.getLangKey());
          // new user is not active
          newUser.setActivated(false);
          // new user gets registration key
          newUser.setActivationKey(RandomUtil.generateActivationKey());
          Set<Authority> authorities = new HashSet<>();
          authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add);
          newUser.setAuthorities(authorities);
          userRepository.save(newUser);
          this.clearUserCaches(newUser);
          LOG.debug("Created Information for User: {}", newUser);
          return newUser;
      }
  
      private boolean removeNonActivatedUser(User existingUser) {
          if (existingUser.isActivated()) {
              return false;
          }
          userRepository.delete(existingUser);
          this.clearUserCaches(existingUser);
          return true;
      }
  
      public User createUser(AdminUserDTO userDTO) {
          User user = new User();
          user.setLogin(userDTO.getLogin().toLowerCase());
          user.setFirstName(userDTO.getFirstName());
          user.setLastName(userDTO.getLastName());
          if (userDTO.getEmail() != null) {
              user.setEmail(userDTO.getEmail().toLowerCase());
          }
          user.setImageUrl(userDTO.getImageUrl());
          if (userDTO.getLangKey() == null) {
              user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language
          } else {
              user.setLangKey(userDTO.getLangKey());
          }
          String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
          user.setPassword(encryptedPassword);
          user.setResetKey(RandomUtil.generateResetKey());
          user.setResetDate(Instant.now());
          user.setActivated(true);
          if (userDTO.getAuthorities() != null) {
              Set<Authority> authorities = userDTO
                  .getAuthorities()
                  .stream()
                  .map(authorityRepository::findById)
                  .filter(Optional::isPresent)
                  .map(Optional::get)
                  .collect(Collectors.toSet());
              user.setAuthorities(authorities);
          }
          userRepository.save(user);
          this.clearUserCaches(user);
          LOG.debug("Created Information for User: {}", user);
          return user;
      }
  
      /**
       * Update all information for a specific user, and return the modified user.
       *
       * @param userDTO user to update.
       * @return updated user.
       */
      public Optional<AdminUserDTO> updateUser(AdminUserDTO userDTO) {
          return Optional.of(userRepository.findById(userDTO.getId()))
              .filter(Optional::isPresent)
              .map(Optional::get)
              .map(user -> {
                  this.clearUserCaches(user);
                  user.setLogin(userDTO.getLogin().toLowerCase());
                  user.setFirstName(userDTO.getFirstName());
                  user.setLastName(userDTO.getLastName());
                  if (userDTO.getEmail() != null) {
                      user.setEmail(userDTO.getEmail().toLowerCase());
                  }
                  user.setImageUrl(userDTO.getImageUrl());
                  user.setActivated(userDTO.isActivated());
                  user.setLangKey(userDTO.getLangKey());
                  Set<Authority> managedAuthorities = user.getAuthorities();
                  managedAuthorities.clear();
                  userDTO
                      .getAuthorities()
                      .stream()
                      .map(authorityRepository::findById)
                      .filter(Optional::isPresent)
                      .map(Optional::get)
                      .forEach(managedAuthorities::add);
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  LOG.debug("Changed Information for User: {}", user);
                  return user;
              })
              .map(AdminUserDTO::new);
      }
  
      public void deleteUser(String login) {
          userRepository
              .findOneByLogin(login)
              .ifPresent(user -> {
                  userRepository.delete(user);
                  this.clearUserCaches(user);
                  LOG.debug("Deleted User: {}", user);
              });
      }
  
      /**
       * Update basic information (first name, last name, email, language) for the current user.
       *
       * @param firstName first name of user.
       * @param lastName  last name of user.
       * @param email     email id of user.
       * @param langKey   language key.
       * @param imageUrl  image URL of user.
       */
      public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) {
          SecurityUtils.getCurrentUserLogin()
              .flatMap(userRepository::findOneByLogin)
              .ifPresent(user -> {
                  user.setFirstName(firstName);
                  user.setLastName(lastName);
                  if (email != null) {
                      user.setEmail(email.toLowerCase());
                  }
                  user.setLangKey(langKey);
                  user.setImageUrl(imageUrl);
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  LOG.debug("Changed Information for User: {}", user);
              });
      }
  
      public void changePassword(String currentClearTextPassword, String newPassword) {
          SecurityUtils.getCurrentUserLogin()
              .flatMap(userRepository::findOneByLogin)
              .ifPresent(user -> {
                  String currentEncryptedPassword = user.getPassword();
                  if (!passwordEncoder.matches(currentClearTextPassword, currentEncryptedPassword)) {
                      throw new InvalidPasswordException();
                  }
                  String encryptedPassword = passwordEncoder.encode(newPassword);
                  user.setPassword(encryptedPassword);
                  userRepository.save(user);
                  this.clearUserCaches(user);
                  LOG.debug("Changed password for User: {}", user);
              });
      }
  
      public Page<AdminUserDTO> getAllManagedUsers(Pageable pageable) {
          return userRepository.findAll(pageable).map(AdminUserDTO::new);
      }
  
      public Page<UserDTO> getAllPublicUsers(Pageable pageable) {
          return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(UserDTO::new);
      }
  
      public Optional<User> getUserWithAuthoritiesByLogin(String login) {
          return userRepository.findOneByLogin(login);
      }
  
      public Optional<User> getUserWithAuthorities() {
          return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneByLogin);
      }
  
      /**
       * Not activated users should be automatically deleted after 3 days.
       * <p>
       * This is scheduled to get fired everyday, at 01:00 (am).
       */
      @Scheduled(cron = "0 0 1 * * ?")
      public void removeNotActivatedUsers() {
          userRepository
              .findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS))
              .forEach(user -> {
                  LOG.debug("Deleting not activated user {}", user.getLogin());
                  userRepository.delete(user);
                  this.clearUserCaches(user);
              });
      }
  
      /**
       * Gets a list of all the authorities.
       * @return a list of all the authorities.
       */
      public List<String> getAuthorities() {
          return authorityRepository.findAll().stream().map(Authority::getName).toList();
      }
  
      private void clearUserCaches(User user) {
          Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE)).evict(user.getLogin());
          if (user.getEmail() != null) {
              Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE)).evict(user.getEmail());
          }
      }
  
      public User getCurrentUser() {
          // return userRepository.findOneByLogin("user").get();
  
          final Optional<User> isUser = this.getUserWithAuthorities();
          return isUser.orElse(null);
      }
  }
  