package ru.yandex.qe.dispenser.ws.quota.request.workflow.service;

import java.util.Map;

import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.collect.Multimap;

import ru.yandex.qe.dispenser.domain.exception.SingleMessageException;
import ru.yandex.qe.dispenser.ws.quota.request.workflow.context.RequestContext;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;

@ParametersAreNonnullByDefault
public class ServicePropertyAllowedValuesValidator implements ServicePropertyValidator {
    private final Multimap<String, String> propertyToAllowedValues;
    private final SimpleServicePropertyValidator simpleDelegateValidator;

    public ServicePropertyAllowedValuesValidator(final Multimap<String, String> propertyToAllowedValues) {
        this.propertyToAllowedValues = propertyToAllowedValues;
        this.simpleDelegateValidator = new SimpleServicePropertyValidator(propertyToAllowedValues.keySet());
    }

    @Override
    public void validateAdditionalPropertiesOnCreate(final RequestContext context, final Map<String, String> additionalProperties) {
        simpleDelegateValidator.validateAdditionalPropertiesOnCreate(context, additionalProperties);
        doValidation(additionalProperties);
    }

    @Override
    public void validateAdditionalPropertiesOnUpdate(final RequestContext context, final Map<String, String> additionalProperties,
                                                     final Map<String, String> currentAdditionalProperties) {
        simpleDelegateValidator.validateAdditionalPropertiesOnUpdate(context, additionalProperties, currentAdditionalProperties);
        if (context.getCampaign() == null || !context.getCampaign().isAllowedModificationOnMissingAdditionalFields()) {
            // Base case - do usual validation
            doValidation(additionalProperties);
            return;
        }
        // Special flag is set
        propertyToAllowedValues.asMap().forEach((propertyKey, allowedValues) -> {
            final boolean hasCurrentValue = currentAdditionalProperties.containsKey(propertyKey)
                    && !trimToEmpty(currentAdditionalProperties.get(propertyKey)).isEmpty();
            final boolean hasValue = additionalProperties.containsKey(propertyKey)
                    && !trimToEmpty(additionalProperties.get(propertyKey)).isEmpty();
            if (!hasValue && !hasCurrentValue) {
                // SKip validation when both new and current values are empty or undefined
                return;
            }
            final String propertyValue = additionalProperties.get(propertyKey);
            if (additionalProperties.containsKey(propertyKey) && !allowedValues.contains(propertyValue)) {
                throw SingleMessageException.illegalArgument("property.value.is.invalid.with.allowed", propertyKey, propertyValue, allowedValues);
            }
        });
    }

    private void doValidation(final Map<String, String> additionalProperties) {
        propertyToAllowedValues.asMap().forEach((propertyKey, allowedValues) -> {
            final String propertyValue = additionalProperties.get(propertyKey);
            if (additionalProperties.containsKey(propertyKey) && !allowedValues.contains(propertyValue)) {
                throw SingleMessageException.illegalArgument("property.value.is.invalid.with.allowed", propertyKey, propertyValue, allowedValues);
            }
        });
    }

}
