package io.delphiplatform.api.v3.bigtable;

import org.openapitools.jackson.nullable.JsonNullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.transaction.NotSupportedException;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.bigtable.processing.PaginationService;
import io.delphiplatform.api.v3.bigtable.processing.PlaylistTrackPositionSummaryPublicAggregator;
import io.delphiplatform.api.v3.bigtable.processing.PlaylistTrackPositionSummarySortingPublicService;
import io.delphiplatform.api.v3.bigtable.processing.TrackPositionConverter;
import io.delphiplatform.api.v3.constant.CountryCodeConstant;
import io.delphiplatform.api.v3.constant.DspConstants;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPosition;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPositionSummary;
import io.delphiplatform.api.v3.rdb.service.dto.PublicPlaylistLookupFilters;
import io.delphiplatform.api.v3.view.util.Params;

import static io.delphiplatform.api.v3.bigtable.reader.BigtableColumns.PLAYLIST_ISRC_TRACK_POSITIONS;

@Service
public class BigtableTrackPositionSummaryPublicService {

    private static final Collection<String> POSITIONS_SUMMARY_SPOTIFY_APPLE_IGNORED_COLUMNS = List.of(PLAYLIST_ISRC_TRACK_POSITIONS);

    private final PaginationService paginationService;
    private final BigtableTrackPositionPublicManager bigtableTrackPositionPublicManager;
    private final BigtableTrackPositionPublicQueryExecutor trackPositionQueryExecutor;

    private final TrackPositionConverter trackPositionConverter;
    private final PlaylistTrackPositionSummaryPublicAggregator trackPositionSummaryPublicAggregator;
    private final PlaylistTrackPositionSummarySortingPublicService sortingService;

    public BigtableTrackPositionSummaryPublicService(
        PaginationService paginationService,
        BigtableTrackPositionPublicManager bigtableTrackPositionPublicManager,
        BigtableTrackPositionPublicQueryExecutor trackPositionQueryExecutor,
        TrackPositionConverter trackPositionConverter,
        PlaylistTrackPositionSummaryPublicAggregator trackPositionSummaryPublicAggregator,
        PlaylistTrackPositionSummarySortingPublicService sortingService
    ) {
        this.paginationService = paginationService;
        this.bigtableTrackPositionPublicManager = bigtableTrackPositionPublicManager;
        this.trackPositionQueryExecutor = trackPositionQueryExecutor;
        this.trackPositionConverter = trackPositionConverter;
        this.trackPositionSummaryPublicAggregator = trackPositionSummaryPublicAggregator;
        this.sortingService = sortingService;
    }

    @Transactional(readOnly = true)
    public CompletableFuture<List<PublicPlaylistTrackPositionSummary>> getPlaylistTrackPositionsSummaryModels(Params params)
        throws NotSupportedException {
        if (CollectionUtils.isNotEmpty(params.getArtistId())
            && CollectionUtils.isEmpty(params.getIsrc())) {
            return CompletableFuture.completedFuture(Collections.emptyList());
        }

        String requestedDsp = CollectionUtils.isEmpty(params.getDsp()) ? null : params.getDsp().get(0);

        return trackPositionQueryExecutor.getAllPublicPlaylistTrackPositions(params, null,
                POSITIONS_SUMMARY_SPOTIFY_APPLE_IGNORED_COLUMNS, Collections.emptyList())
            .thenApply(trackPositionConverter::convertPublicPlaylistPositions)
            //filter only US positions for Apple
            .thenApply(models -> models.filter(model ->
                !DspConstants.APPLE.equals(model.getDsp()) || CountryCodeConstant.US.equals(model.getCountryCode())))
            //filter models list by playlist properties
            .thenApply(models -> filterPositionsForSummaryByPlaylistProperties(models, params))
            //transform positions models into summary models
            .thenApply(trackPositionSummaryPublicAggregator::groupBy)
            .thenApply(models -> models.peek(m -> m.setDsp(JsonNullable.of(requestedDsp))))
            //perform sorting/paging
            .thenApply(
                models -> sortingService.sortPlaylistPositions(models, params.getSortBy(), params.getSortOrder()))
            .thenApply(models -> paginationService.getPage(models, params))
            .thenApply(models -> models.collect(Collectors.toList()));
    }

    private Stream<PublicPlaylistTrackPosition> filterPositionsForSummaryByPlaylistProperties(
        Stream<PublicPlaylistTrackPosition> modelsStream, Params params) {
        List<PublicPlaylistTrackPosition> models = modelsStream.collect(Collectors.toList());

        if (models.isEmpty()) {
            return Stream.empty();
        }

        Set<String> filteredPlaylistIds = bigtableTrackPositionPublicManager.findPlaylistIdsFilteredWithSingleAppleCountry(
            models.stream()
                .map(PublicPlaylistTrackPosition::getPlaylistId)
                .collect(Collectors.toSet()),
            PublicPlaylistLookupFilters.builder()
                .requireSaveTrackList(true)
                .requireSaveHistory(true)
                .playlistTypes(params.getPlaylistTypes())
                .appleCountryCode(CountryCodeConstant.US)
                .build()
        );

        return models.stream()
            //filter by just playlistId (no country) as Apple positions must be already filtered by US-only
            .filter(position -> filteredPlaylistIds.contains(position.getPlaylistId()));
    }
}
