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

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

import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.delphiplatform.api.v3.model.apple.music.AppleMusicTrackChartsSearchResult;
import io.delphiplatform.api.v3.model.spotify.ChartTrackMetricsDimension;
import io.delphiplatform.api.v3.rdb.entity.apple.music.AppleMusicChartDateTrackKey;
import io.delphiplatform.api.v3.rdb.entity.apple.music.AppleMusicChartTrackEntity;
import io.delphiplatform.api.v3.view.util.Params;

@Service
@Transactional(readOnly = true)
public class AppleMusicTracksChartsChangesService {

    private static final LocalDate DEFAULT_PREV_DATE_IF_NO_DATA = LocalDate.EPOCH;

    private final AppleMusicChartTrackService chartTrackService;
    private final AppleMusicTrackService trackService;
    private final AppleMusicTracksChartsService tracksChartsService;

    @Autowired
    public AppleMusicTracksChartsChangesService(
        AppleMusicChartTrackService chartTrackService,
        AppleMusicTrackService trackService,
        AppleMusicTracksChartsService tracksChartsService) {
        this.chartTrackService = chartTrackService;
        this.trackService = trackService;
        this.tracksChartsService = tracksChartsService;
    }

    public List<AppleMusicTrackChartsSearchResult> getAdditions(Params params) {
        LocalDate requestedDate = getRequestedDateOrDefault(params.getDate());
        LocalDate requestedPreviousDate = getRequestedPreviousDateOrDefault(params.getPreviousDate(), requestedDate);

        return getChartTracksChanges(requestedPreviousDate, requestedDate, params);
    }

    public List<AppleMusicTrackChartsSearchResult> getRemovals(Params params) {
        LocalDate requestedDate = getRequestedDateOrDefault(params.getDate());
        LocalDate requestedPreviousDate = getRequestedPreviousDateOrDefault(params.getPreviousDate(), requestedDate);

        return getChartTracksChanges(requestedDate, requestedPreviousDate, params);
    }

    private List<AppleMusicTrackChartsSearchResult> getChartTracksChanges(
        LocalDate dateToExclude,
        LocalDate baseDate,
        Params params) {

        Set<String> appleTrackIdsToQuery = trackService.findAppleMusicTrackIds(params.getTrackId(), params.getIsrc());

        if (appleTrackIdsToQuery.isEmpty()) {
            return Collections.emptyList();
        }

        List<AppleMusicChartTrackEntity> tracksToExclude = chartTrackService.findChartTracks(
            params.getTrackIdSet(),
            params.getIsrcSet(),
            dateToExclude,
            params.getMinPosition(),
            params.getMaxPosition(),
            params.getMinRank(),
            params.getMaxRank(),
            params.getCountryCode(),
            params.getChartTrackMetricsDimension()
        );

        Set<AppleMusicChartDateTrackKey> chartTrackKeysToExclude = getKeysToExclude(baseDate, tracksToExclude,
            params.getChartTrackMetricsDimension());
        List<AppleMusicChartTrackEntity> delta = chartTrackService.findChartTracks(
            chartTrackKeysToExclude,
            params.getTrackIdSet(),
            params.getIsrcSet(),
            baseDate,
            params.getMinPosition(),
            params.getMaxPosition(),
            params.getMinRank(),
            params.getMaxRank(),
            params.getCountryCode(),
            params.getChartTrackMetricsDimension()
        );

        return tracksChartsService.getAppleTrackChartsSearchResults(params.getTrackStateIncludes(),
            params.getChartTrackMetricsDimension(), delta);
    }

    private Set<AppleMusicChartDateTrackKey> getKeysToExclude(LocalDate baseDate,
        List<AppleMusicChartTrackEntity> tracksToExclude,
        ChartTrackMetricsDimension chartTrackMetricsDimension) {
        Set<AppleMusicChartDateTrackKey> baseKeysToExclude = addIsrcBasedKeysIfRequired(tracksToExclude, baseDate,
            chartTrackMetricsDimension);

        return baseKeysToExclude.stream().peek(key -> key.setChartDate(baseDate)).collect(Collectors.toSet());
    }

    private Set<AppleMusicChartDateTrackKey> addIsrcBasedKeysIfRequired(List<AppleMusicChartTrackEntity> tracksToExclude,
        LocalDate baseDate,
        ChartTrackMetricsDimension chartTrackMetricsDimension) {
        Set<AppleMusicChartDateTrackKey> keysToExcludeBasedOnTrackId = tracksToExclude.stream()
            .map(AppleMusicChartTrackEntity::getChartDateTrackKey)
            .collect(Collectors.toSet());
        if (chartTrackMetricsDimension == null
            || chartTrackMetricsDimension == ChartTrackMetricsDimension.TRACK_ID) {
            return keysToExcludeBasedOnTrackId;
        }

        Set<AppleMusicChartDateTrackKey> trackWithSameIsrcForDate = chartTrackService
            .findTrackWithSameIsrcForDateByKey(keysToExcludeBasedOnTrackId, baseDate);

        return Stream.concat(keysToExcludeBasedOnTrackId.stream(), trackWithSameIsrcForDate.stream())
            .collect(Collectors.toSet());
    }

    private LocalDate getRequestedDateOrDefault(LocalDate requestDate) {
        if (requestDate != null) {
            return requestDate;
        }

        LocalDate earliestDateBefore = chartTrackService.getEarliestDateBefore(null);

        if (earliestDateBefore != null) {
            return earliestDateBefore;
        }

        return DEFAULT_PREV_DATE_IF_NO_DATA;
    }

    private LocalDate getRequestedPreviousDateOrDefault(LocalDate requestedPreviousDate, LocalDate requestedDate) {
        if (requestedPreviousDate != null) {
            return requestedPreviousDate;
        }

        //default is always requestedDate-1 because currently only 'daily' charts are supported for AppleMusic
        return requestedDate.minusDays(1);
    }
}
