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.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.time.LocalDate;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.util.HttpUtils;
import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.model.IncludeChart;
import io.delphiplatform.api.v3.model.SortOrder;
import io.delphiplatform.api.v3.model.amazon.AmazonChartPositions;
import io.delphiplatform.api.v3.model.amazon.AmazonChartTrackSummaries;
import io.delphiplatform.api.v3.model.amazon.AmazonCharts;
import io.delphiplatform.api.v3.model.datahealth.AmazonChartsDataHealthStatus;
import io.delphiplatform.api.v3.model.datahealth.AmazonChartsDataHealthStatusReport;
import io.delphiplatform.api.v3.model.datahealth.AmazonDataHealthStatusSummary;
import io.delphiplatform.api.v3.validation.ExactMatchesForSingleValue;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;
import io.delphiplatform.api.v3.view.util.Params;

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

@Validated
public interface AmazonChartsApi {

    @OnlyDeclaredParams
    @GetMapping(value = "/amazon/charts")
    default ResponseEntity<AmazonCharts> getAmazonCharts(
        @RequestParam(value = "chart_id", required = false) String chartId,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes,
        @RequestParam(value = "genre_name", required = false) Set<String> genreNamesUnused, //preserved for params validation
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "chart_id", "total_positions", "country_code", "genre"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder,
        @Min(0)
        @Max(ApplicationConstants.MAX_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false) Integer offset,
        HttpServletRequest request
    ) {
        List<String> chartIds = chartId == null ? null : Collections.singletonList(chartId);
        Set<String> genreNamesParsed =
            HttpUtils.parseRawStringListQueryParamCommaSafe(request.getQueryString(), "genre_name");

        Params params = Params.builder()
            .chartId(chartIds)
            .genreName(genreNamesParsed.isEmpty() ? null : genreNamesParsed)
            .countryCode(countryCodes)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .limit(limit)
            .offset(offset)
            .build();
        return getAmazonCharts(params);
    }

    ResponseEntity<AmazonCharts> getAmazonCharts(Params params);

