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

import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

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.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.Video;
import io.delphiplatform.api.v3.model.video.VideoDspSlug;
import io.delphiplatform.api.v3.model.video.Videos;
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;

@Validated
public interface VideosApi {

    /**
     * GET /videos/{video_id} : Get one Video Resource route for getting a single &#x60;Video&#x60; object.
     *
     * @param videoId Primary key identifier for a Video (required)
     * @return &#x60;Video&#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;404 Not Found&#x60; response status is returned when a single item queried
     * by primary key identifier was not found in the database, or an endpoint path was provided that does not exist.
     * (status code 404) 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)
     */
    @RequestMapping(value = "/videos/{video_id}",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<Video> videosGet(
        @Pattern(regexp = ApplicationConstants.VIDEO_ID_PATTERN)
        @PathVariable("video_id") String videoId) {
        return findOne(videoId);
    }

    ResponseEntity<Video> findOne(String videoId);

    /**
     * GET /videos : Search for Videos Resource route for getting a list of &#x60;Video&#x60; objects with filtering
     * based on provided parameters.  At least one identifying parameter must be provided (e.g.: &#x60;isrc&#x60;, or
     * &#x60;track_id&#x60;, etc.)
     *
     * @param artistId  An optional &#x60;artist_id&#x60; query parameter. (optional)
     * @param channelId A video channel identifier. (optional)
     * @param isrc      Array of International Standard Recording Code (ISRC) numbers (optional, default to new
     *                  ArrayList&lt;&gt;())
     * @param trackId   (optional, default to new ArrayList&lt;&gt;())
     * @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 Product Family.  (optional, default to new ArrayList&lt;&gt;())
     * @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 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;Videos&#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, channel_id, isrc, track_id]",
            script = "artistId || channelId || isrc || trackId"),
        @ParameterScriptAssert(lang = "groovy",
            message = "ISRC is required if expand_to=related_isrc provided",
            script = "!expandTo || expandTo && isrc")
    })
    @RequestMapping(value = "/videos",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<Videos> videosSearch(
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "artist_id", required = false) String artistId,
        @RequestParam(value = "content_type", required = false) Set<YoutubeContentType> contentTypes,
        @Pattern(regexp = ApplicationConstants.VIDEO_ID_PATTERN)
        @RequestParam(value = "channel_id", required = false) String channelId,
        @RequestParam(value = "isrc", required = false)
            List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc,
        @RequestParam(value = "track_id", required = false)
            List<@Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN) String> trackId,
        @RequestParam(value = "dsp", required = false) VideoDspSlug dsp,
        @RequestParam(value = "expand_to", required = false) Set<ExpandTo> expandTo,
        @Min(0) @Max(ApplicationConstants.MAX_PAGE_SIZE) @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false) Integer offset,
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder) {
        // ToDo: Remove restriction in DAE-1481 and use contentTypes param
        contentTypes = ApiUtil.getFilteredContentTypes(contentTypes);
        return getSearchData(Params.builder()
            .artistId(artistId)
            .channelId(channelId)
            .isrc(isrc)
            .trackId(trackId)
            .youTubeContentTypes(contentTypes)
            .minPremiumUgcViews(ApplicationConstants.MIN_PREMIUM_UGC_VIEWS)
            .videoDsp(dsp)
            .expandTo(expandTo)
            .limit(limit)
            .offset(offset)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build());
    }

    ResponseEntity<Videos> getSearchData(Params params);


}
