package io.delphiplatform.api.v3.view;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.NativeWebRequest;

import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.constant.CountryCodeConstant;
import io.delphiplatform.api.v3.constant.DspConstants;
import io.delphiplatform.api.v3.model.GroupByField;
import io.delphiplatform.api.v3.model.SortOrder;
import io.delphiplatform.api.v3.model.trackposition.PlaylistTrackPositionAdditions;
import io.delphiplatform.api.v3.model.trackposition.PlaylistTrackPositionSummaries;
import io.delphiplatform.api.v3.model.trackposition.PlaylistTrackPositions;
import io.delphiplatform.api.v3.validation.ExactMatches;
import io.delphiplatform.api.v3.validation.ExactMatchesForSingleValue;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;
import io.delphiplatform.api.v3.view.util.Params;

import static io.delphiplatform.api.v3.constant.ApplicationConstants.COUNTRY_CODE_PATTERN;

@Deprecated
@Validated
public interface TrackPositionsApi {

    default Optional<NativeWebRequest> getRequest() {
        return Optional.empty();
    }

    /**
     * GET /track-positions/playlists : Search for TrackPositions Resource route for getting a list of &#x60;TrackPositions&#x60; with filtering
     * based on provided parameters. Currently only the latest results within the provided date range are included.
     *
     * @param startDate  The earliest date to include in the query range. Data is start_date inclusive. (required)
     * @param endDate    The latest date to include in the query. Data returned is end_date inclusive. (required)
     * @param dspUnused  preserved for params validation
     * @param playlistId (optional)
     * @param artistId   (optional)
     * @param isrc       Array of International Standard Recording Code (ISRC) numbers (optional, default to new ArrayList&lt;&gt;())
     * @param include    Include additional data like the &#x60;Playlist&#x60; object in each response item.  (optional, default to new
     *                   ArrayList&lt;&gt;())
     * @param limit      The maximum number of &#x60;items&#x60; to return in a single request. (optional)
     * @param offset     The number of &#x60;items&#x60; to offset for pagination. (optional)
     * @param limitRange Limit of the range to query in days to return in a single request. Format: `days:28` (optional)
     * @param cursor     Cursor to query, containing start date in format `date:2020-01-01` to return in a single request. (optional)
     * @param groupBy    Specific time-based term to group the data by (optional)
     * @param sortBy     Field name by which to sort the data. (optional)
     * @param sortOrder  Direction to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided. (optional, default to desc)
     * @return &#x60;PlaylistTrackPositions&#x60; response object (status code 200) or 400 Invalid Input Response (status code 400) or 401
     * Unauthorized (AuthError) Response (status code 401) or 500 Internal Error Response (status code 500)
     */
    @OnlyDeclaredParams
    @RequestMapping(value = "/track-positions/playlists",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PlaylistTrackPositions>> trackPositionsSearchPlaylists(
        @RequestParam(value = "start_date", required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date", required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
        @Pattern(regexp = "^[a-z]*$")
        @RequestParam(value = "dsp", required = false) String dspUnused, //preserved for params validation
        @RequestParam(value = "playlist_id", required = false) String playlistId,
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "artist_id", required = false) String artistId,
        @RequestParam(value = "isrc", required = false)
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes,
        @RequestParam(value = "streams_country_code", required = false, defaultValue = CountryCodeConstant.WORLDWIDE)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> streamsCountryCodes,
        @ExactMatches(anyOf = {"streams", "playlists", "track_info", "streams_for_period"})
        @RequestParam(value = "include", required = false) List<String> include,
        @Min(0) @Max(ApplicationConstants.MAX_BIGTABLE_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false) Integer offset,
        @Pattern(regexp = ApplicationConstants.PAGE_LIMIT_RANGE_PATTERN)
        @RequestParam(value = "limit_range", required = false) String limitRange,
        @Pattern(regexp = ApplicationConstants.PAGE_CURSOR_PATTERN)
        @RequestParam(value = "cursor", required = false) String cursor,
        @RequestParam(value = "group_by", required = false) GroupByField groupBy,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "playlist_id", "current", "date", "dsp", "isrc", "streams", "trend", "num_days_on", "num_weeks_on",
            "earliest_position_date", "latest_position_date", "previous", "top",
            "streaming_for_7_days.isrc_in_playlist", "streaming_for_7_days.playlist",
            "streaming_for_1_days.isrc_in_playlist", "previous_position_change_14_days",
            "trend_change_14_days", "last_date_change_14_days"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder
    ) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(Collections.singletonList(DspConstants.AMAZON))
            .playlistId(CollectionUtils.isEmpty(playlistId) ? null : Collections.singletonList(playlistId))
            .playlistTypes(playlistTypes)
            .artistId(artistId)
            .isrc(isrc)
            .countryCode(countryCodes)
            .streamsCountryCode(streamsCountryCodes)
            .include(include)
            .groupByField(groupBy)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        validateParams(params);
        return getPlaylistTrackPositions(loadAdditionalParams(params));
    }

    @OnlyDeclaredParams
    @RequestMapping(value = "/track-positions/playlists/summary",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PlaylistTrackPositionSummaries>> trackPositionsSearchPlaylistsSummary(
        @RequestParam(value = "start_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
        @RequestParam(value = "dsp", required = false) String dspUnused, //preserved for params validation
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "artist_id", required = false) String artistId,
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @RequestParam(value = "isrc", required = false)
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @Min(0) @Max(ApplicationConstants.MAX_BIGTABLE_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0)
        @RequestParam(value = "offset", required = false) Integer offset,
        @Pattern(regexp = ApplicationConstants.PAGE_LIMIT_RANGE_PATTERN)
        @RequestParam(value = "limit_range", required = false) String limitRange,
        @Pattern(regexp = ApplicationConstants.PAGE_CURSOR_PATTERN)
        @RequestParam(value = "cursor", required = false) String cursor,
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder
    ) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(List.of(DspConstants.AMAZON))
            .artistId(artistId)
            .playlistTypes(playlistTypes)
            .isrc(isrc)
            .groupByField(GroupByField.DATE)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        validateParams(params);
        return getPlaylistTrackPositionsSummaries(loadAdditionalParams(params));
    }

