using System;
using System.Linq.Expressions;
using Hangfire;
using log4net;
using LeafWeb.Core.DAL;
using LeafWeb.Core.Entities;
using LeafWeb.Core.Remote;
using Polly;

namespace LeafWeb.WebCms.Services.PiscalQueue
{
	public abstract class PiscalQueueBase : IDisposable
	{
		protected readonly DataService DataService;
		protected readonly PiscalService PiscalService;
		protected readonly ILog Logger;
		private readonly Policy _retryPolicy;

		protected PiscalQueueBase(DataService dataService, PiscalService piscalService)
		{
			DataService = dataService;
			PiscalService = piscalService;
			Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

			_retryPolicy = 
				Policy
				.Handle<TimeoutException>()
				.Retry(3, 
					(exception, i) => Logger.Warn($"Retry {i} after exception: {exception.Message}"));
		}

		protected PiscalQueueBase() : this(new DataService(), new PiscalService()) { }

		protected string FormatException(Exception ex)
		{
			return
				(ex is PiscalClientException ? $"LeafInputId: {((PiscalClientException) ex).LeafInputId}{Environment.NewLine}" : "") +
				$"Class: {GetType().Name}{Environment.NewLine}" + 
				$"Exception message: {ex.Message}{Environment.NewLine}" +
				(ex.InnerException != null ? $"InnerException: {ex.InnerException}{Environment.NewLine}" : string.Empty)
				+ $"StackTrace: {ex.StackTrace}";
		}

		protected string FormatLeafInputInfo(LeafInput leafInput)
		{
			return
				$"User name: {leafInput.Name}{Environment.NewLine}" +
				$"User email: {leafInput.Email}{Environment.NewLine}" +
				$"Identifier: {leafInput.Identifier}{Environment.NewLine}" +
				$"Site Id: {leafInput.SiteId}{Environment.NewLine}" +
				$"Photosynthesis Type: {leafInput.PhotosynthesisType}{Environment.NewLine}";
		}

		protected void PiscalExceptionHandler(PiscalClientException ex, LeafInput leafInput)
		{
			var errorMessage = FormatException(ex);
			Logger.Error(errorMessage);

			// send admin an email
			BackgroundJobEnqueueRetry<EmailNotificationService>(
				email => email.SendAdministratorMessage(
					$"LeafWeb: PiscalQueue {GetType().Name} Exception",
					"Piscal exception reported. LeafInput info:" + Environment.NewLine
					+ FormatLeafInputInfo(leafInput)
					+ Environment.NewLine
					+ "ErrorMessage:" + Environment.NewLine
					+ errorMessage));

			// send user email too
			BackgroundJobEnqueueRetry<EmailNotificationService>(
				email => email.SendLeafWebSystemException(leafInput.Identifier, leafInput.Email));

			if (leafInput != null)
			{
				DataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Exception, "Error occurred processing LeafInput",
					ex.Message);
			}
		}

		protected void PiscalWarningHandler(string warningMessage, LeafInput leafInput)
		{
			Logger.Warn(warningMessage);

			// send admin an email
			BackgroundJobEnqueueRetry<EmailNotificationService>(
				email => email.SendAdministratorMessage(
					$"LeafWeb: PiscalQueue {GetType().Name} Warning", 
					"Piscal warning reported. LeafInput info:" + Environment.NewLine
					+ FormatLeafInputInfo(leafInput)
					+ Environment.NewLine
					+ "Warning message:" + Environment.NewLine
					+ warningMessage));
		}

		public void Dispose()
		{
			DataService.Dispose();
		}

		protected string BackgroundJobEnqueueRetry<T>(Expression<Action<T>> a)
		{
			return _retryPolicy.Execute(() => BackgroundJob.Enqueue(a));
		}
	}
}