using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BaseDomain.SimpleValidator
{
    public class ObjectValidatorBuilder<T>
    {
        /// <summary>
        /// model that is validated
        /// </summary>
        private T model;
        private bool isValid;

        public ObjectValidatorBuilder(T objectOrEntity)
        {
            model = objectOrEntity;
        }

        /// <summary>
        /// Check if model meetf all rules
        /// </summary>
        public ObjectValidatorBuilder<T> Build()
        {
            //select all ruiles without OnlyIf expression or with satisfied OnlyIf expression
            var invalidResults = _rules.Where(m => m.OnlyIf == null || (m.OnlyIf != null && m.OnlyIf(model)));

            //select all rules that arent satisfied
            invalidResults = invalidResults.Where(m => !m.ValidationExpression(model));

            //set error messages of unsatisfied rules
            ErrorMessages = invalidResults.Select(m => m.ErrorMessage).ToList();

            isValid =!invalidResults.Any();

            return this;
        }

        public bool IsValid()
        {
            return isValid;
        }

        public void ThrowErrorMessages()
        {
            if (!isValid) throw new ArgumentException(this.ErrorMessagesAsString());            
        }

        public string ErrorMessagesAsString()
        {
            return String.Join("\n", this.ErrorMessages);
        }

        /// <summary>
        /// Get error messages for rules that are not satisfied
        /// </summary>
        public List<string> ErrorMessages { get; set; }

        /// <summary>
        /// List of rules that are specified for model
        /// </summary>
        private readonly List<ValidationRule<T>> _rules = new List<ValidationRule<T>>();

        public ObjectValidatorBuilder<T> AddRule(Func<T, bool> validationExpression, Func<T, bool> onlyIf, String errorMessage)
        {
            _rules.Add(new ValidationRule<T>()
            {
                ValidationExpression = validationExpression,
                OnlyIf = onlyIf,
                ErrorMessage = errorMessage
            });

            return this;
        }

        public ObjectValidatorBuilder<T> AddRule(Func<T, bool> validationExpression, String errorMessage)
        {
            AddRule(validationExpression, null, errorMessage);
            return this;
        }

        public ObjectValidatorBuilder<T> AddRule(AValueObject valueObject)
        {

            AddRule((valueObject) => (valueObject as AValueObject).GetValidationExpression(), null, AValueObject.ErrorMessage);
            return this;
        }

        public ObjectValidatorBuilder<T> AddRule(ValidationRule<T> rule)
        {
            AddRule(rule);
            return this;
        }

        public void ClearRules()
        {
            _rules.Clear();
        }
    }
}
