package io.delphiplatform.api.v3.view;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;

import javax.transaction.NotSupportedException;

import io.delphiplatform.api.exception.InvalidParameterException;
import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.bigtable.BigtableStreamDatesService;
import io.delphiplatform.api.v3.bigtable.BigtableStreamsService;
import io.delphiplatform.api.v3.bigtable.BigtableTrackStreamsInsightsService;
import io.delphiplatform.api.v3.constant.DspConstants;
import io.delphiplatform.api.v3.constant.RequestConstants;
import io.delphiplatform.api.v3.model.AggBy;
import io.delphiplatform.api.v3.model.StreamDate;
import io.delphiplatform.api.v3.model.StreamDates;
import io.delphiplatform.api.v3.model.StreamModel;
import io.delphiplatform.api.v3.model.StreamsCountryCodeRankInfo;
import io.delphiplatform.api.v3.model.StreamsModel;
import io.delphiplatform.api.v3.model.StreamsNewListenersTopCountryResults;
import io.delphiplatform.api.v3.model.TrackStreamsInsightsItem;
import io.delphiplatform.api.v3.rdb.service.MarketRankService;
import io.delphiplatform.api.v3.rdb.service.spotify.SpotifyStreamsNewListenersService;
import io.delphiplatform.api.v3.view.util.Params;
import io.delphiplatform.api.v3.view.util.ParamsHelper;
import lombok.NonNull;

import static io.delphiplatform.api.v3.constant.DspConstants.AMAZON;
import static io.delphiplatform.api.v3.constant.DspConstants.APPLE;
import static io.delphiplatform.api.v3.constant.DspConstants.SPOTIFY;

@RestController
@RequestMapping("${spring.data.rest.base-path:/v3}")
public class StreamsApiController implements StreamsApi {

    private static final List<String> STREAMS_COUNTRY_CODE_RANK_ENABLED_DSPS = List.of(AMAZON, APPLE, SPOTIFY);

    private final ParamsHelper paramsHelper;
    private final BigtableStreamsService bigtableStreamsService;
    private final BigtableStreamDatesService bigtableStreamDatesService;
    private final BigtableTrackStreamsInsightsService bigtableTrackPositionStreamsInsightsService;

    private final SpotifyStreamsNewListenersService spotifyStreamsNewListenersService;
    private final MarketRankService marketRankService;

    public StreamsApiController(
        ParamsHelper paramsHelper,
        BigtableStreamsService bigtableStreamsService,
        BigtableStreamDatesService bigtableStreamDatesService,
        BigtableTrackStreamsInsightsService bigtableTrackPositionStreamsInsightsService,
        SpotifyStreamsNewListenersService spotifyStreamsNewListenersService,
        MarketRankService marketRankService
    ) {
        this.paramsHelper = paramsHelper;
        this.bigtableStreamsService = bigtableStreamsService;
        this.bigtableStreamDatesService = bigtableStreamDatesService;
        this.bigtableTrackPositionStreamsInsightsService = bigtableTrackPositionStreamsInsightsService;
        this.spotifyStreamsNewListenersService = spotifyStreamsNewListenersService;
        this.marketRankService = marketRankService;
    }

    @Override
    public CompletableFuture<ResponseEntity<StreamsModel>> getSearchData(@NonNull Params params) {
        try {
            CompletableFuture<List<StreamModel>> rows = bigtableStreamsService.getRows(params);
            return rows.thenApply(r -> ResponseEntity.ok(new StreamsModel()
                .items(r)
                .nextCursor(params.getNextCursor())
                .count(r.size())));
        } catch (NotSupportedException e) {
            throw new ResponseStatusException(HttpStatus.NOT_IMPLEMENTED, e.getMessage());
        }
    }

    @Override
    public void validateParams(@NonNull Params params) throws ResponseStatusException {
        String msg = null;
        AggBy aggBy = params.getAggBy();

        if (CollectionUtils.isEmpty(params.getArtistId())
            && CollectionUtils.isEmpty(params.getChartId())
            && CollectionUtils.isEmpty(params.getPlaylistId())
            && CollectionUtils.isEmpty(params.getTrackId())
            && CollectionUtils.isEmpty(params.getIsrc())) {
            msg = "Missing at least one identifying parameter from set: "
                + "[artist_id, playlist_id, track_id, isrc]";

        } else if (aggBy == AggBy.ARTIST) {
            if (CollectionUtils.isEmpty(params.getArtistId())) {
                msg = "Missing required param [artist_id] with provided agg_by=" + aggBy;
            }
            if (CollectionUtils.isNotEmpty(params.getPlaylistId())
                || CollectionUtils.isNotEmpty(params.getChartId())
                || CollectionUtils.isNotEmpty(params.getTrackId())
                || CollectionUtils.isNotEmpty(params.getIsrc())
                || CollectionUtils.isNotEmpty(params.getProductId())
                || params.getSubset() != null) {
                msg = "Parameters [playlist_id, track_id, isrc, product_id, subset] " +
                    "are not compatible with agg_by=" + aggBy;
            }
        } else if (aggBy == AggBy.ISRC) {
            if (CollectionUtils.isNotEmpty(params.getIsrc()) && CollectionUtils.isNotEmpty(params.getTrackId())) {
                msg = "Using parameters [track_id, isrc] together with agg_by=" + aggBy;
            }
        }
        if (msg != null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, msg);
        }
    }

    @Override
    public Params loadAdditionalParams(@NonNull Params params) {
        return paramsHelper.loadAdditionalParamsForTrackStreams(params);
    }

    @Override
    public CompletableFuture<ResponseEntity<StreamDates>> getStreamDates(Params params) {
        CompletableFuture<List<StreamDate>> rows = bigtableStreamDatesService.getStreamDates(params);
        return rows.thenApply(r -> ResponseEntity.ok(new StreamDates()
            .items(r)
            .count(r.size())));
    }

    @Override
    public ResponseEntity<StreamsNewListenersTopCountryResults> getStreamsNewListenersTopCountry(@NonNull Params params) {
        String dsp = params.getDsp().iterator().next();

        if (!DspConstants.ENABLED_DSPS.contains(dsp)) {
            throw new InvalidParameterException(RequestConstants.DSP, Stream.of(DspConstants.ENABLED_DSPS), dsp);
        }

        //currently - only Spotify is supported
        if (!dsp.equals(SPOTIFY)) {
            return ResponseEntity.ok(new StreamsNewListenersTopCountryResults(Collections.emptyList()));
        }

        return ResponseEntity.ok(new StreamsNewListenersTopCountryResults(Collections.singletonList(
            spotifyStreamsNewListenersService.findTracksForTopIsrcsOfMarket(params)
        )));
    }

    @Override
    public ResponseEntity<Map<String, List<TrackStreamsInsightsItem>>> getStreamsInsights(Params params) {
        return ResponseEntity.ok(bigtableTrackPositionStreamsInsightsService.calculateStreamsInsights(params));
    }

    @Override
    public ResponseEntity<Map<String, List<StreamsCountryCodeRankInfo>>> getStreamsCountryCodeRank(Params params) {
        if (CollectionUtils.isNotEmpty(params.getDsp())) {
            params.getDsp().forEach(dsp -> {
                if (!STREAMS_COUNTRY_CODE_RANK_ENABLED_DSPS.contains(dsp)) {
                    throw new InvalidParameterException(RequestConstants.DSP, Stream.of(STREAMS_COUNTRY_CODE_RANK_ENABLED_DSPS), dsp);
                }
            });
        }

        return ResponseEntity.ok(marketRankService.getStreamsMarketRank(params));
    }
}
