using System;
using System.Collections.Generic;
using System.IO.Abstractions;
using System.Linq;

namespace Quadarax.Foundation.Core.Value
{
    public abstract class Validator
    {

        public IList<ValidationResult> Results { get; private set; }
        public bool IsSuccess => GetIsSuccess();

        protected IFileSystem FileSystem { get; private set; }

        protected Validator() : this(new FileSystem())
        {
        }
        protected Validator(IFileSystem fileSystemAbstraction)
        {
            FileSystem = fileSystemAbstraction ?? throw new ArgumentNullException(nameof(fileSystemAbstraction));
            Results = new List<ValidationResult>();
        }

        public void Validate(object validatingObject, ValidatorContext context)
        {
            Results.Clear();
            OnValidate(validatingObject, context);
        }

        protected abstract void OnValidate(object validatingObject, ValidatorContext context);

        public void Reset()
        {
            Results.Clear();
        }

        public AggregateException ToAggregateException(bool includeWarning = false, bool inclueInfo = false)
        {
            return ToAggregateException(Results, includeWarning, inclueInfo);
        }

        public static AggregateException ToAggregateException(IEnumerable<ValidationResult> results, bool includeWarning = false, bool inclueInfo = false)
        {
            var errors = new List<Exception>();
            foreach (var result in results)
            {
                if (result.Severity == ValidationResult.ValidationSeverityEnum.Error ||
                    result.Severity == ValidationResult.ValidationSeverityEnum.Critical ||
                    (includeWarning && result.Severity == ValidationResult.ValidationSeverityEnum.Warning) ||
                    (inclueInfo && result.Severity == ValidationResult.ValidationSeverityEnum.Info))
                {
                    errors.Add(new Exception(result.Description));
                }
            }
            if (errors.Count>0)
                return new AggregateException(errors);
            return null;
        }

        protected void AddResults(IEnumerable<ValidationResult> resultsToAdd)
        {
            ((List<ValidationResult>)Results).AddRange(resultsToAdd);
        }

        protected bool CheckIfEmpty(string name,string value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            if (!string.IsNullOrEmpty(value)) 
                return false;

            if (string.IsNullOrEmpty(description))
                description = $"Property named '{name}' must be filled.";
            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfNull(string name,object value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            if (value!=null) 
                return false;

            if (string.IsNullOrEmpty(description))
                description = $"Property named '{name}' must be filled.";
            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfCollectionEmpty(string name,IEnumerable<object> value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            if (value!=null && value.Any())  
                return false;

            if (string.IsNullOrEmpty(description))
                description = $"Collection named '{name}' must have at least one element.";
            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfDirectoryNotExists(string name,string value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            
            if (string.IsNullOrEmpty(description))
                description = $"Directory '{value}' in property '{name}' doesn't exists or not accessible.";

            try
            {
                if (FileSystem.Directory.Exists(value))
                    return false;
            }
            catch
            {
                // ignored
            }

            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfNotNumeric(string name,string value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            
            if (string.IsNullOrEmpty(description))
                description = $"Property '{name}' must have numeric value.";

            if (decimal.TryParse(value, out decimal outval))
                return false;

            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfNotGreater(string name,string value, decimal compareValue, bool equalAllowed, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            
            if (string.IsNullOrEmpty(description))
                description = $"Property '{name}' with value '{value}' must be greater than '{compareValue}'.";

            
            if (decimal.TryParse(value, out decimal outval))
            {
                var result = false;
                if (equalAllowed)
                    result = (outval > compareValue);
                else
                    result = (outval >= compareValue);
                if(result)
                    return false;
            }

            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfNotGreater(string name, decimal value, decimal compareValue, bool equalAllowed,
            ValidationResult.ValidationSeverityEnum severity, string description = "")
        {

            if (string.IsNullOrEmpty(description))
                description = $"Property '{name}' with value '{value}' must be greater than '{compareValue}'.";

            var result = false;
            if (equalAllowed)
                result = (value > compareValue);
            else
                result = (value >= compareValue);
            if (result)
                return false;


            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected bool CheckIfFileNotExists(string name,string value, ValidationResult.ValidationSeverityEnum severity, string description = "")
        {
            
            if (string.IsNullOrEmpty(description))
                description = $"File '{value}' in property '{name}' doesn't exists or not accessible.";

            try
            {
                if (FileSystem.File.Exists(value))
                    return false;
            }
            catch
            {
                // ignored
            }

            Results.Add(new ValidationResult(name, severity, description));
            return true;
        }

        protected virtual bool GetIsSuccess()
        {
            return !Results.Any(x =>
                /*x.Severity == ValidationResult.ValidationSeverityEnum.Warning ||*/
                x.Severity == ValidationResult.ValidationSeverityEnum.Error ||
                x.Severity == ValidationResult.ValidationSeverityEnum.Critical);
        }

    }
}