    /**
     * GET /track-positions/playlists/additions : Query for PlaylistTrackPositionAdditions Resource route for getting a list of
     * &#x60;PlaylistTrackPositionAdditions&#x60;.  This endpoint can be used to query for Playlists that a track has recently been added to.
     * Requests can also expose &#x60;streams&#x60; and the &#x60;Playlist&#x60; metadata by using the &#x60;include&#x60; parameter.
     *
     * @param startDate  The earliest date to include in the query range. Data is start_date inclusive. (required)
     * @param endDate    The latest date to include in the query. Data returned is end_date inclusive. (required)
     * @param isrc       International Standard Recording Code (ISRC) number (required)
     * @param include    Include additional data like the &#x60;Playlist&#x60; object in each response item.  (optional, default to new
     *                   ArrayList&lt;&gt;())
     * @param limitRange Pagination limit to specify the number of days to include in a single response page in the format: &#x60;days:28&#x60;.
     *                   Results spanning more than one page will return a &#x60;next_cursor&#x60; value in the response. Provide this via the
     *                   &#x60;cursor&#x60; parameter in subsequent requests to page through results. If the number of days provided exceeds the
     *                   number of days between the &#x60;start_date&#x60; (or &#x60;cursor&#x60;) before the &#x60;end_date&#x60;, the results
     *                   will only include data through the &#x60;end_date&#x60;. See more extensive documentation in [The Delphi API - Endpoints
     *                   &amp; Parameters - Paginating Results](https://data-analytics.atlassian.net/wiki/spaces/DDPS/pages/427720719/Endpoints+Parameters#Paginating-Results).
     *                   (optional)
     * @param cursor     This pagination parameter should be the value from a previous response&#39;s &#x60;next_cursor&#x60;, which contains the
     *                   starting point of the current page results&#39; data. Provide this updated value to effectively page through results when
     *                   using the &#x60;limit_range&#x60; parameter. Passing a cursor outside of the provided &#x60;start_date&#x60; and
     *                   &#x60;end_date&#x60; range will result in the &#x60;cursor&#x60; simply be ignored.  See more extensive documentation in
     *                   [The Delphi API - Endpoints &amp; Parameters - Paginating Results](https://data-analytics.atlassian.net/wiki/spaces/DDPS/pages/427720719/Endpoints+Parameters#Paginating-Results).
     *                   (optional)
     * @param limit      The maximum number of &#x60;items&#x60; to return in a single request (i.e.: a single page). (optional, default to
     *                   10000)
     * @param offset     The number of &#x60;items&#x60; to offset (aka skip) for pagination.  (optional, default to 0)
     * @param sortBy     Field name existing in the top level of an object in &#x60;items&#x60; by which to sort the results. (optional)
     * @param sortOrder  Direction to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided. (optional, default to desc)
     * @return &#x60;PlaylistTrackPositionSummaries&#x60; response object (status code 200) or A &#x60;400 Bad Request&#x60; response status
     * indicates a problem with the input provided by a client request. If you are receiving this error, check your request parameters are valid.
     * (status code 400) or A &#x60;401 Unauthorized&#x60; response status indicates that the request did not include a required Authorization
     * header, or that there was a problem authenticating the client. Possible problems include an expired or invalid token, or the Authorization
     * header is not in the expected format.  (status code 401) or A &#x60;500 Internal Error&#x60; response status is returned for any
     * application and server level errors that are not already associated with another error status. A 500 is used as a generic fallback error.
     * (status code 500)
     */
    @OnlyDeclaredParams
    @RequestMapping(value = "/track-positions/playlists/additions",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PlaylistTrackPositionAdditions>> trackPositionsSearchPlaylistAdditions(
        @RequestParam(value = "start_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
        @RequestParam(value = "dsp", required = false) List<String> dspUnused, //preserved for params validation
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @Pattern(regexp = ApplicationConstants.ISRC_PATTERN)
        @RequestParam(value = "isrc") String isrc,
        @RequestParam(value = "include", required = false) List<String> include,
        @Min(0) @Max(ApplicationConstants.MAX_BIGTABLE_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0)
        @RequestParam(value = "offset", required = false) Integer offset,
        @Pattern(regexp = ApplicationConstants.PAGE_LIMIT_RANGE_PATTERN)
        @RequestParam(value = "limit_range", required = false) String limitRange,
        @Pattern(regexp = ApplicationConstants.PAGE_CURSOR_PATTERN)
        @RequestParam(value = "cursor", required = false) String cursor,
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(Collections.singletonList(DspConstants.AMAZON))
            .playlistTypes(playlistTypes)
            .isrc(CollectionUtils.isEmpty(isrc) ? null : Collections.singletonList(isrc))
            .include(include)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        validateParams(params);
        return getPlaylistTrackPositionsAdditions(params);
    }

    CompletableFuture<ResponseEntity<PlaylistTrackPositions>> getPlaylistTrackPositions(Params params);

    CompletableFuture<ResponseEntity<PlaylistTrackPositionSummaries>> getPlaylistTrackPositionsSummaries(Params params);

    CompletableFuture<ResponseEntity<PlaylistTrackPositionAdditions>> getPlaylistTrackPositionsAdditions(Params params);

    void validateParams(Params params);

    Params loadAdditionalParams(Params params);
}
