package ru.yandex.direct.api.v5.entity.adextensions.validation;

import java.time.format.DateTimeFormatter;
import java.util.Set;

import com.yandex.direct.api.v5.adextensions.AdExtensionsSelectionCriteria;
import com.yandex.direct.api.v5.adextensions.GetRequest;
import com.yandex.direct.api.v5.adextensiontypes.AdExtensionTypeEnum;

import ru.yandex.direct.api.v5.common.validation.GetRequestGeneralValidator;
import ru.yandex.direct.api.v5.validation.DefectType;
import ru.yandex.direct.api.v5.validation.DefectTypes;
import ru.yandex.direct.api.v5.validation.constraints.Constraints;
import ru.yandex.direct.validation.builder.Constraint;
import ru.yandex.direct.validation.builder.ItemValidationBuilder;
import ru.yandex.direct.validation.builder.When;
import ru.yandex.direct.validation.result.ValidationResult;

import static java.util.Collections.singleton;
import static ru.yandex.direct.api.v5.validation.DefectTypeCreators.invalidRequestParamsIncorrectDate;
import static ru.yandex.direct.api.v5.validation.DefectTypeCreators.invalidRequestParamsInvalidDateFormat;
import static ru.yandex.direct.api.v5.validation.DefectTypeCreators.wrongSelectionCriteriaUnsupportedFieldValues;
import static ru.yandex.direct.api.v5.validation.constraints.Constraints.eachNotNull;
import static ru.yandex.direct.api.v5.validation.constraints.Constraints.maxListSize;
import static ru.yandex.direct.utils.FunctionalUtils.mapList;
import static ru.yandex.direct.validation.Predicates.validDateTime;

public class GetRequestValidator {
    static final int GET_IDS_LIMIT = 10000;

    static final String SELECTION_CRITERIA_FIELD = "SelectionCriteria";
    static final String IDS_FIELD = "Ids";
    static final String TYPES_FIELD = "Types";
    static final String MODIFIED_SINCE_FIELD = "ModifiedSince";

    private static final Set<AdExtensionTypeEnum> ALLOWED_TYPES = singleton(AdExtensionTypeEnum.CALLOUT);
    private static final String MODIFIED_SINCE_DATE_PATTERN = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$";

    public static ValidationResult<GetRequest, DefectType> validateRequest(GetRequest externalRequest) {
        ItemValidationBuilder<GetRequest, DefectType> vb = ItemValidationBuilder.of(externalRequest);
        vb.checkBy(GetRequestGeneralValidator::validateRequest);
        vb.item(externalRequest.getSelectionCriteria(), SELECTION_CRITERIA_FIELD)
                .checkBy(GetRequestValidator::validateSelectionCriteria, When.notNull());
        return vb.getResult();
    }

    private static ValidationResult<AdExtensionsSelectionCriteria, DefectType> validateSelectionCriteria(
            AdExtensionsSelectionCriteria selectionCriteria) {
        ItemValidationBuilder<AdExtensionsSelectionCriteria, DefectType> vb =
                ItemValidationBuilder.of(selectionCriteria);
        vb.item(selectionCriteria.getIds(), IDS_FIELD)
                .check(maxListSize(GET_IDS_LIMIT), DefectTypes.maxIdsInSelection())
                .check(eachNotNull());
        vb.list(selectionCriteria.getTypes(), TYPES_FIELD)
                .checkEach(
                        Constraints.inSet(ALLOWED_TYPES),
                        wrongSelectionCriteriaUnsupportedFieldValues(TYPES_FIELD, mapList(ALLOWED_TYPES, Enum::name)));
        vb.item(selectionCriteria.getModifiedSince(), MODIFIED_SINCE_FIELD)
                .check(Constraints.matchPattern(MODIFIED_SINCE_DATE_PATTERN),
                        invalidRequestParamsInvalidDateFormat(MODIFIED_SINCE_FIELD))
                .check(Constraint.fromPredicate(validDateTime(DateTimeFormatter.ISO_OFFSET_DATE_TIME),
                        invalidRequestParamsIncorrectDate(MODIFIED_SINCE_FIELD)), When.isValid());
        return vb.getResult();
    }
}
