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

import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

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.tiktok.TikTokChartType;
import io.delphiplatform.api.v3.model.tiktok.TikTokStateMetric;
import io.delphiplatform.api.v3.model.tiktok.TikTokTopAnalyticMetric;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopChartTrackStates;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopCharts;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopTracksAnalytics;
import io.delphiplatform.api.v3.model.tiktok.top.sound.TikTokTopIsrcSoundsItems;
import io.delphiplatform.api.v3.model.tiktok.top.video.TikTokTopIsrcVideoTopBy;
import io.delphiplatform.api.v3.model.tiktok.top.video.TikTokTopIsrcVideos;
import io.delphiplatform.api.v3.model.video.ExpandTo;
import io.delphiplatform.api.v3.validation.ExactMatchesForIntCollection;
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 TikTokTopApi {

    @OnlyDeclaredParams
    @GetMapping(value = "/tiktok/top/tracks-charts", produces = {MediaType.APPLICATION_JSON_VALUE})
    default ResponseEntity<TikTokTopCharts> tiktokTracksCharts(
        @RequestParam(value = "country_code", required = false) Set<String> countryCodes,
        @ExactMatchesForIntCollection(anyOf = {1, 7}, message = "Value does not match to the expected set : [1,7]")
        @RequestParam(value = "period_days", required = false) Set<Integer> periods,
        @RequestParam(value = "chart_type", required = false) Set<TikTokChartType> chartTypes) {
        return getTikTokTracksCharts(Params.builder()
            .countryCode(countryCodes)
            .tikTokChartTypes(chartTypes)
            .periodDays(periods)
            .build());
    }

    ResponseEntity<TikTokTopCharts> getTikTokTracksCharts(Params build);

    @OnlyDeclaredParams
    @ParameterScriptAssert(lang = "groovy",
        message = ApplicationConstants.ERROR_MESSAGE_MIN_MAX_POSITIONS,
        script = ApplicationConstants.SCRIPT_CHECK_POSITIONS)
    @GetMapping(value = "/tiktok/top/tracks", produces = {MediaType.APPLICATION_JSON_VALUE})
    default ResponseEntity<TikTokTopChartTrackStates> tiktokTopTracks(
        @Pattern(regexp = ApplicationConstants.NO_COMMA_PATTERN,
            message = ApplicationConstants.ERROR_MESSAGE_ARRAYS_INSTEAD_OF_SINGLE_ELEMENT)
        @RequestParam(value = "top_chart_id") String chartId,
        @RequestParam(value = "date", required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate date,
        @RequestParam(value = "track_state_includes", required = false) Set<TikTokStateMetric> trackStateIncludes,
        @RequestParam(value = "min_position", required = false) @Min(0) Integer minPosition,
        @RequestParam(value = "max_position", required = false) @Min(0) Integer maxPosition,
        @RequestParam(value = "isrc", required = false) List<@Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String> isrc) {
        if (CollectionUtils.isEmpty(trackStateIncludes)) {
            trackStateIncludes = Arrays.stream(TikTokStateMetric.values()).collect(Collectors.toSet());
        }
        return getTikTokTopTracks(Params.builder()
            .singleChartId(chartId)
            .date(date)
            .isrc(isrc)
            .minPosition(minPosition)
            .maxPosition(maxPosition)
            .tikTokTrackStateIncludes(trackStateIncludes)
            .build());
    }

    ResponseEntity<TikTokTopChartTrackStates> getTikTokTopTracks(Params build);

    @OnlyDeclaredParams
    @ParameterScriptAssert(lang = "groovy",
        message = ApplicationConstants.ERROR_MESSAGE_START_END_DATES,
        script = ApplicationConstants.SCRIPT_CHECK_DATES)
    @GetMapping(value = "/tiktok/top/tracks/analytics", produces = {
        MediaType.APPLICATION_JSON_VALUE})
    default ResponseEntity<TikTokTopTracksAnalytics> tiktokTopTracksAnalytics(
        @Pattern(regexp = ApplicationConstants.NO_COMMA_PATTERN,
            message = ApplicationConstants.ERROR_MESSAGE_ARRAYS_INSTEAD_OF_SINGLE_ELEMENT)
        @RequestParam(value = "top_chart_id") String chartId,
        @RequestParam(value = "isrc") @Pattern(regexp = ApplicationConstants.ISRC_PATTERN) String isrc,
        @RequestParam(value = "start_date") @DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date") @DateTimeFormat(iso = ISO.DATE) LocalDate endDate,
        @RequestParam(value = "metrics", required = false) Set<TikTokTopAnalyticMetric> metrics) {
        return getTikTokTopTracksAnalytics(Params.builder()
            .singleChartId(chartId)
            .isrc(Collections.singletonList(isrc))
            .startDate(startDate)
            .endDate(endDate)
            .tikTokTopAnalyticMetrics(metrics)
            .build());
    }

    ResponseEntity<TikTokTopTracksAnalytics> getTikTokTopTracksAnalytics(Params build);

    @OnlyDeclaredParams
    @GetMapping(value = "/tiktok/isrc/top/videos", produces = {MediaType.APPLICATION_JSON_VALUE})
    default ResponseEntity<TikTokTopIsrcVideos> tiktokTopVideos(
        @Pattern(regexp = ApplicationConstants.ISRC_PATTERN) @RequestParam(value = "isrc") String isrc,
        @ExactMatchesForSingleValue(anyOf = "related_isrcs") @RequestParam(value = "expand_to", required = false) String expandTo,
        @ExactMatchesForSingleValue(anyOf = {"views", "views_growth_7_days", "views_growth_28_days"})
        @RequestParam(value = "top_by") String topBy,
        @Min(1) @Max(10) @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit) {
        return getTikTokTopVideos(
            Params.builder()
                .isrc(List.of(isrc))
                .expandTo(CollectionUtils.isEmpty(expandTo) ? null : Set.of(ExpandTo.fromValue(expandTo)))
                .tikTokTopIsrcVideoTopBy(CollectionUtils.isEmpty(topBy) ? null : TikTokTopIsrcVideoTopBy.fromValue(topBy))
                .limit(limit)
                .build());
    }

    ResponseEntity<TikTokTopIsrcVideos> getTikTokTopVideos(Params params);

    @OnlyDeclaredParams
    @GetMapping(value = "/tiktok/isrc/top/sounds", produces = {MediaType.APPLICATION_JSON_VALUE})
    default CompletableFuture<ResponseEntity<TikTokTopIsrcSoundsItems>> tiktokTopSounds(
        @Pattern(regexp = ApplicationConstants.ISRC_PATTERN) @RequestParam(value = "isrc") String isrc,
        @ExactMatchesForSingleValue(anyOf = "related_isrcs") @RequestParam(value = "expand_to", required = false) String expandTo,
        @RequestParam(value = "start_date")
        @DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date")
        @DateTimeFormat(iso = ISO.DATE) LocalDate endDate,
        @Min(1) @Max(ApplicationConstants.MAX_BIGTABLE_PAGE_SIZE)
        @RequestParam(value = "limit", required = false, defaultValue = ApplicationConstants.DEFAULT_RESPONSE_LIMIT_PARAM_VALUE) Integer limit) {
        return getTikTokTopIsrcSounds(Params.builder()
            .isrc(isrc == null ? Collections.emptyList() : List.of(isrc))
            .startDate(startDate)
            .endDate(endDate)
            .expandTo(CollectionUtils.isEmpty(expandTo) ? null : Set.of(ExpandTo.fromValue(expandTo)))
            .limit(limit)
            .build());
    }

    CompletableFuture<ResponseEntity<TikTokTopIsrcSoundsItems>> getTikTokTopIsrcSounds(Params params);

}
