package eu.eudat.models.validators.fluentvalidator;

import eu.eudat.models.validators.fluentvalidator.predicates.ComparisonOperator;
import eu.eudat.models.validators.fluentvalidator.predicates.ConditionalOperator;
import eu.eudat.models.validators.fluentvalidator.predicates.FieldSelector;
import eu.eudat.models.validators.fluentvalidator.rules.AbstractFluentValidatorRule;
import eu.eudat.models.validators.fluentvalidator.rules.CompareRule;
import eu.eudat.models.validators.fluentvalidator.rules.NotEmptyRule;

import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by ikalyvas on 8/31/2018.
 */
public class FluentValidatorBuilder<T> {
    private FieldSelector selector;
    private T item;
    private String name;
    private String message;
    private List<String> validationMessages = new LinkedList<>();
    private List<AbstractFluentValidatorRule> rules = new LinkedList<>();
    private ConditionalOperator<T> conditionalOperator;
    public FluentValidatorBuilder(FieldSelector selector) {
        this.selector = selector;
    }

    public FluentValidatorBuilder<T> notEmpty() {
        this.rules.add(new NotEmptyRule<>(this.selector));
        return this;
    }

    public FluentValidatorBuilder<T> ruleIf(ConditionalOperator<T> conditionalOperator) {
        this.conditionalOperator = conditionalOperator;
        return this;
    }

    public <R> FluentValidatorBuilder<T> compareAs(FieldSelector<T, R> comparisonSelector, ComparisonOperator<R, R> comparisonOperator) {
        this.rules.add(new CompareRule<>(this.selector, comparisonSelector, comparisonOperator));
        return this;
    }

    public FluentValidatorBuilder<T> withName(String name) {
        this.name = name;
        return this;
    }

    public FluentValidatorBuilder<T> withMessage(String message) {
        this.message = message;
        return this;
    }

    public List<FluentValidatorResult> validate(T item) {
        this.item = item;
        List<FluentValidatorResult> errors = new LinkedList<>();
        if(this.conditionalOperator != null && !this.conditionalOperator.apply(this.item)) return errors;
        this.rules.forEach(x -> errors.add(this.evaluateError(x)));
        return errors.stream().filter(x -> x != null).collect(Collectors.toList());
    }

    private FluentValidatorResult evaluateError(AbstractFluentValidatorRule<T> rule) {
        if (rule.assertValue(this.item)) {
            return new FluentValidatorResult(this.name, this.message);
        }
        return null;
    }
}
