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

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;

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.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.model.IncludeChart;
import io.delphiplatform.api.v3.model.shazam.ShazamChartTrackPosition;
import io.delphiplatform.api.v3.rdb.entity.shazam.FactShazamChartEntity;
import io.delphiplatform.api.v3.rdb.repository.FactShazamTrackDailyRepository;
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;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.COUNTRY_CODE;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.DATE_PROPERTY;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.LATEST_POSITION;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.REGION;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SHAZAM_CITY;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.SHAZAM_CITY_ID;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.TRACK;

@Service
@Transactional(readOnly = true)
public class ShazamChartTrackPositionService extends EntityService<FactShazamChartEntity> {

    private static final ImmutableMap<String, String> SORT_BY_TO_ENTITY_FIELD_MAP = ImmutableMap
        .<String, String>builder()
        .put("chart.chart_id", "chart_chart_id")
        .put("chart.name", "chart_name")
        .put("chart.city_id", "chart_shazam_city_shazam_city_id")
        .put("chart.city_name", "chart_shazam_city_city_name")
        .put("chart.total_positions", "total_positions")
        .put("chart.country_code", "chart_country_code_region_id")
        .put("chart.dsp", "chart_dsp_name")
        .put("position.current", "latest_position")
        .put("position.date", "date")
        .put("position.date_gap", "date_gap")
        .put("position.is_entry", "is_entry")
        .put("position.is_exit", "is_exit")
        .put("position.is_reentry", "is_reentry")
        .put("position.previous_date", "previous_date")
        .put("position.previous_position", "previous_position")
        .put("position.trend", "trend")
        .put("position.total_days", "on_chart_days")
        .put("track.name", "track_track_name")
        .put("track.apple_track_id", "track_apple_track_id")
        .put("track.is_sony", "track_is_sony")
        .put("track.isrc", "track_isrc")
        .put("track.release_date", "track_release_date")
        .build();
    private final SpecificationProvider<FactShazamChartEntity> specificationProvider;
    private final ShazamArtistService artistService;

    @Autowired
    public ShazamChartTrackPositionService(FactShazamTrackDailyRepository repository,
        SpecificationProvider<FactShazamChartEntity> specificationProvider,
        ShazamArtistService artistService) {
        super(repository, SORT_BY_TO_ENTITY_FIELD_MAP);
        this.specificationProvider = specificationProvider;
        this.artistService = artistService;
    }

    public List<ShazamChartTrackPosition> getShazamChartTrackPositions(Params params) {
        Set<JpaPropertyPath> requestFetches = Sets
            .newHashSet(JpaPropertyPath.ofJoin(TRACK));

        Specification<FactShazamChartEntity> spec = and(Arrays.asList(
            specificationProvider.greaterThanOrEqualToValueSpec(DATE_PROPERTY, params.getStartDate()),
            specificationProvider.lessThanOrEqualToValueSpec(DATE_PROPERTY, params.getEndDate()),
            specificationProvider.multiValueSpec("chartId", params.getChartId(), CHART),
            specificationProvider.singleValueSpec("isSony", params.getIsSony(), TRACK),
            specificationProvider.singleValueSpec("isExit", params.getIsExit()),
            specificationProvider.multiValueSpec("isrc", params.getIsrc(), TRACK),
            specificationProvider.greaterThanOrEqualToValueSpec(LATEST_POSITION, params.getMinPosition()),
            specificationProvider.lessThanOrEqualToValueSpec(LATEST_POSITION, params.getMaxPosition()),
            specificationProvider.multiValueSpec(List.of(CHART, SHAZAM_CITY, SHAZAM_CITY_ID), params.getCityCode()),
            specificationProvider.multiValueSpec(List.of(CHART, REGION, COUNTRY_CODE), params.getCountryCode()),
            specificationProvider.fetchesSpecOfPaths(requestFetches)
        ));

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

        Map<String, List<String>> isrcToArtists = artistService.findByIsrcToArtistNames(entities
            .stream()
            .map(e -> e.getTrack().getIsrc())
            .collect(Collectors.toSet()));

        boolean includeTrack = CollectionUtils.contains(params.getIncludeChart(), IncludeChart.TRACK);
        return entities.stream().map(elem -> ShazamChartTrackPosition.of(elem, includeTrack,
                isrcToArtists.get(elem.getTrack().getIsrc())))
            .collect(Collectors.toList());
    }

}