    @OnlyDeclaredParams
    @ParameterScriptAssert.List({
        @ParameterScriptAssert(lang = "groovy",
            message = ApplicationConstants.ERROR_MESSAGE_START_END_DATES,
            script = ApplicationConstants.SCRIPT_CHECK_DATES
        ),
        @ParameterScriptAssert(lang = "groovy",
            message = ApplicationConstants.ERROR_MESSAGE_MIN_MAX_POSITIONS,
            script = ApplicationConstants.SCRIPT_CHECK_POSITIONS
        )
    })
    @GetMapping(value = "/amazon/charts/track-positions")
    default ResponseEntity<AmazonChartPositions> getAmazonChartTrackPositions(
        @RequestParam(value = "start_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date")
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
        @RequestParam(value = "chart_id", required = false) String chartId,
        @RequestParam(value = "isrc", required = false) List<@Pattern(regexp = ISRC_PATTERN) String> isrc,
        @RequestParam(value = "track_id", required = false) List<String> trackId,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes,
        @RequestParam(value = "genre_name", required = false) Set<String> genreNamesUnused, //preserved for params validation
        @RequestParam(value = "include", required = false) Set<IncludeChart> include,
        @RequestParam(value = "min_position", required = false) @Min(0) Integer minPosition,
        @RequestParam(value = "max_position", required = false) @Min(0) Integer maxPosition,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "isrc", "chart.chart_id", "chart.total_positions", "chart.country_code", "chart.genre",
            "track.amazon_track_id", "track.isrc", "track.name", "track.release_date", "position.current", "position.date",
            "position.date_gap", "position.is_entry", "position.is_exit", "position.is_reentry", "position.previous_date",
            "position.previous_position", "position.trend", "position.total_days"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder,
        @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 = "is_exit", required = false) Boolean isExit,
        @RequestParam(value = "is_sony", required = false) Boolean isSony,
        @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,
        HttpServletRequest request
    ) {
        Set<String> genreNamesParsed =
            HttpUtils.parseRawStringListQueryParamCommaSafe(request.getQueryString(), "genre_name");

        Params params = Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .chartId(chartId != null ? Collections.singletonList(chartId) : null)
            .isrc(isrc)
            .trackId(trackId)
            .countryCode(countryCodes)
            .minPosition(minPosition)
            .maxPosition(maxPosition)
            .limit(limit)
            .isSony(isSony)
            .isExit(isExit)
            .offset(offset)
            .limitRange(limitRange)
            .cursor(cursor)
            .includeChart(include)
            .genreName(genreNamesParsed.isEmpty() ? null : genreNamesParsed)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        validateParams(params);
        return getAmazonChartsTrackPositions(params);
    }

    ResponseEntity<AmazonChartPositions> getAmazonChartsTrackPositions(Params params);

    @OnlyDeclaredParams
    @GetMapping(value = "/amazon/charts/track-positions/summary")
    default ResponseEntity<AmazonChartTrackSummaries> getAmazonChartTrackPositionsSummary(
        @RequestParam(value = "chart_id", required = false) String chartId,
        @RequestParam(value = "isrc", required = false) List<@Pattern(regexp = ISRC_PATTERN) String> isrc,
        @RequestParam(value = "track_id", required = false) List<String> trackId,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes,
        @RequestParam(value = "include", required = false) Set<IncludeChart> include,
        @RequestParam(value = "is_sony", required = false) Boolean isSony,
        @ExactMatchesForSingleValue(message = ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER, anyOf = {
            "isrc", "chart.chart_id", "chart.total_positions", "chart.country_code","chart.genre",
            "track.amazon_track_id", "track.isrc", "track.name", "track.release_date", "summary.earliest_position",
            "summary.earliest_position_date", "summary.latest_position", "summary.latest_position_date", "summary.max_position",
            "summary.max_position_date", "summary.min_position", "summary.min_position_date", "summary.total_days"
        })
        @RequestParam(value = "sort_by", required = false) String sortBy,
        @RequestParam(value = "sort_order", required = false, defaultValue = "desc") SortOrder sortOrder,
        @Min(0)
        @Max(ApplicationConstants.MAX_PAGE_SIZE)
        @RequestParam(value = "limit", required = false) Integer limit,
        @Min(0) @RequestParam(value = "offset", required = false) Integer offset
    ) {
        Params params = Params.builder()
            .chartId(chartId != null ? Collections.singletonList(chartId) : null)
            .isrc(isrc)
            .trackId(trackId)
            .countryCode(countryCodes)
            .limit(limit)
            .includeChart(include)
            .isSony(isSony)
            .offset(offset)
            .sortBy(sortBy)
            .sortOrder(sortOrder)
            .build();
        validateParams(params);
        return getAmazonChartsTrackPositionsSummary(params);
    }

    ResponseEntity<AmazonChartTrackSummaries> getAmazonChartsTrackPositionsSummary(Params params);

    @OnlyDeclaredParams
    @GetMapping(value = "/amazon/charts/data-health/status", produces = {"application/json"})
    default ResponseEntity<AmazonChartsDataHealthStatusReport<AmazonDataHealthStatusSummary>> getAmazonMusicChartsDataHealthStatus(
        @RequestParam(value = "status") @NotEmpty(message = ApplicationConstants.ERROR_MESSAGE_STATUS_IS_EMPTY)
            Set<AmazonChartsDataHealthStatus> status,
        @RequestParam(value = "country_code", required = false)
            Set<@Pattern(regexp = COUNTRY_CODE_PATTERN) String> countryCodes
    ) {
        Params params = Params.builder()
            .dataHealthStatus(
                status.stream().map(AmazonChartsDataHealthStatus::getStatus).collect(Collectors.toSet()))
            .countryCode(CollectionUtils.isEmpty(countryCodes) ? Collections.emptySet() : new HashSet<>(countryCodes))
            .build();
        return getAmazonMusicChartsDataHealthStatus(params);
    }

    ResponseEntity<AmazonChartsDataHealthStatusReport<AmazonDataHealthStatusSummary>> getAmazonMusicChartsDataHealthStatus(
        Params params);

    void validateParams(Params params);
}