content: package com.nonononoki.alovoa.model;
  
  import com.nonononoki.alovoa.Tools;
  import com.nonononoki.alovoa.component.TextEncryptorConverter;
  import com.nonononoki.alovoa.entity.User;
  import com.nonononoki.alovoa.entity.user.*;
  import com.nonononoki.alovoa.rest.MediaController;
  import com.nonononoki.alovoa.service.UserService;
  import lombok.*;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import javax.crypto.BadPaddingException;
  import javax.crypto.IllegalBlockSizeException;
  import javax.crypto.NoSuchPaddingException;
  import java.io.UnsupportedEncodingException;
  import java.security.InvalidAlgorithmParameterException;
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  import java.time.LocalDateTime;
  import java.util.*;
  
  @Getter
  @Setter
  @RequiredArgsConstructor
  public class UserDto {
      public static final int LA_STATE_ACTIVE_1 = 5; // in minutes
      public static final int LA_STATE_ACTIVE_2 = 1;
      public static final int LA_STATE_ACTIVE_3 = 7;
      public static final int LA_STATE_ACTIVE_4 = 30;
      public static final int VERIFICATION_MINIMUM = 5;
      public static final int VERIFICATION_FACTOR = 5;
      private static final double MILES_TO_KM = 0.6214;
      private static final Logger LOGGER = LoggerFactory.getLogger(UserDto.class);
  
      private UUID uuid;
      private String email;
      private String firstName;
      private int age;
      private float donationAmount;
      private Gender gender;
      private boolean hasAudio;
      private String audio;
      private String zodiac;
      private boolean showZodiac;
      private int units;
      private int preferedMinAge;
      private int preferedMaxAge;
      private Set<UserMiscInfo> miscInfos;
      private Set<Gender> preferedGenders;
      private UserIntention intention;
      private List<UserInterest> interests;
      private List<UserInterest> commonInterests;
      private List<UserPrompt> prompts;
      private String profilePicture;
      private List<UserImageDto> images;
      private String description;
      private String country;
      private int distanceToUser;
      private double totalDonations;
      private long numBlockedByUsers;
      private long numReports;
      private boolean blockedByCurrentUser;
      private boolean reportedByCurrentUser;
      private boolean likesCurrentUser;
      private boolean likedByCurrentUser;
      private boolean hiddenByCurrentUser;
      private long numberReferred;
      private long numberProfileViews;
      private boolean compatible;
      private boolean hasLocation;
      private Double locationLatitude;
      private Double locationLongitude;
      private UserDtoVerificationPicture verificationPicture;
      private int lastActiveState = 5;
      private UserSettings userSettings;
  
      public static UserDto userToUserDto(DtoBuilder builder)
              throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException,
              NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, AlovoaException {
          User user = builder.user;
          User currentUser = builder.currentUser;
          UserService userService = builder.userService;
          boolean ignoreIntention = builder.ignoreIntention;
          final UUID uuid;
  
          if (user == null) {
              return null;
          }
  
          UserDto dto = new UserDto();
          if (user.equals(currentUser)) {
              dto.setEmail(user.getEmail());
              dto.setLocationLatitude(user.getLocationLatitude());
              dto.setLocationLongitude(user.getLocationLongitude());
  
              UserSettings settings = user.getUserSettings();
              dto.setUserSettings(settings);
          }
          uuid = Tools.getUserUUID(user, userService);
  
          dto.setUuid(uuid);
  
          if (user.getDates() != null) {
              dto.setAge(Tools.calcUserAge(user));
          }
          if (user.getLocationLatitude() != null) {
              dto.setHasLocation(true);
          }
  
          dto.setDescription(user.getDescription());
          dto.setFirstName(user.getFirstName());
          dto.setGender(user.getGender());
  
          if (user.getVerificationPicture() != null) {
              dto.setVerificationPicture(UserDtoVerificationPicture.map(user, currentUser, userService, user.getVerificationPicture().getUuid()));
          }
  
          dto.setCountry(Tools.getCountryEmoji(user.getCountry()));
  
          if (currentUser.isShowZodiac()) {
              dto.setZodiac(getUserZodiac(user));
          }
          dto.setShowZodiac(user.isShowZodiac());
          dto.setUnits(user.getUnits());
          dto.setMiscInfos(user.getMiscInfos());
          dto.setPreferedGenders(user.getPreferedGenders());
          dto.setPreferedMinAge(user.getPreferedMinAge());
          dto.setPreferedMaxAge(user.getPreferedMaxAge());
          if (dto.getPreferedMinAge() < Tools.AGE_LEGAL && dto.getAge() >= Tools.AGE_LEGAL) {
              dto.setPreferedMinAge(Tools.AGE_LEGAL);
          }
          dto.setImages(UserImageDto.buildFromUserImages(user, userService));
          dto.setGender(user.getGender());
          dto.setIntention(user.getIntention());
          if (user.getProfilePicture() != null) {
              UUID picUuid = Tools.getProfilePictureUUID(user.getProfilePicture(), userService);
              dto.setProfilePicture(UserProfilePicture.getPublicUrl(userService.getDomain(), picUuid));
          }
          dto.setTotalDonations(user.getTotalDonations());
          dto.setNumBlockedByUsers(user.getBlockedByUsers().size());
          dto.setNumReports(user.getReportedByUsers().size());
          dto.setInterests(user.getInterests());
          if (user.getAudio() != null) {
              UUID picUuid = user.getAudio().getUuid() != null ? user.getAudio().getUuid() : uuid;
              dto.setAudio(userService.getDomain() + MediaController.URL_REQUEST_MAPPING +
                      MediaController.URL_AUDIO + picUuid);
          }
          dto.setHasAudio(user.getAudio() != null);
          dto.setNumberReferred(user.getNumberReferred());
          dto.setPrompts(user.getPrompts());
  
          if (!user.isAdmin()) {
              LocalDateTime now = LocalDateTime.now();
              LocalDateTime activeDateTime = Tools.dateToLocalDateTime(user.getDates().getActiveDate());
              if (activeDateTime.isAfter(now.minusMinutes(LA_STATE_ACTIVE_1))) {
                  dto.setLastActiveState(1);
              } else if (activeDateTime.isAfter(now.minusDays(LA_STATE_ACTIVE_2))) {
                  dto.setLastActiveState(2);
              } else if (activeDateTime.isAfter(now.minusDays(LA_STATE_ACTIVE_3))) {
                  dto.setLastActiveState(3);
              } else if (activeDateTime.isAfter(now.minusDays(LA_STATE_ACTIVE_4))) {
                  dto.setLastActiveState(4);
              }
          }
  
          if (!user.equals(currentUser)) {
              if (currentUser.getBlockedUsers() != null) {
                  dto.blockedByCurrentUser = currentUser.getBlockedUsers().stream()
                          .filter(o -> o.getUserTo() != null)
                          .anyMatch(o -> Objects.equals(o.getUserTo().getId(), user.getId()));
              }
              if (currentUser.getReported() != null) {
                  dto.reportedByCurrentUser = currentUser.getReported().stream()
                          .filter(o -> o.getUserTo() != null)
                          .anyMatch(o -> Objects.equals(o.getUserTo().getId(), user.getId()));
              }
              if (user.getLikes() != null) {
                  try {
                      dto.likesCurrentUser = user.getLikes().stream()
                              .filter(o -> o.getUserTo() != null)
                              .anyMatch(o -> Objects.equals(o.getUserTo().getId(), currentUser.getId()));
                  } catch (Exception ignored) {
                      dto.likesCurrentUser = false;
                  }
              }
              if (currentUser.getLikes() != null) {
                  dto.likedByCurrentUser = currentUser.getLikes().stream()
                          .filter(o -> o.getUserTo() != null)
                          .anyMatch(o -> Objects.equals(o.getUserTo().getId(), user.getId()));
              }
              if (currentUser.getHiddenUsers() != null) {
                  dto.hiddenByCurrentUser = currentUser.getHiddenUsers().stream()
                          .filter(o -> o.getUserTo() != null)
                          .anyMatch(o -> Objects.equals(o.getUserTo().getId(), user.getId()));
              }
  
              List<UserInterest> commonInterests = new ArrayList<>();
              for (int i = 0; i < currentUser.getInterests().size(); i++) {
                  UserInterest interest = currentUser.getInterests().get(i);
                  if (user.getInterests().contains(interest)) {
                      commonInterests.add(interest);
                  }
              }
              dto.setCommonInterests(commonInterests);
  
              int dist = 99999;
              if (!currentUser.isAdmin()) {
                  dist = Tools.getDistanceToUser(user, currentUser);
                  if (currentUser.getUnits() == User.UNIT_IMPERIAL) {
                      dist = (int) (dist * MILES_TO_KM);
                  }
              }
              dto.setDistanceToUser(dist);
          }
          dto.setCompatible(Tools.usersCompatible(currentUser, user, ignoreIntention));
          return dto;
      }
  
      public static long decodeIdThrowing(String id, TextEncryptorConverter textEncryptor)
              throws NumberFormatException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
              NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException {
          String en = new String(Base64.getDecoder().decode(id));
          return Long.parseLong(textEncryptor.decode(en));
      }
  
      @Deprecated
      public static Optional<Long> decodeId(String id, TextEncryptorConverter textEncryptor) {
          try {
              String en = new String(Base64.getDecoder().decode(id));
              return Optional.of(Long.parseLong(textEncryptor.decode(en)));
          } catch (Exception e) {
              LOGGER.debug(String.format("Couldn't decode id '%s'", id), e);
          }
          return Optional.empty();
      }
  
  
      public static String getUserZodiac(User user) {
          Calendar cal = Calendar.getInstance();
          cal.setTime(user.getDates().getDateOfBirth());
          int month = cal.get(Calendar.MONTH) + 1;
          int day = cal.get(Calendar.DAY_OF_MONTH);
          if (month == 12 && day >= 22 || month == 1 && day <= 19)
              return "capricorn";
          else if (month == 1 || month == 2 && day <= 17)
              return "aquarius";
          else if (month == 2 && day <= 29 || month == 3 && day <= 19)
              return "pisces";
          else if (month == 3 || month == 4 && day <= 19)
              return "aries";
          else if (month == 4 && day <= 30 || month == 5 && day <= 20)
              return "taurus";
          else if (month == 5 || month == 6 && day <= 20)
              return "gemini";
          else if (month == 6 && day <= 30 || month == 7 && day <= 22)
              return "cancer";
          else if (month == 7 || month == 8 && day <= 22)
              return "leo";
          else if (month == 8 || month == 9 && day <= 22)
              return "virgo";
          else if (month == 9 && day <= 30 || month == 10 && day <= 22)
              return "libra";
          else if (month == 10 || month == 11 && day <= 21)
              return "scorpio";
          else if (month == 11 && day <= 30 || month == 12)
              return "sagittarius";
          return null;
  
      }
  
      public static boolean isVerifiedByUsers(UserVerificationPicture pic) {
          if (pic.getUserYes().size() < VERIFICATION_MINIMUM) {
              return false;
          }
          return pic.getUserNo().size() * VERIFICATION_FACTOR <= pic.getUserYes().size();
      }
  
      @Getter
      @Setter
      public static class UserDtoVerificationPicture {
          private boolean verifiedByAdmin;
          private boolean verifiedByUsers;
          private boolean votedByCurrentUser;
          private boolean hasPicture;
          private String data;
          private String text;
          private UUID uuid;
          private int userYes;
          private int userNo;
  
          public static UserDtoVerificationPicture map(User user, User currentUser, UserService userService, UUID uuid) {
              UserDtoVerificationPicture verificationPicture = new UserDtoVerificationPicture();
              verificationPicture.setText(userService.getVerificationCode(user));
              UserVerificationPicture pic = user.getVerificationPicture();
              verificationPicture.setHasPicture(pic != null && pic.getBin() != null);
  
              if (pic == null) {
                  return verificationPicture;
              }
  
              UUID picUuid = pic.getUuid() != null ? pic.getUuid() : uuid;
  
              if (!pic.isVerifiedByAdmin()) {
                  verificationPicture.setData(UserVerificationPicture.getPublicUrl(userService.getDomain(), uuid));
              }
  
              //only show verification for users with verification
              if (currentUser == user || currentUser.getVerificationPicture() == null && !currentUser.isAdmin()) {
                  return verificationPicture;
              }
  
              verificationPicture.setUserNo(pic.getUserNo().size());
              verificationPicture.setUserYes(pic.getUserYes().size());
              verificationPicture.setVerifiedByUsers(UserDto.isVerifiedByUsers(pic));
              verificationPicture.setVerifiedByAdmin(pic.isVerifiedByAdmin());
              verificationPicture.setVotedByCurrentUser(pic.getUserYes().contains(currentUser) || pic.getUserNo().contains(currentUser));
              verificationPicture.setUuid(picUuid);
  
              return verificationPicture;
          }
      }
  
      @Builder
      public static class DtoBuilder {
          private User user;
          private User currentUser;
          private UserService userService;
          private boolean ignoreIntention;
      }
  }
  