/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.exoplatform.social.core.jpa.search;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

import org.exoplatform.commons.search.es.client.ElasticSearchingClient;
import org.exoplatform.commons.utils.CommonsUtils;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.PropertiesParam;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.manager.IdentityManagerImpl;
import org.exoplatform.social.core.profile.ProfileFilter;
import org.exoplatform.social.core.profileproperty.ProfilePropertyService;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertySetting;
import org.exoplatform.social.core.relationship.model.Relationship;
import org.exoplatform.social.core.search.Sorting;

@RunWith(MockitoJUnitRunner.class)
public class ProfileSearchConnectorTest {
    private ProfileSearchConnector profileSearchConnector;

    @Mock
    private ProfilePropertyService profilePropertyService;

    private static final MockedStatic<CommonsUtils> COMMONS_UTILS = mockStatic(CommonsUtils.class);

    @AfterClass
    public static void tearDown() {
      COMMONS_UTILS.close();
    }

    @Test
    public void testSearch() {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        ProfileFilter filter = new ProfileFilter();
        Identity identity1 = new Identity("test","usernameee");
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastName.raw": {"order": "ASC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "query_string" : {
                    "query" : "null",
                    "fields" : ["connections"]
                  }
                }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
          """;
        long offset = 0;
        long limit = 10;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
        Identity identity = new Identity("username","test");
        Relationship.Type type = Relationship.Type.CONFIRMED;
        List<String> result = profileSearchConnector.search(identity, filter, type, offset, limit);
        Assert.assertEquals(1, result.size());
    }

    @Test
    public void testSearchWithEmail() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        Identity identity1 = new Identity("test","test");
        Identity identity2 = new Identity("test2","test2");
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.FIRSTNAME, Sorting.OrderBy.DESC);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();

        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("test","test");
        filter.setSearchEmail(true);
        filter.setSearchUserName(true);
        filter.setName("te-s t");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("internal");
        filter.setEnrollmentStatus("notEnrolled");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"firstName.raw": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": false
                              }
                            },
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "external"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": [{
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }],
                                "must": {
                                 "term": {
                                   "external": false
                                   }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                }      ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*te-s* OR email:*te-s* OR userName:*te-s*) AND ( name.whitespace:*t* OR email:*t* OR userName:*t*)"
                    }
                }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        long offset = 0;
        long limit = 10;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);

        List<String> result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());
    }

    @Test
    public void testSearchWithNameOrUsername() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.DATE, Sorting.OrderBy.DESC);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();
        Identity identity1 = new Identity("test","test");
        Identity identity2 = new Identity("test2","test2");
        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("test","test");
        filter.setSearchEmail(false);
        filter.setSearchUserName(false);
        filter.setName("\\\"te-s t\\\"");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("external");
        filter.setEnrollmentStatus("noEnrollmentPossible");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        filter.setRemoteIds(Collections.singletonList("test"));
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            },
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
        long offset = 0;
        long limit = 10;
        List<String>  result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());

        //when
        filter.setEnrollmentStatus("enrolled");
        query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
          """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());
    }

    @Test
    public void testSearchWithProfileSetting() {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.DATE, Sorting.OrderBy.DESC);
        ProfilePropertySetting profilePropertySetting = mock(ProfilePropertySetting.class);
        lenient().when(profilePropertySetting.getPropertyName()).thenReturn("testProperty");
        when(profilePropertyService.getProfileSettingByName("testProperty")).thenReturn(profilePropertySetting);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();
        Identity identity1 = new Identity("test","test");
        Identity identity2 = new Identity("test2","test2");
        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("testProperty","valueProperty");
        filter.setSearchEmail(false);
        filter.setSearchUserName(false);
        filter.setName("\\\"te-s t\\\"");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("external");
        filter.setEnrollmentStatus("noEnrollmentPossible");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        filter.setRemoteIds(Collections.singletonList("test"));
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            },
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
          ,
            {
              "query_string": {
                "query": "testProperty.whitespace:*valueProperty*"
              }
           }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
        long offset = 0;
        long limit = 10;
        List<String>  result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());
    }
    @Test
    public void testSearchWithProfileSettingWithSpacedValue() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.DATE, Sorting.OrderBy.DESC);
        ProfilePropertySetting profilePropertySetting = mock(ProfilePropertySetting.class);
        lenient().when(profilePropertySetting.getPropertyName()).thenReturn("testProperty");
        when(profilePropertyService.getProfileSettingByName("testProperty")).thenReturn(profilePropertySetting);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();
        Identity identity1 = new Identity("test","test");
        Identity identity2 = new Identity("test2","test2");
        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("testProperty","value of test Property");
        filter.setSearchEmail(false);
        filter.setSearchUserName(false);
        filter.setName("\\\"te-s t\\\"");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("external");
        filter.setEnrollmentStatus("noEnrollmentPossible");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        filter.setRemoteIds(Collections.singletonList("test"));
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            },
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
          ,
            {
              "query_string": {
                "query": " testProperty.whitespace:*value* AND  testProperty.whitespace:*of* AND  testProperty.whitespace:*test* AND  testProperty.whitespace:*Property*"
              }
           }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
        long offset = 0;
        long limit = 10;
        List<String>  result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());
    }

    @Test
    public void testSearchWithProfileSettingAndSpecialChars() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.DATE, Sorting.OrderBy.DESC);
        ProfilePropertySetting profilePropertySetting = mock(ProfilePropertySetting.class);
        lenient().when(profilePropertySetting.getPropertyName()).thenReturn("testProperty");
        when(profilePropertyService.getProfileSettingByName("testProperty")).thenReturn(profilePropertySetting);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();
        Identity identity1 = new Identity("test","test");
        Identity identity2 = new Identity("test2","test2");
        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("testProperty","value+ -=&&||of><!(){} test[]^\"~*? Property:\\/");
        filter.setSearchEmail(false);
        filter.setSearchUserName(false);
        filter.setName("\\\"te-s t\\\"");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("external");
        filter.setEnrollmentStatus("noEnrollmentPossible");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        filter.setRemoteIds(Collections.singletonList("test"));
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 10,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            },
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
          ,
            {
              "query_string": {
                "query": " testProperty.whitespace:*value* AND  testProperty.whitespace:*of* AND  testProperty.whitespace:*test* AND  testProperty.whitespace:*Property*"
              }
           }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");
        COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
        long offset = 0;
        long limit = 10;
        List<String>  result = profileSearchConnector.search(null, filter, null, offset, limit);
        Assert.assertEquals(1, result.size());
    }

    @Test
    public void testCount() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
        profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
        ProfileFilter filter = new ProfileFilter();
        Sorting sorting = new Sorting(Sorting.SortBy.DATE, Sorting.OrderBy.DESC);
        Map <String,String> profileSettings = new HashMap<>();
        List<Identity> excludedIdentityList = new ArrayList<>();
        Identity identity1 = new Identity("test1","test1");
        Identity identity2 = new Identity("test2","test2");
        excludedIdentityList.add(identity1);
        excludedIdentityList.add(identity2);
        profileSettings.put("test","test");
        filter.setSearchEmail(false);
        filter.setSearchUserName(false);
        filter.setName("\\\"te-s t\\\"");
        filter.setEnabled(true);
        filter.setConnected(true);
        filter.setSorting(sorting);
        filter.setUserType("external");
        filter.setEnrollmentStatus("noEnrollmentPossible");
        filter.setProfileSettings(profileSettings);
        filter.setExcludedIdentityList(excludedIdentityList);
        filter.setRemoteIds(Collections.singletonList("test"));
        String index = "profile_alias";
        String query = """
          {
             "from" : 0, "size" : 1,
             "sort": {"lastUpdatedDate": {"order": "DESC"}}
          ,
          "query" : {
                "constant_score" : {
                  "filter" : {
                    "bool" :{
                "must": [
                {
                  "bool": {
                    "should": [
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "bool": {
                    "should": [
                            {
                              "bool": {
                                "must_not": {
                                  "exists": {
                                    "field": "enrollmentDate"
                                    }
                                  },
                                "must": {
                                  "exists": {
                                    "field": "lastLoginTime"
                                  }
                                }
                              }
                            },
                            {
                              "term": {
                                "external": true
                              }
                            }          ],
                    "minimum_should_match" : 1
                  }
                },
                {
                  "terms" :{
                    "userName" : ["test"]
                  }\s
                }
                ]
                ,
                "must_not": [
                {
                    "ids" : {
                       "values" : ["null","null"]
                    }
                  }
                ]
                ,
                "filter": [
                {          "query_string": {
                      "query": "( name.whitespace:*\\"te-s*) AND ( name.whitespace:*t\\"*)"
                    }
                }
                ]
               }\s
             }\s
            }
           }
          ,"_source": false
          ,"fields": ["_id"]
          }
                """;
        when(elasticSearchClient.sendRequest(query, index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"test\"]},\"sort\":[\"test\"]}]}}");


        int result = profileSearchConnector.count(null, filter, null);
        Assert.assertEquals(1, result);
    }

    @Test
    public void testSearchWithSpace() {
      ElasticSearchingClient elasticSearchClient = Mockito.mock(ElasticSearchingClient.class);
      profileSearchConnector = new ProfileSearchConnector(getInitParams(), elasticSearchClient, profilePropertyService);
      IdentityManagerImpl identityManager = Mockito.mock(IdentityManagerImpl.class);
      ProfileFilter filter = new ProfileFilter();
      filter.setSearchEmail(false);
      filter.setSearchUserName(false);
      filter.setName("\\\"aaa\\\"");
      filter.setEnabled(true);
      filter.setUserType("internal");
      filter.setSpaceIdentityIds(Arrays.asList("5"));
      filter.setEnrollmentStatus("noEnrollmentPossible");
      String index = "profile_alias";
      String expectedQuery = """
        {
           "from" : 0, "size" : 10,
           "sort": {"lastName.raw": {"order": "ASC"}}
        ,
        "query" : {
              "constant_score" : {
                "filter" : {
                  "bool" :{
              "must": [
              {
                "terms": {
                  "permissions": [
        5
                  ]
                }
              },
              {
                "bool": {
                  "should": [
                          {
                            "term": {
                              "external": false
                            }
                          },
                          {
                            "bool": {
                              "must_not": {
                                "exists": {
                                  "field": "external"
                                }
                              }
                            }
                          }          ],
                  "minimum_should_match" : 1
                }
              },
              {
                "bool": {
                  "should": [
                          {
                            "bool": {
                              "must_not": {
                                "exists": {
                                  "field": "enrollmentDate"
                                  }
                                },
                              "must": {
                                "exists": {
                                  "field": "lastLoginTime"
                                }
                              }
                            }
                          },
                          {
                            "term": {
                              "external": true
                            }
                          }          ],
                  "minimum_should_match" : 1
                }
              }      ]
              ,
              "filter": [
              {          "query_string": {
                    "query": "name.whitespace:*\\"aaa\\"*"
                  }
              }
              ]
             }\s
           }\s
          }
         }
        ,"_source": false
        ,"fields": ["_id"]
        }
              """;
      when(elasticSearchClient.sendRequest(expectedQuery,
                                           index)).thenReturn("{\"took\":39,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[{\"_index\":\"profile_v2\",\"_type\":\"_doc\",\"_id\":\"6\",\"_score\":null,\"fields\":{\"userName\":[\"aaa\"]},\"sort\":[\"aaa\"]}]}}");
      COMMONS_UTILS.when(() -> CommonsUtils.getService(Mockito.any())).thenReturn(identityManager);
      long offset = 0;
      long limit = 10;
      List<String> result = profileSearchConnector.search(null, filter, null, offset, limit);
      Assert.assertEquals(1, result.size());
    }

    private String buildAdvancedFilterQuery(ProfileFilter filter) throws NoSuchMethodException,
                                                                  InvocationTargetException,
                                                                  IllegalAccessException {
      Method method = profileSearchConnector.getClass().getDeclaredMethod("buildAdvancedFilterExpression", ProfileFilter.class);
      method.setAccessible(true);
      return (String) method.invoke(profileSearchConnector, filter);
    }
    
    private InitParams getInitParams() {
        InitParams params = new InitParams();
        PropertiesParam constructorParams = new PropertiesParam();
        constructorParams.setName("constructor.params");
        constructorParams.setProperty("searchType", "profile_alias");
        constructorParams.setProperty("displayName", "profile_alias");
        constructorParams.setProperty("index", "profile_alias");
        constructorParams.setProperty("type", "profile_alias");
        constructorParams.setProperty("searchFields", "name");
        params.addParam(constructorParams);
        return params;
    }
}
