package io.delphiplatform.api.v3.rdb.service.spotify;

import com.google.common.collect.ImmutableMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.util.JpaUtils;
import io.delphiplatform.api.v3.model.spotify.SpotifyChartBreakdown;
import io.delphiplatform.api.v3.model.spotify.SpotifyChartType;
import io.delphiplatform.api.v3.rdb.entity.spotify.IsrcAndChartAndTrackIds;
import io.delphiplatform.api.v3.rdb.entity.spotify.SpotifyChartTrackLifetimeEntity;
import io.delphiplatform.api.v3.rdb.entity.spotify.SpotifyTrackId;
import io.delphiplatform.api.v3.rdb.repository.spotify.SpotifyChartTrackLifetimeRepository;
import io.delphiplatform.api.v3.rdb.service.EntityService;
import io.delphiplatform.api.v3.rdb.service.dto.JpaPropertyPath;
import io.delphiplatform.api.v3.rdb.service.specification.SpecificationProvider;

import static io.delphiplatform.api.util.CollectionUtils.firstOfTwoMerger;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.ARTISTS;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CHART;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CHART_RANK;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.ISRC;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.MARKET_RANK;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_CHART;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_CHART_BREAKDOWN;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_CHART_ID;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_CHART_TYPE;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_COUNTRY_ID;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SPOTIFY_TRACK_ID;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.TRACK;

@Service
@Transactional(readOnly = true)
public class SpotifyChartTrackLifetimeService extends EntityService<SpotifyChartTrackLifetimeEntity> {

    private static final ImmutableMap<String, String> SORT_BY_TO_ENTITY_FIELD_MAP =
        ImmutableMap.<String, String>builder().build();

    private final SpotifyChartTrackLifetimeRepository repository;
    private final SpecificationProvider<SpotifyChartTrackLifetimeEntity> specificationProvider;

    @Autowired
    public SpotifyChartTrackLifetimeService(SpotifyChartTrackLifetimeRepository repository,
        SpecificationProvider<SpotifyChartTrackLifetimeEntity> specificationProvider) {
        super(repository, SORT_BY_TO_ENTITY_FIELD_MAP);
        this.repository = repository;
        this.specificationProvider = specificationProvider;
    }

    public Map<SpotifyTrackId, SpotifyChartTrackLifetimeEntity> findTrackLifetimeMetrics(
        Set<String> spotifyTrackIds,
        String chartId
    ) {
        return repository.findAll(JpaUtils.and(List.of(
                specificationProvider.multiValueSpec(
                    List.of(SPOTIFY_TRACK_ID), spotifyTrackIds),
                specificationProvider.singleValueSpec(
                    List.of(SPOTIFY_CHART_ID), chartId)
            ))).stream()
            .collect(Collectors.toMap(e -> e.getTrack().getId(), Function.identity(), firstOfTwoMerger()));
    }

    public Map<IsrcAndChartAndTrackIds, SpotifyChartTrackLifetimeEntity> findChartTrackLifetimeMetricsByIds(
        List<IsrcAndChartAndTrackIds> ids
    ) {
        return repository.findAllByChartAndTrackIdsIn(ids)
            .stream()
            .collect(Collectors.toMap(SpotifyChartTrackLifetimeEntity::getIsrcAndChartAndTrackIds,
                Function.identity(),
                CollectionUtils.firstOfTwoMerger()));
    }

    public List<SpotifyChartTrackLifetimeEntity> findChartTrackLifetimeMetrics(
        Set<String> trackIds,
        Set<String> isrcs,
        Integer minRank, Integer maxRank,
        Set<String> countryCode,
        Set<SpotifyChartBreakdown> breakdowns,
        Set<SpotifyChartType> types
    ) {
        return repository.findAll(JpaUtils.and(Arrays.asList(
            specificationProvider.multiValueSpec(SPOTIFY_TRACK_ID, trackIds),
            specificationProvider.multiValueSpec(ISRC, isrcs),

            specificationProvider.greaterThanOrEqualToValueSpec(CHART_RANK, minRank, List.of(CHART, MARKET_RANK)),
            specificationProvider.lessThanOrEqualToValueSpec(CHART_RANK, maxRank, List.of(CHART, MARKET_RANK)),

            specificationProvider.multiValueSpec(SPOTIFY_COUNTRY_ID, countryCode),
            specificationProvider.multiValueSpec(List.of(SPOTIFY_CHART, SPOTIFY_CHART_BREAKDOWN), breakdowns),
            specificationProvider.multiValueSpec(List.of(SPOTIFY_CHART, SPOTIFY_CHART_TYPE), types),

            specificationProvider.fetchesSpecOfPaths(List.of(
                JpaPropertyPath.ofJoins(TRACK, ARTISTS),
                JpaPropertyPath.ofJoins(SPOTIFY_CHART, MARKET_RANK)
            ), true)
        )));
    }
}
