package io.delphiplatform.api.v3.search.service;

import com.google.common.collect.ImmutableMap;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.PaginatedList;
import io.delphiplatform.api.v3.constant.SearchIndexNames;
import io.delphiplatform.api.v3.model.ArtistSimple;
import io.delphiplatform.api.v3.rdb.service.ArtistService;
import io.delphiplatform.api.v3.search.entity.ArtistDocumentEntity;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

class ArtistSearchServiceTest {

    private static final Map<String, String> ARTIST_ID_FULLNAME_MAP = ImmutableMap.<String, String>builder()
        .put("GRAS_1845580", "J.Durrill")
        .put("GRAS_2058475", "D.Dubbeldam")
        .put("GRAS_1572674", "J.Durrill")
        .put("GRAS_1122220", "Crisis.dub")
        .put("GRAS_1241107", "Prof.R.N.Dubey")
        .build();

    @Mock
    private ElasticsearchOperations elasticsearchOperations;
    @Mock
    ArtistService artistService;

    private ArtistSearchService artistSearchService;

    @BeforeEach
    public void setup() {
        MockitoAnnotations.openMocks(this);
        artistSearchService = new ArtistSearchService(artistService, null, null, elasticsearchOperations);
    }

    @Test
    void searchAndConvertAllByFullNameWithRdb() {
        long totalHits = 20;
        IndexCoordinates index = IndexCoordinates.of(SearchIndexNames.ARTIST);
        when(artistService.findAllById(anyList())).thenReturn(initArtistList());
        List<SearchHit<ArtistDocumentEntity>> searchHitsForArtists = initSearchHits();
        SearchHits<ArtistDocumentEntity> searchHits = new SearchHitsImpl<>(totalHits, TotalHitsRelation.EQUAL_TO, 1.0f,
            null, searchHitsForArtists, null);
        when(elasticsearchOperations.search((Query) any(), eq(ArtistDocumentEntity.class), eq(index)))
            .thenReturn(searchHits);
        PaginatedList<ArtistSimple> result = artistSearchService.searchAndConvertAllByFullNameWithRdb("any",
            ARTIST_ID_FULLNAME_MAP.size(),
            0);
        Assertions.assertEquals(totalHits, result.totalCount());
        Assertions.assertEquals(ARTIST_ID_FULLNAME_MAP.size(), result.size());
    }

    private static List<SearchHit<ArtistDocumentEntity>> initSearchHits() {
        return ARTIST_ID_FULLNAME_MAP.entrySet().stream()
            .map(entry -> {
                ArtistDocumentEntity artistDocumentEntity = new ArtistDocumentEntity();
                artistDocumentEntity.setArtistId(entry.getKey());
                artistDocumentEntity.setFullName(entry.getValue());

                return new SearchHit<>("artist-000003", entry.getKey(), null, 1.0f,
                    Collections.emptyList().toArray(), Collections.emptyMap(), Collections.emptyMap(), null, null,
                    Collections.emptyList(), artistDocumentEntity);
            })
            .collect(Collectors.toList());
    }

    private static List<ArtistSimple> initArtistList() {
        return ARTIST_ID_FULLNAME_MAP.entrySet().stream()
            .map(entry -> new ArtistSimple().artistId(entry.getKey()).fullName(entry.getValue()))
            .collect(Collectors.toList());
    }
}