using Quadarax.Foundation.Core.Data;
using Quadarax.Foundation.Core.Logging;
using Quadarax.Foundation.Core.Value.Extensions;

namespace Quadarax.Foundation.Core.Business.Processor.Task.ExecuteProvider
{
    public abstract class TaskExecuteProvider : ITaskExecuteProvider
    {
        #region *** Properties ***
        protected ILog Log => _log;
        #endregion

        #region *** Private fields ***
        private ILog _log;
        #endregion

        #region *** Constructors ***
        protected TaskExecuteProvider()
        {
            _log = new ConsoleLogger().GetLogger(GetType());
        }
        #endregion

        public ITaskExecuteResult Execute(ITaskIdentifier taskIdentifier, ITypedArgument[] arguments)
        {
            Log.Log(LogSeverityEnum.Debug, $"[{taskIdentifier}] Start executing task '{GetType().Name}' ...");
            var start = DateTime.Now;
            var duration = TimeSpan.Zero;
            var result = new TaskExecuteResult(TimeSpan.Zero);
            try
            {
                OnValidateArguments(arguments);
                OnExecute(taskIdentifier, result);
            }
            catch (Exception e)
            {
                Log.Log(LogSeverityEnum.Error, $"[{taskIdentifier}] Error executing task '{GetType().Name}' : {e.Message}", e);
                result.AddError(new Error(e));
                result.SetSuccess(false);
            }
            finally
            {
                duration = DateTime.Now.Subtract(start);
                Log.Log(LogSeverityEnum.Debug,
                    $"[{taskIdentifier}] Finished executing task @{DateTime.Now.Subtract(start).ToReadableString()}");
            }
            result.ExecutionTime = duration;
            return result;
        }

        protected abstract void OnExecute(ITaskIdentifier taskIdentifier, ITaskExecuteResult result);
        protected abstract void OnValidateArguments(ITypedArgument[] arguments);

        protected TValue? GetArgument<TValue>(ITypedArgument[] arguments, string argumentName)
        {
            if (arguments == null || arguments.Length == 0) throw new ArgumentNullException(nameof(arguments));

            var result = arguments.FirstOrDefault(x => string.Equals(x.Name, argumentName, StringComparison.InvariantCultureIgnoreCase));
            if (result == null) throw new ArgumentException($"Argument '{argumentName}' not found!");

            return result.GetTypedValue<TValue>();
        }

        #region *** Nested classes ***
        private class TaskExecuteResult : Result, ITaskExecuteResult
        {
            #region *** Properties ***
            public TimeSpan ExecutionTime { get; set; }
            #endregion

            #region *** Constructors ***
            public TaskExecuteResult()
            {

            }
            public TaskExecuteResult(TimeSpan executionTime)
            {
                ExecutionTime = executionTime;
            }
            #endregion
        }
        #endregion
    }
}
