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

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

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

import javax.validation.constraints.NotEmpty;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.constant.ApplicationConstants;
import io.delphiplatform.api.v3.model.datahealth.CoreDataHealthStatus;
import io.delphiplatform.api.v3.model.datahealth.DataCompletenessItems;
import io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdown;
import io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdownGroup;
import io.delphiplatform.api.v3.model.datahealth.DataHealthChartPositions;
import io.delphiplatform.api.v3.model.datahealth.DataHealthRangeReport;
import io.delphiplatform.api.v3.model.datahealth.DataHealthStatusReport;
import io.delphiplatform.api.v3.view.util.Params;
import io.delphiplatform.api.v3.view.interceptor.OnlyDeclaredParams;

@Validated
public interface DataHealthApi {

    /**
     * GET /status/data : Get details about the latest partner data updates Resource route for getting the status of
     * partner data updates by DSP.
     *
     * @return &#x60;DataCompleteness&#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 = "/data-health/completeness",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<DataCompletenessItems> completenessStatusGet() {
        return getCompletenessData();
    }

    ResponseEntity<DataCompletenessItems> getCompletenessData();

    /**
     * GET /data-health/chart-positions : Get overall data info by DSP &amp; Chart Group Resource route for getting
     * overall meta info for Chart Positions datasets. This endpoint exposes data points like the earliest and latest
     * available date overall for an entire DSP Chart Group.
     *
     * @param chartGroup Identifier for a chart group (category). (optional)
     * @param dsp        The slug name of a DSP. This is a lowercase value that is the same as the &#x60;dsp_id&#x60;.
     *                   (optional)
     * @return &#x60;DataHealthChartPositionsResponse&#x60; response object (status code 200)
     * <p>
     * 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)
     * <p>
     * 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)
     * <p>
     * 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)
     * <p>
     * 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
    @RequestMapping(
        value = "/data-health/chart-positions",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<DataHealthChartPositions> chartPositionsGet(
        @RequestParam(value = "chart_group", required = false) String chartGroup,
        @RequestParam(value = "dsp", required = false) String dsp) {
        Params params = Params.builder()
            .chartGroup(chartGroup)
            .dsp(dsp != null ? List.of(dsp) : Collections.emptyList())
            .build();
        return getChartPositionsData(params);
    }

    ResponseEntity<DataHealthChartPositions> getChartPositionsData(Params params);


    /**
     * GET /data-health/dsp/status/analytics Returns DSP data health status data for the specified date range and
     * breakdowns. Consolidates multiple data providers into uniform API. Provides a high-level overview over the daily
     * data processing statuses.
     *
     * @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 breakdownGroups List of the breakdown groups to include in the response. By default only dsp_segment
     *                        breakdown group is included. (optional, default to new ArrayList&lt;&gt;())
     * @param breakdowns      List of the breakdowns to include in the response. By default only days breakdown is
     *                        included. (optional, default to new ArrayList&lt;&gt;())
     * @param dsp             Optionally include one or more DSP slug names. Omitting this field entirely will result in
     *                        all available DSPs being queried. The results from each DSP will be combined and
     *                        aggregated. To query for data by individual DSPs only, use separate requests providing one
     *                        DSP per request. (optional, default to new ArrayList&lt;&gt;())
     * @return DataHealthStatus 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)
     */
    @RequestMapping(value = "/data-health/dsp/status/analytics",
        produces = {"application/json"},
        method = RequestMethod.GET)
    @ParameterScriptAssert(lang = "groovy",
        message = ApplicationConstants.ERROR_MESSAGE_START_END_DATES,
        script = ApplicationConstants.SCRIPT_CHECK_DATES)
    @OnlyDeclaredParams
    default ResponseEntity<DataHealthRangeReport> dataHealthStatusAnalyticsGet(
        @RequestParam(value = "start_date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
        @RequestParam(value = "end_date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
        @RequestParam(value = "breakdown_group", required = false, defaultValue = "dsp_segment")
            Set<DataHealthBreakdownGroup> breakdownGroups,
        @RequestParam(value = "breakdown", required = false, defaultValue = "days") Set<DataHealthBreakdown> breakdowns,
        @RequestParam(value = "dsp", required = false) Set<String> dsp) {
        return getDataHealthStatusAnalytics(Params.builder()
            .startDate(startDate)
            .endDate(endDate)
            .dataHealthBreakdownGroups(breakdownGroups)
            .dataHealthBreakdowns(breakdowns)
            .dsp(CollectionUtils.isEmpty(dsp) ? Collections.emptyList() : new ArrayList<>(dsp))
            .build());
    }

    ResponseEntity<DataHealthRangeReport> getDataHealthStatusAnalytics(Params params);


    /**
     * GET /data-health/dsp/status Returns DSP data health status data for the dsp lifetime.
     *
     * @param dsp    Optionally include one or more DSP slug names. Omitting this field entirely will result in all
     *               available DSPs being queried. The results from each DSP will be combined and aggregated. To query
     *               for data by individual DSPs only, use separate requests providing one DSP per request. (optional,
     *               default to new ArrayList&lt;&gt;())
     * @param status Data health status describing the state of data. (optional)
     * @return DataHealthStatus 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
    @RequestMapping(value = "/data-health/dsp/status",
        produces = {"application/json"},
        method = RequestMethod.GET)
    default ResponseEntity<DataHealthStatusReport> dataHealthStatusGet(
        @RequestParam(value = "dsp", required = false) Set<String> dsp,
        @RequestParam(value = "status") @NotEmpty(message = ApplicationConstants.ERROR_MESSAGE_STATUS_IS_EMPTY)
            Set<CoreDataHealthStatus> status) {
        return getDataHealthStatus(Params.builder()
            .dsp(CollectionUtils.isEmpty(dsp) ? Collections.emptyList() : new ArrayList<>(dsp))
            .dataHealthStatus(status.stream().map(CoreDataHealthStatus::getStatus).collect(Collectors.toSet()))
            .build());
    }

    ResponseEntity<DataHealthStatusReport> getDataHealthStatus(Params params);

}
