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

import com.google.common.collect.ImmutableMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
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.v3.model.spotify.SpotifyChart;
import io.delphiplatform.api.v3.rdb.entity.spotify.SpotifyChartEntity;
import io.delphiplatform.api.v3.rdb.repository.spotify.SpotifyChartRepository;
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 io.delphiplatform.api.v3.view.util.Params;

import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CHART_RANK;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.MARKET_RANK;
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;

@Service
@Transactional(readOnly = true)
public class SpotifyChartService extends EntityService<SpotifyChartEntity> {
    private static final ImmutableMap<String, String> SORT_BY_TO_ENTITY_FIELD_MAP =
        ImmutableMap.<String, String>builder().build();

    private final SpecificationProvider<SpotifyChartEntity> specificationProvider;

    @Autowired
    public SpotifyChartService(
        SpotifyChartRepository repository,
        SpecificationProvider<SpotifyChartEntity> specificationProvider
    ) {
        super(repository, SORT_BY_TO_ENTITY_FIELD_MAP);
        this.specificationProvider = specificationProvider;
    }

    @Transactional(readOnly = true)
    public List<SpotifyChart> getSpotifyCharts(Params params) {
        Set<JpaPropertyPath> fetches = Set.of(JpaPropertyPath.ofJoin(MARKET_RANK));

        Specification<SpotifyChartEntity> spec = and(Arrays.asList(
            specificationProvider.multiValueSpec(SPOTIFY_CHART_ID, params.getChartId()),
            specificationProvider.multiValueSpec(SPOTIFY_COUNTRY_ID, params.getCountryCode()),
            specificationProvider.multiValueSpec(SPOTIFY_CHART_TYPE, params.getTypes()),
            specificationProvider.multiValueSpec(SPOTIFY_CHART_BREAKDOWN, params.getBreakdowns()),
            specificationProvider.greaterThanOrEqualToValueSpec(CHART_RANK, params.getMinRank(),
                Collections.singletonList(MARKET_RANK)),
            specificationProvider.lessThanOrEqualToValueSpec(CHART_RANK, params.getMaxRank(),
                Collections.singletonList(MARKET_RANK)),
            specificationProvider.fetchesSpecOfPaths(fetches)
        ));

        List<SpotifyChartEntity> entities = find(spec, getPageRequestSortByToCamelCase(params));

        return entities.stream().map(SpotifyChart::of).collect(Collectors.toList());
    }

    public Map<String, SpotifyChartEntity> findChartsByIdsWithMarketRank(Collection<String> chartIds) {
        Set<JpaPropertyPath> fetches = Set.of(JpaPropertyPath.ofJoin(MARKET_RANK));

        Specification<SpotifyChartEntity> spec = and(Arrays.asList(
            specificationProvider.multiValueSpec(SPOTIFY_CHART_ID, chartIds),

            specificationProvider.fetchesSpecOfPaths(fetches)
        ));

        return find(spec, null)
            .stream()
            .collect(Collectors.toMap(SpotifyChartEntity::getSpotifyChartId, Function.identity()));
    }
}
