package io.delphiplatform.api.v3.view;

import org.hibernate.validator.constraints.ParameterScriptAssert;
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.model.GroupByField;
import io.delphiplatform.api.v3.model.PlaylistPublicPositionsDates;
import io.delphiplatform.api.v3.model.SortOrder;
import io.delphiplatform.api.v3.model.trackposition.PlaylistsTrackPositionsCurrentTracklists;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPositionAdditions;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPositionSummaries;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPositions;
import io.delphiplatform.api.v3.model.trackposition.PublicPlaylistTrackPreviousPositions;
import io.delphiplatform.api.v3.model.video.ExpandTo;
import io.delphiplatform.api.v3.validation.ExactMatches;
import io.delphiplatform.api.v3.validation.ExactMatchesForSingleValue;
import io.delphiplatform.api.v3.validation.ExactMatchesForStringCollection;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;
import io.delphiplatform.api.v3.view.util.Params;
import io.delphiplatform.api.v3.view.util.SortWithOrder;

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

@Validated
public interface TrackPositionsPublicApi {

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

    /**
     * GET /public/track-positions/playlists : public - 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 dsp                        Slug name of a DSP (optional)
     * @param playlistId                 (optional)
     * @param playlistTypes              filter response by playlist types (optional)
     * @param playlistOwnerCategoryId    filter response by playlist owner category id (optional)
     * @param playlistNameContains       filter response by playlist name (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 countryCodes               filter response by playlist position country code (optional)
     * @param appleCountryCodes          filter response by playlist position country code, affects Apple positions only (optional)
     * @param amazonCountryCodes         filter response by playlist position country code, affects Amazon positions only (optional)
     * @param streamsCountryCodes        include streams count only for specified countries (optional)
     * @param expandIsrcByTrackPositions expand single playlist position into multiple if there are >1 records with tracks of same isrc (optional,
     *                                   default to false)
     * @param includeIgnored             include ignored playlists into response (optional, default to false)
     * @param includeRemoved             include removed playlists into response (optional, default to false)
     * @param minAddedDate               Limits response by last_added_date from the response. 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 sortByList                 Field name(s) by which to sort the data. (optional)
     * @param sortOrderList              Directions to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided. (optional,
     *                                   default to desc)
     * @return &#x60;PublicPlaylistTrackPositions&#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
    @ParameterScriptAssert(lang = "groovy",
        message = ApplicationConstants.ERROR_MESSAGE_START_END_DATES_BOTH_PRESENT_OR_NULL,
        script = ApplicationConstants.SCRIPT_CHECK_DATES_BOTH_PRESENT_OR_NULL
    )
    @RequestMapping(value = "/public/track-positions/playlists",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PublicPlaylistTrackPositions>> trackPositionsSearchPlaylistsPublic(
        @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 dsp,
        @RequestParam(value = "playlist_id", required = false) List<String> playlistId,
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @RequestParam(value = "playlist_owner_category_id", required = false) String playlistOwnerCategoryId,
        @RequestParam(value = "playlist_name_contains", required = false) String playlistNameContains,
        @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 = "apple_country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> appleCountryCodes,
        @RequestParam(value = "amazon_country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> amazonCountryCodes,
        @RequestParam(value = "streams_country_code", required = false, defaultValue = CountryCodeConstant.WORLDWIDE)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> streamsCountryCodes,
        @RequestParam(value = "expand_isrc_by_track_positions", required = false, defaultValue = "false")
            Boolean expandIsrcByTrackPositions,
        @RequestParam(value = "include_ignored", required = false, defaultValue = "false")
            Boolean includeIgnored,
        @RequestParam(value = "include_removed", required = false, defaultValue = "false")
            Boolean includeRemoved,
        @RequestParam(value = "min_added_date", required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate minAddedDate,
        @ExactMatches(anyOf = {"playlists", "gras_track_info", "track_info", "streams_for_period",
            "meta_owner_categories", "playlist_dates", "num_tracks"})
        @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,
        @ExactMatchesForStringCollection(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "playlist_id", "current", "date", "dsp", "isrc", "trend", "num_days_on", "num_weeks_on",
            "earliest_position_date_time", "latest_position_date_time", "previous", "top",
            "streaming_info.worldwide.1.isrc_in_playlist", "streaming_info.worldwide.1.playlist",
            "streaming_info.worldwide.7.isrc_in_playlist", "streaming_info.worldwide.7.playlist",
            "streaming_info.worldwide.14.isrc_in_playlist", "streaming_info.worldwide.14.playlist",
            "previous_position_change_14_days",
            "trend_change_14_days", "last_date_change_14_days",
            "playlist.followers"
        })
        @RequestParam(value = "sort_by", required = false) List<String> sortByList,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") List<SortOrder> sortOrderList
    ) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(CollectionUtils.isEmpty(dsp) ? null : Collections.singletonList(dsp))
            .playlistId(playlistId)
            .playlistTypes(playlistTypes)
            .playlistOwnerCategoryId(playlistOwnerCategoryId == null ? null : Collections.singleton(playlistOwnerCategoryId))
            .playlistNameContains(playlistNameContains)
            .artistId(artistId)
            .isrc(isrc)
            .countryCode(countryCodes)
            .appleCountryCode(appleCountryCodes)
            .amazonCountryCode(amazonCountryCodes)
            .streamsCountryCode(streamsCountryCodes)
            .include(include)
            .groupByField(groupBy)
            .expandTo(expandIsrcByTrackPositions ? Collections.singleton(ExpandTo.TRACK_POSITIONS_OF_ISRC) : null)
            .includeIgnored(includeIgnored)
            .includeRemoved(includeRemoved)
            .minAddedDate(minAddedDate)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortList(parseSortList(sortByList, sortOrderList))
            .build();
        validateParams(params);
        return getPlaylistTrackPositions(loadAdditionalParams(params));
    }

    @OnlyDeclaredParams
    @RequestMapping(value = "/public/track-positions/playlists/summary",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PublicPlaylistTrackPositionSummaries>> trackPositionsSearchPlaylistsSummaryPublic(
        @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 dsp,
        @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,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "date", "isrc", "dsp", "total_playlists"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @ExactMatches(anyOf = {"asc", "desc", "ASC", "DESC"}, message = "Allowed values for sort_order: asc or desc")
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") String sortOrder
    ) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(CollectionUtils.isEmpty(dsp) ? null : Collections.singletonList(dsp))
            .artistId(artistId)
            .playlistTypes(playlistTypes)
            .isrc(isrc)
            .groupByField(GroupByField.DATE)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortBy(sortBy)
            .sortOrder(SortOrder.fromValue(sortOrder.toLowerCase()))
            .build();
        validateParams(params);
        return getPlaylistTrackPositionsSummaries(loadAdditionalParams(params));
    }

    /**
     * GET /public/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;PublicPlaylistTrackPositionAdditions&#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 = "/public/track-positions/playlists/additions",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PublicPlaylistTrackPositionAdditions>> trackPositionsSearchPlaylistAdditionsPublic(
        @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> dsp,
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @Pattern(regexp = ApplicationConstants.ISRC_PATTERN)
        @RequestParam(value = "isrc") String isrc,
        @ExactMatches(anyOf = {"playlists"})
        @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,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "playlist_id", "dsp", "isrc"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @ExactMatches(anyOf = {"asc", "desc", "ASC", "DESC"}, message = "Allowed values for sort_order: asc or desc")
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") String sortOrder) {
        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dsp(dsp)
            .playlistTypes(playlistTypes)
            .isrc(CollectionUtils.isEmpty(isrc) ? null : Collections.singletonList(isrc))
            .include(include)
            .limit(limit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .sortBy(sortBy)
            .sortOrder(SortOrder.fromValue(sortOrder.toLowerCase()))
            .build();
        validateParams(params);
        return getPlaylistTrackPositionsAdditions(params);
    }

    /**
     * GET /public/track-positions/previous/playlists : Search for track's previous playlists Resource route for getting a list of playlists which
     * contained requested track (isrc) at some point but not currently.
     *
     * @param isrc                    International Standard Recording Code (ISRC) number list
     * @param dsp                     Slug name of a DSP (optional)
     * @param countryCodes            filter response by playlist position country code
     * @param appleCountryCodes       filter response by playlist position country code, affects Apple positions only (optional)
     * @param playlistId              (optional)
     * @param playlistTypes           filter response by playlist types (optional)
     * @param playlistNameContains    filter response by playlist name (optional)
     * @param playlistOwnerCategoryId filter response by playlist owner category id (optional)
     * @param minRemovedDate          Limits response by removed_date from the response.
     * @param includeIgnored          include ignored playlists into response (optional, default to false)
     * @param includeRemoved          include removed playlists into response (optional, default to false)
     * @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 sortByList              Field name(s) by which to sort the data. (optional)
     * @param sortOrderList           Directions to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided. (optional, default
     *                                to desc)
     * @return &#x60;PublicPlaylistTrackPreviousPositions&#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 = "/public/track-positions/previous/playlists",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PublicPlaylistTrackPreviousPositions>> trackPositionsSearchPreviousPlaylistsPublic(
        @RequestParam(value = "isrc")
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @RequestParam(value = "playlist_id", required = false) List<String> playlistId,
        @RequestParam(value = "dsp", required = false) List<String> dsp,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes,
        @RequestParam(value = "apple_country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> appleCountryCodes,
        @RequestParam(value = "playlist_type", required = false) Set<String> playlistTypes,
        @RequestParam(value = "playlist_name_contains", required = false) String playlistNameContains,
        @RequestParam(value = "min_removed_date", required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate minRemovedDate,
        @RequestParam(value = "playlist_owner_category_id", required = false) String playlistOwnerCategoryId,
        @RequestParam(value = "include_ignored", required = false, defaultValue = "false") Boolean includeIgnored,
        @RequestParam(value = "include_removed", required = false, defaultValue = "false") Boolean includeRemoved,
        @ExactMatches(anyOf = {"playlists", "gras_track_info", "meta_owner_categories", "playlist_dates", "dsp_sync_date_time", "streams_for_period"})
        @RequestParam(value = "include", required = false) List<String> include,
        @RequestParam(value = "streams_country_code", required = false, defaultValue = CountryCodeConstant.WORLDWIDE)
        Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> streamsCountryCodes,
        @Min(0) @Max(ApplicationConstants.MAX_BIGTABLE_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false) Integer offset,
        @ExactMatchesForStringCollection(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "num_days_on", "earliest_position_date_time", "removed_date_time", "playlist_id", "playlist.followers"
        })
        @RequestParam(value = "sort_by", required = false) List<String> sortByList,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") List<SortOrder> sortOrderList
    ) {
        Params params = Params.builder()
            .isrc(isrc)
            .playlistId(CollectionUtils.isEmpty(playlistId) ? Collections.emptyList() : playlistId)
            .dsp(dsp)
            .appleCountryCode(CollectionUtils.isEmpty(appleCountryCodes) ? Collections.emptySet() : appleCountryCodes)
            .countryCode(countryCodes)
            .playlistTypes(playlistTypes)
            .playlistNameContains(playlistNameContains)
            .playlistOwnerCategoryId(playlistOwnerCategoryId == null ? null : Collections.singleton(playlistOwnerCategoryId))
            .minRemovedDate(minRemovedDate)
            .includeIgnored(includeIgnored)
            .includeRemoved(includeRemoved)
            .include(include)
            .streamsCountryCode(streamsCountryCodes)
            .limit(limit)
            .offset(offset)
            .sortList(parseSortList(sortByList, sortOrderList))
            .build();
        return trackPositionsSearchPreviousPlaylists(params);
    }

    /**
     * GET /public/track-positions/playlists/current-tracklist : Getting the most up-to-date tracklist of the playlist. For regular Spotify
     * playlist it fetches the data from Spotify API directly.
     *
     * @param playlistIds         Array of International Standard Recording Code (ISRC) number
     * @param dsp                 Slug name of a DSP (optional)
     * @param countryCodes        filter response by playlist position country code (optional)
     * @param streamsCountryCodes Streams for track, sums 7 days track streams, 1 day streams (optional)
     * @param include             Include additional data
     * @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 sortByList          Field name(s) by which to sort the data. (optional)
     * @param sortOrderList       Directions to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided.
     * @return &#x60;PlaylistsTrackPositionsCurrentTracklists&#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 = "/public/track-positions/playlists/current-tracklist",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<PlaylistsTrackPositionsCurrentTracklists>> trackPositionsCurrentTracklist(
        @RequestParam(value = "playlist_id") List<String> playlistIds,
        @RequestParam(value = "dsp", required = false) List<String> dsp,
        @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 = {"track_info", "streams_for_period", "playlist_dates", "num_tracks"})
        @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,
        @ExactMatchesForStringCollection(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "num_days_on", "earliest_position", "current", "last_added_date_time", "earliest_position_date_time"
        })
        @RequestParam(value = "sort_by", required = false) List<String> sortByList,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") List<SortOrder> sortOrderList) {
        Params params = Params.builder()
            .playlistId(playlistIds)
            .dsp(dsp)
            .countryCode(countryCodes)
            .streamsCountryCode(streamsCountryCodes)
            .include(include)
            .limit(limit)
            .offset(offset)
            .sortList(parseSortList(sortByList, sortOrderList))
            .build();
        return getCurrentTracklist(params);
    }

    /**
     * GET /public/track-positions/playlists/dates : Provide the information about when playlist was updated, synced or last sync try was.
     *
     * @param playlistId   (required)
     * @param countryCodes Typically a lower-case two letter code for the country (optional)
     * @return &#x60;PublicPlaylistDates&#x60; response object (status code 200) or 400 Invalid Input Response (status code 400) or 401
     * Unauthorized (AuthError) Response (status code 401) or 404 Not Found Error Response (status code 404) or 500 Internal Error Response
     * (status code 500)
     */
    @OnlyDeclaredParams
    @RequestMapping(value = "/public/track-positions/playlists/dates",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<PlaylistPublicPositionsDates> syncDates(
        @RequestParam(value = "playlist_id") String playlistId,
        @RequestParam(value = "country_code", required = false) Set<String> countryCodes,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "country_code", "dsp_update_date_time", "playlist_id", "playlist_updated_at_date_time"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder
    ) {
        return getPlaylistsTrackPositionsDates(playlistId, countryCodes, sortBy, sortOrder);
    }

    ResponseEntity<PlaylistPublicPositionsDates> getPlaylistsTrackPositionsDates(String playlistId, Set<String> countryCodes,
        String sortBy, SortOrder sortOrder);

    CompletableFuture<ResponseEntity<PlaylistsTrackPositionsCurrentTracklists>> getCurrentTracklist(Params params);

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

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

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

    CompletableFuture<ResponseEntity<PublicPlaylistTrackPreviousPositions>> trackPositionsSearchPreviousPlaylists(Params params);

    void validateParams(Params params);

    Params loadAdditionalParams(Params params);

    List<SortWithOrder> parseSortList(List<String> sortByList, List<SortOrder> sortOrderList);
}
