package io.delphiplatform.api.v3.view;

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.PathVariable;
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.List;
import java.util.concurrent.CompletableFuture;

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

import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.model.ArtistFollowers;
import io.delphiplatform.api.v3.model.ArtistSimple;
import io.delphiplatform.api.v3.model.Artists;
import io.delphiplatform.api.v3.model.GroupByField;
import io.delphiplatform.api.v3.model.SortOrder;
import io.delphiplatform.api.v3.validation.ExactMatchesForSingleValue;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;
import io.delphiplatform.api.v3.view.util.Params;

@Validated
public interface ArtistsApi {

    /**
     * GET /artists/{artist_id} : Query for a single Artist Resource route for getting a single &#x60;Artist&#x60; with
     * filtering based on provided parameters
     *
     * @param artistId (required)
     * @return &#x60;Artist&#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)
     */
    @RequestMapping(value = "/artists/{artist_id}",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<ArtistSimple> artistsGet(
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @PathVariable("artist_id") String artistId) {
        return findOne(artistId);
    }

    ResponseEntity<ArtistSimple> findOne(String artistId);


    /**
     * GET /artists/{artist_id}/followers : Query for a single Artist with followers grouped by date Resource route for
     * getting &#x60;ArtistFollowers&#x60; for a single &#x60;Artist&#x60; with filtering based on provided parameters
     *
     * @param artistId   (required)
     * @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 groupBy    Specific time-based term to group the data by (optional)
     * @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 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;ArtistFollowers&#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 = "/artists/{artist_id}/followers",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default CompletableFuture<ResponseEntity<ArtistFollowers>> artistsGetFollowers(
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @PathVariable("artist_id") String artistId,
        @NotNull
        @RequestParam(value = "start_date")
        @DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
        @NotNull
        @RequestParam(value = "end_date")
        @DateTimeFormat(iso = ISO.DATE) LocalDate endDate,
        @RequestParam(value = "group_by", required = false) GroupByField groupBy,
        @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)
            .artistId(artistId)
            .groupByField(groupBy)
            .limit(limit)
            .offset(offset)
            .cursor(cursor)
            .limitRange(limitRange)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        return getArtistFollowers(params);
    }

    CompletableFuture<ResponseEntity<ArtistFollowers>> getArtistFollowers(Params params);


    /**
     * GET /artists : Search for Artists Resource route for getting a list of &#x60;Artists&#x60; with filtering based
     * on provided parameters
     *
     * @param productId (optional)
     * @param trackId   (optional)
     * @param isrc      International Standard Recording Code (ISRC) number (optional)
     * @param artistIds An optional &#x60;artist_id&#x60; query parameter. (optional)
     * @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 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;Artists&#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 = "Missing at least one identifying parameter from set: [product_id, track_id, isrc, artist_id]",
        script = "productId || trackId || isrc || artistIds")
    @RequestMapping(value = "/artists",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<Artists> artistsSearch(
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "product_id", required = false) String productId,
        @Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN)
        @RequestParam(value = "track_id", required = false) String trackId,
        @Pattern(regexp = ApplicationConstants.ISRC_PATTERN)
        @RequestParam(value = "isrc", required = false) String isrc,
        @RequestParam(value = "artist_id", required = false)
            List<@Pattern(regexp = ApplicationConstants.GRAS_ID_PATTERN) String> artistIds,
        @Min(0) @Max(ApplicationConstants.MAX_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0)
        @RequestParam(value = "offset", required = false) Integer offset,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "artist_id", "first_name", "last_name", "full_name", "spotify_artist_id", "apple_artist_id",
            "spotify_popularity"})
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder
    ) {
        return getSearchData(productId, trackId, isrc, artistIds, limit, offset, sortBy, sortOrder);
    }

    ResponseEntity<Artists> getSearchData(String productId, String trackId, String isrc, List<String> artistIds,
        Integer limit, Integer offset, String sortBy, SortOrder sortOrder);

}
