package io.delphiplatform.api.v3.view.video;

import com.google.common.collect.Sets;

import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
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 java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Set;

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.model.SortOrder;
import io.delphiplatform.api.v3.model.video.ExpandTo;
import io.delphiplatform.api.v3.model.video.IncludeVideoChart;
import io.delphiplatform.api.v3.model.video.VideoChartGroup;
import io.delphiplatform.api.v3.model.video.VideoChartPositionSummaries;
import io.delphiplatform.api.v3.model.video.VideoChartPositions;
import io.delphiplatform.api.v3.model.video.VideoDspSlug;
import io.delphiplatform.api.v3.model.video.YoutubeContentType;
import io.delphiplatform.api.v3.view.util.ApiUtil;
import io.delphiplatform.api.v3.view.util.Params;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;
import io.swagger.annotations.Api;


@Validated
@Api(value = "video-positions")
public interface VideoPositionsApi {

    /**
     * GET /video-positions/charts : Get Video Chart positions Resource route for getting a list of
     * &#x60;VideoChartPositions&#x60;. At least one of the identifying keys is required in the request to filter data:
     * - &#x60;artist_id&#x60; - &#x60;chart_id&#x60; - &#x60;isrc&#x60; - &#x60;video_id&#x60;
     *
     * @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 artistId    An optional &#x60;artist_id&#x60; query parameter. (optional)
     * @param chartGroup  Identifier for a video chart group (category). (optional)
     * @param chartId     (optional)
     * @param countryCode Typically a lower-case two letter code for the country (optional)
     * @param dsp         The slug name of a video DSP. This is a lowercase value that is the same as the
     *                    &#x60;dsp_id&#x60;. (optional)
     * @param expandTo    Providing &#x60;expand_to&#x3D;related_isrcs&#x60; will expand the query to include data from
     *                    related ISRCs by using the parent Product Family. If this is provided, an &#x60;isrc&#x60; is
     *                    required to be sent with the request.  (optional, default to new ArrayList&lt;&gt;())
     * @param include     Include additional data like the &#x60;Video&#x60; object in each response item.  (optional,
     *                    default to new ArrayList&lt;&gt;())
     * @param isrc        Array of International Standard Recording Code (ISRC) numbers (optional, default to new
     *                    ArrayList&lt;&gt;())
     * @param videoId     Array of Primary key identifiers for a Video (optional)
     * @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)
     * @param offset      The number of &#x60;items&#x60; to offset (aka skip) for pagination.  (optional, default to
     *                    0)
     * @param sortBy      Field name existing within objects in &#x60;items&#x60; by which to sort the results (dot
     *                    notation).  (optional)
     * @param sortOrder   Direction to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided.
     *                    (optional, default to desc)
     * @return &#x60;VideoChartPositions&#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
    @ParameterScriptAssert.List({
        @ParameterScriptAssert(lang = "groovy",
            message = "Missing at least one identifying parameter from set: [artist_id, chart_id, isrc, video_id]",
            script = "artistId || chartId || isrc || videoId"),
        @ParameterScriptAssert(lang = "groovy",
            message = "ISRC is required if expand_to=related_isrc provided",
            script = "!expandTo || expandTo && isrc"),
        @ParameterScriptAssert(lang = "groovy",
            message = ApplicationConstants.ERROR_MESSAGE_START_END_DATES,
            script = ApplicationConstants.SCRIPT_CHECK_DATES)
    })
    @RequestMapping(value = "/video-positions/charts",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<VideoChartPositions> videoPositionsChartsSearch(
        @RequestParam(value = "start_date") @DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date") @DateTimeFormat(iso = ISO.DATE) LocalDate endDate,
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "artist_id", required = false) String artistId,
        @RequestParam(value = "chart_group", required = false) VideoChartGroup chartGroup,
        @RequestParam(value = "chart_id", required = false) String chartId,
        @RequestParam(value = "content_type", required = false) Set<YoutubeContentType> contentTypes,
        @RequestParam(value = "country_code", required = false) String countryCode,
        @RequestParam(value = "dsp", required = false) VideoDspSlug dsp,
        @RequestParam(value = "expand_to", required = false) Set<ExpandTo> expandTo,
        @RequestParam(value = "include", required = false) Set<IncludeVideoChart> include,
        @RequestParam(value = "is_sony", required = false) Boolean isSony,
        @RequestParam(value = "isrc", required = false)
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @RequestParam(value = "video_id", required = false)
            Set<@Pattern(regexp = ApplicationConstants.VIDEO_ID_PATTERN) String> videoId,
        @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,
        @Min(0) @Max(ApplicationConstants.MAX_PAGE_SIZE) @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset,
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder) {
        Params params = buildParams(startDate, endDate, artistId, chartGroup, chartId, contentTypes, countryCode, dsp,
            expandTo,
            include, isSony, isrc, videoId, limitRange, cursor, limit, offset, sortBy, sortOrder);
        return getVideoChartPositions(params);
    }

    ResponseEntity<VideoChartPositions> getVideoChartPositions(Params params);


    /**
     * GET /video-positions/charts/summary : Get Video Chart position aggregated summaries Resource route for getting a
     * list of &#x60;VideoChartPositionSummary&#x60;. At least one of the identifying keys is required in the request to
     * filter data: - &#x60;artist_id&#x60; - &#x60;isrc&#x60; - &#x60;video_id&#x60;
     *
     * @param artistId    An optional &#x60;artist_id&#x60; query parameter. (optional)
     * @param chartGroup  Identifier for a video chart group (category). (optional)
     * @param chartId     (optional)
     * @param countryCode Typically a lower-case two letter code for the country (optional)
     * @param dsp         The slug name of a video DSP. This is a lowercase value that is the same as the
     *                    &#x60;dsp_id&#x60;. (optional)
     * @param expandTo    Providing &#x60;expand_to&#x3D;related_isrcs&#x60; will expand the query to include data from
     *                    related ISRCs by using the parent Product Family. If this is provided, an &#x60;isrc&#x60; is
     *                    required to be sent with the request.  (optional, default to new ArrayList&lt;&gt;())
     * @param include     Include additional data like the &#x60;Video&#x60; object in each response item.  (optional,
     *                    default to new ArrayList&lt;&gt;())
     * @param isrc        Array of International Standard Recording Code (ISRC) numbers (optional, default to new
     *                    ArrayList&lt;&gt;())
     * @param videoId     Array of Primary key identifiers for a Video (optional)
     * @param limit       The maximum number of &#x60;items&#x60; to return in a single request (i.e.: a single page).
     *                    (optional)
     * @param offset      The number of &#x60;items&#x60; to offset (aka skip) for pagination.  (optional, default to
     *                    0)
     * @param sortBy      Field name existing within objects in &#x60;items&#x60; by which to sort the results (dot
     *                    notation).  (optional)
     * @param sortOrder   Direction to sort the data. Default: &#x60;desc&#x60; if &#x60;sort_by&#x60; provided.
     *                    (optional, default to desc)
     * @return &#x60;VideoChartPositionSummaries&#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
    @ParameterScriptAssert.List({
        @ParameterScriptAssert(lang = "groovy",
            message = "Missing at least one identifying parameter from set: [artist_id, chart_id, isrc, video_id]",
            script = "artistId || chartId || isrc || videoId"),
        @ParameterScriptAssert(lang = "groovy",
            message = "ISRC is required if expand_to=related_isrc provided",
            script = "!expandTo || expandTo && isrc")
    })
    @RequestMapping(value = "/video-positions/charts/summary",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<VideoChartPositionSummaries> videoPositionsChartsSummarySearch(
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "artist_id", required = false) String artistId,
        @RequestParam(value = "chart_group", required = false) VideoChartGroup chartGroup,
        @RequestParam(value = "chart_id", required = false) String chartId,
        @RequestParam(value = "content_type", required = false) Set<YoutubeContentType> contentTypes,
        @RequestParam(value = "country_code", required = false) String countryCode,
        @RequestParam(value = "dsp", required = false) VideoDspSlug dsp,
        @RequestParam(value = "expand_to", required = false) Set<ExpandTo> expandTo,
        @RequestParam(value = "include", required = false) Set<IncludeVideoChart> include,
        @RequestParam(value = "is_sony", required = false) Boolean isSony,
        @RequestParam(value = "isrc", required = false)
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @RequestParam(value = "video_id", required = false)
            Set<@Pattern(regexp = ApplicationConstants.VIDEO_ID_PATTERN) String> videoId,
        @Min(0) @Max(ApplicationConstants.MAX_PAGE_SIZE) @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset,
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder) {
        Params params = buildParams(null, null, artistId, chartGroup, chartId, contentTypes, countryCode, dsp,
            expandTo, include, isSony, isrc, videoId, null, null, limit, offset, sortBy, sortOrder);
        return getVideoPositionsChartsSummarySearch(params);
    }

    private Params buildParams(LocalDate startDate, LocalDate endDate, String artistId, VideoChartGroup chartGroup,
        String chartId, Set<YoutubeContentType> contentTypes, String countryCode, VideoDspSlug dsp,
        Set<ExpandTo> expandTo, Set<IncludeVideoChart> include, Boolean isSony, List<String> isrc, Set<String> videoIds,
        String limitRange, String cursor, Integer limit, Integer offset, String sortBy, SortOrder sortOrder) {
        return Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .artistId(artistId)
            .videoChartGroup(chartGroup)
            .chartId(CollectionUtils.isNotEmpty(chartId) ? Collections.singletonList(chartId) : null)
            .youTubeContentTypes(ApiUtil.getFilteredContentTypes(contentTypes))
            .minPremiumUgcViews(ApplicationConstants.MIN_PREMIUM_UGC_VIEWS)
            .countryCode(CollectionUtils.isEmpty(countryCode) ? null : Sets.newHashSet(countryCode))
            .videoDsp(dsp)
            .expandTo(expandTo)
            .includeVideChart(include)
            .isSonyVideo(isSony)
            .isrc(isrc)
            .videoIds(videoIds)
            .limitRange(limitRange)
            .cursor(cursor)
            .limit(limit)
            .offset(offset)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
    }

    ResponseEntity<VideoChartPositionSummaries> getVideoPositionsChartsSummarySearch(Params params);

}
