content: package com.nonononoki.alovoa.service;
  
  import com.nonononoki.alovoa.Tools;
  import com.nonononoki.alovoa.component.TextEncryptorConverter;
  import com.nonononoki.alovoa.entity.User;
  import com.nonononoki.alovoa.entity.user.Gender;
  import com.nonononoki.alovoa.model.AlovoaException;
  import com.nonononoki.alovoa.model.SearchDto;
  import com.nonononoki.alovoa.model.UserDto;
  import com.nonononoki.alovoa.repo.*;
  import com.nonononoki.alovoa.util.AuthTestUtil;
  import org.apache.commons.lang3.StringUtils;
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.mockito.Mockito;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.test.context.ActiveProfiles;
  import org.springframework.test.context.bean.override.mockito.MockitoBean;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.time.LocalDateTime;
  import java.util.List;
  import java.util.Set;
  
  import static org.junit.jupiter.api.Assertions.*;
  import static org.mockito.ArgumentMatchers.any;
  
  @SpringBootTest
  @ActiveProfiles("test")
  @Transactional
  class SearchAndMessageServiceTest {
  
      @Autowired
      private RegisterService registerService;
  
      @Autowired
      private CaptchaService captchaService;
  
      @Autowired
      private UserService userService;
  
      @Autowired
      private SearchService searchService;
  
      @Autowired
      private MessageService messageService;
  
      @Autowired
      private UserRepository userRepo;
  
      @Autowired
      private UserLikeRepository userLikeRepo;
  
      @Autowired
      private UserHideRepository userHideRepo;
  
      @Autowired
      private UserBlockRepository userBlockRepo;
  
      @Autowired
      private UserReportRepository userReportRepo;
  
      @Autowired
      private UserNotificationRepository userNotificationRepo;
  
      @Autowired
      private ConversationRepository conversationRepo;
  
      @Autowired
      private MessageRepository messageRepo;
  
      @Autowired
      private TextEncryptorConverter textEncryptor;
  
      @Value("${app.age.min}")
      private int minAge;
  
      @Value("${app.age.max}")
      private int maxAge;
  
      @Value("${app.message.size}")
      private int maxMessageSize;
  
      @Value("${app.conversation.messages-max}")
      private int maxConvoMessages;
  
      @Value("${app.first-name.length-max}")
      private int firstNameLengthMax;
  
      @Value("${app.first-name.length-min}")
      private int firstNameLengthMin;
  
      @Value("${app.like.message.length}")
      private int likeMessageLength;
  
      @MockitoBean
      private AuthService authService;
  
      @MockitoBean
      private MailService mailService;
  
      private static final Long INTENTION_TEST = 1L;
  
      private static final int USER1_AGE = 18;
      private static final int USER2_AGE = 20;
      private static final int USER3_AGE = 30;
  
      private List<User> testUsers;
  
      @BeforeEach
      void before() throws Exception {
          Mockito.when(mailService.sendMail(Mockito.any(String.class), any(String.class), any(String.class),
                  any(String.class))).thenReturn(true);
          testUsers = RegisterServiceTest.getTestUsers(captchaService, registerService, firstNameLengthMax,
                  firstNameLengthMin);
      }
  
      @AfterEach
      void after() throws Exception {
          RegisterServiceTest.deleteAllUsers(userService, authService, captchaService, conversationRepo, userRepo);
      }
  
      @Test
      void test() throws Exception {
  
          User user1 = testUsers.get(0);
          User user2 = testUsers.get(1);
          User user3 = testUsers.get(2);
  
          // set location manually since no extra service is needed
          user1.setLocationLatitude(0.0);
          user1.setLocationLongitude(0.0);
  
          user2.setLocationLatitude(0.0);
          user2.setLocationLongitude(0.0);
  
          user3.setLocationLatitude(0.0);
          user3.setLocationLongitude(0.0);
  
          userRepo.saveAndFlush(user1);
          userRepo.saveAndFlush(user2);
          userRepo.saveAndFlush(user3);
  
          assertEquals(4, userRepo.count());
  
          String imgMimePng = "png";
          String INTEREST = "interest";
  
          // setup settings
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          byte[] img1 = Tools.resourceToBytes("img/profile1.png");
          userService.updateProfilePicture(img1, imgMimePng);
          userService.addInterest(INTEREST);
          userService.updateDescription("description1");
          userService.updateIntention(INTENTION_TEST);
          userService.updateMaxAge(maxAge);
          userService.updateMinAge(minAge);
          userService.updatePreferedGender(2, true);
  
          AuthTestUtil.mockGetCurrentUser(authService, user2);
          byte[] img2 = Tools.resourceToBytes("img/profile2.png");
          userService.updateProfilePicture(img2, imgMimePng);
          userService.addInterest(INTEREST);
          userService.updateDescription("description2");
          userService.updateIntention(INTENTION_TEST);
          userService.updateMaxAge(maxAge);
          userService.updateMinAge(minAge);
          userService.updatePreferedGender(1, true);
  
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          byte[] img3 = Tools.resourceToBytes("img/profile3.png");
          userService.updateProfilePicture(img3, imgMimePng);
          assertNotNull(user3.getProfilePicture());
          userService.addInterest(INTEREST);
          assertEquals(1, user3.getInterests().size());
          String description = "description3";
          userService.updateDescription(description);
          assertEquals(description, user3.getDescription());
          userService.updateIntention(INTENTION_TEST);
          assertEquals(INTENTION_TEST, user3.getIntention().getId());
          userService.updateMaxAge(maxAge);
          assertEquals(maxAge, user3.getPreferedMaxAge());
          userService.updateMinAge(minAge);
          assertEquals(minAge, user3.getPreferedMinAge());
  
          userService.updatePreferedGender(1, true);
          assertEquals(3, user3.getPreferedGenders().size());
          userService.updatePreferedGender(2, true);
          assertEquals(3, user3.getPreferedGenders().size());
          userService.updatePreferedGender(2, false);
          assertEquals(2, user3.getPreferedGenders().size());
  
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          List<UserDto> searchDtos1 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(2, searchDtos1.size());
  
          AuthTestUtil.mockGetCurrentUser(authService, user2);
          List<UserDto> searchDtos2 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(1, searchDtos2.size());
  
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          List<UserDto> searchDtos3 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(1, searchDtos3.size());
  
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          userService.updatePreferedGender(2, true);
          assertEquals(3, user3.getPreferedGenders().size());
          List<UserDto> searchDtos4 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(2, searchDtos4.size());
  
          // search filtered by location
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          List<UserDto> locationSearchDtos = searchService.searchComplete(SearchService.SearchParams.builder().latitude(90).longitude(180).showOutsideParameters(true).build()).getUsers();
          assertEquals(2, locationSearchDtos.size());
          List<UserDto> locationSearchDtos2 = searchService.searchComplete(SearchService.SearchParams.builder().latitude(0).longitude(0).showOutsideParameters(true).build()).getUsers();
          assertEquals(2, locationSearchDtos2.size());
  
          // search filtered by interest
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          List<UserDto> interestSearchDto2 = searchService.searchComplete(SearchService.SearchParams.builder().interests(Set.of(INTEREST)).showOutsideParameters(false).build()).getUsers();
          assertEquals(2, interestSearchDto2.size());
          List<UserDto> interestSearchDto = searchService.searchComplete(SearchService.SearchParams.builder().interests(Set.of(INTEREST + "DOESNOTEXIST")).showOutsideParameters(false).build()).getUsers();
          assertEquals(0, interestSearchDto.size());
  
          // search filtered by preferred gender
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          List<UserDto> genderSearchDto = searchService.searchComplete(SearchService.SearchParams.builder().preferredGenderIds(Set.of(Gender.OTHER)).showOutsideParameters(false).build()).getUsers();
          assertEquals(0, interestSearchDto.size());
          assertEquals(1, user1.getPreferedGenders().size());
          List<UserDto> genderSearchDto2 = searchService.searchComplete(SearchService.SearchParams.builder().preferredGenderIds(Set.of(Gender.MALE, Gender.FEMALE, Gender.OTHER)).showOutsideParameters(false).build()).getUsers();
          assertEquals(2, interestSearchDto2.size());
          assertEquals(3, user1.getPreferedGenders().size());
  
          // search filtered by preferred age
          AuthTestUtil.mockGetCurrentUser(authService, user2);
          List<UserDto> ageSearchDto1 = searchService.searchComplete(SearchService.SearchParams.builder().preferredMinAge(USER1_AGE + 1).build()).getUsers();
          assertEquals(1, ageSearchDto1.size());
          assertEquals(USER1_AGE + 1, user2.getPreferedMinAge());
          SearchDto searchResult2 = searchService.searchComplete(SearchService.SearchParams.builder().preferredMaxAge(USER3_AGE - 1).build());
          List<UserDto> ageSearchDto2 = searchResult2.getUsers();
          assertEquals(0, ageSearchDto2.size());
          assertEquals(USER3_AGE - 1, user2.getPreferedMaxAge());
          user2.getDates().setDateOfBirth(Tools.localDateToDate(LocalDateTime.now().minusYears(minAge).toLocalDate()));
          AuthTestUtil.mockGetCurrentUser(authService, user2);
          List<UserDto> ageSearchDto3 = searchService.searchComplete(SearchService.SearchParams.builder().preferredMaxAge(maxAge).preferredMinAge(minAge).build()).getUsers();
          assertEquals(0, ageSearchDto3.size());
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          List<UserDto> ageSearchDto4 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(1, ageSearchDto4.size());
          user2.getDates().setDateOfBirth(Tools.ageToDate(USER2_AGE));
  
          // likeUser
          List<UserDto> searchDtos6 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          assertEquals(2, searchDtos6.size());
          String longMessage = StringUtils.repeat("*", likeMessageLength);
          assertThrows(AlovoaException.class, () -> {
              userService.likeUser(user1.getUuid(), longMessage + "a");
          });
          userService.likeUser(user1.getUuid(), StringUtils.repeat("*", likeMessageLength));
          assertEquals(1, userLikeRepo.count());
          assertEquals(1, user3.getLikes().size());
          assertEquals(1, userNotificationRepo.count());
          List<UserDto> searchDtos7 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(1, searchDtos7.size());
  
          // hideUser
          userService.hideUser(user2.getUuid());
          assertEquals(1, userHideRepo.count());
          assertEquals(1, user3.getHiddenUsers().size());
          List<UserDto> searchDtos8 = searchService.searchComplete(SearchService.SearchParams.builder().build()).getUsers();
          assertEquals(0, searchDtos8.size());
  
          user3.getHiddenUsers().clear();
          userRepo.saveAndFlush(user3);
          assertEquals(0, userHideRepo.count());
  
          // blockUser
          userService.blockUser(user2.getUuid());
          assertEquals(1, userBlockRepo.count());
          assertEquals(1, user3.getBlockedUsers().size());
  
          assertThrows(Exception.class, () -> {
              // cannot like user when blocked
              userService.likeUser(user2.getUuid(), null);
          });
  
          userService.unblockUser(user2.getUuid());
          assertEquals(0, userBlockRepo.count());
  
          // like back
          assertThrows(Exception.class, () -> {
              userService.likeUser(user3.getUuid(), null);
          });
  
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          userService.likeUser(user3.getUuid(), null);
  
          assertEquals(2, userLikeRepo.count());
          assertEquals(2, userNotificationRepo.count());
          assertEquals(1, conversationRepo.count());
          assertEquals(1, user1.getConversations().size());
  
          messageService.send(user1.getConversations().get(0).getId(), "Hello");
          assertEquals(1, messageRepo.count());
  
          String verylongString = StringUtils.repeat("*", maxMessageSize);
          messageService.send(user1.getConversations().get(0).getId(), verylongString);
  
          assertEquals(2, messageRepo.count());
  
          assertThrows(Exception.class, () -> {
              messageService.send(user1.getConversations().get(0).getId(), verylongString + "a");
          });
  
          assertEquals(2, messageRepo.count());
  
          // test sending message to blocked users
          userService.blockUser(user3.getUuid());
          assertEquals(1, userBlockRepo.count());
          assertThrows(Exception.class, () -> {
              messageService.send(user3.getConversations().get(0).getId(), "Hello");
          });
  
          assertEquals(2, messageRepo.count());
  
          AuthTestUtil.mockGetCurrentUser(authService, user3);
          assertThrows(Exception.class, () -> {
              messageService.send(user1.getConversations().get(0).getId(), "Hello");
          });
  
          assertEquals(2, messageRepo.count());
  
          AuthTestUtil.mockGetCurrentUser(authService, user1);
          userService.unblockUser(user3.getUuid());
          assertEquals(0, userBlockRepo.count());
  
          messageService.send(user1.getConversations().get(0).getId(), verylongString);
          assertEquals(3, messageRepo.count());
  
          for (int i = 0; i < maxConvoMessages; i++) {
              messageService.send(user1.getConversations().get(0).getId(), verylongString);
          }
          assertEquals(maxConvoMessages, messageRepo.count());
  
          assertEquals(0, userReportRepo.count());
          userService.reportUser(user3.getUuid(), "report");
          assertEquals(1, userReportRepo.count());
      }
  
  }
  