using Microsoft.Extensions.Logging;
using SewingDressesContracts.BusinessLogicsContracts;
using SewingDressesContracts.BindingModels;
using SewingDressesContracts.SearchModels;
using SewingDressesContracts.ViewModels;
using SewingDressesDataModels.Enums;

namespace SewingDressesBusinessLogic.BusinessLogic
{
	public class WorkModeling : IWorkProcess
	{
		private readonly ILogger _logger;
		private readonly Random _rnd;
		private IOrderLogic _orderLogic;
		public WorkModeling(ILogger<WorkModeling> logger)
		{
			_logger = logger;
			_rnd = new Random(1000);
		}
		public void DoWork(IImplementLogic implementLogic, IOrderLogic orderLogic)
		{
			_orderLogic = orderLogic;
			var implementers = implementLogic.ReadList(null);
			if (implementers == null)
			{
				_logger.LogWarning("DoWork. Implementers is null");
				return;
			}
			var orders = _orderLogic.ReadList(new OrderSearchModel { Status = OrderStatus.Принят });
			if (orders == null || orders.Count == 0)
			{
				_logger.LogWarning("DoWork. Orders is null or empty");
				return;
			}
			_logger.LogDebug("DoWork for {Count} orders", orders.Count);
			foreach (var implementer in implementers) {
				Task.Run(() => WorkerWorkAsync(implementer, orders));
			}
		
		}
		private async Task WorkerWorkAsync(ImplementViewModel implement, List<OrderViewModel> orders)
		{
			if (_orderLogic == null || implement == null)
				return;
			await RunOrderInWork(implement);
			await Task.Run(() =>
			{
				foreach (var order in orders)
				{
					try
					{
						_logger.LogDebug("DoWork. Worker {Id} try get order {Order}", implement.Id, order.Id);
						_orderLogic.TakeOrderInWork(new OrderBindingModel
						{
							Id = order.Id,
							ImplementId = implement.Id
						});
						Thread.Sleep(implement.WorkExperience * _rnd.Next(100, 1000) * order.Count);
						_logger.LogDebug("DoWork. Worker {Id} finish order { Order}", implement.Id, order.Id);
						_orderLogic.FinishOrder(new OrderBindingModel
						{
							Id = order.Id,
							ImplementId = implement.Id
						});
					}
					catch (InvalidOperationException ex)
					{
						_logger.LogWarning(ex, "Error try get work");
					}
					catch (Exception ex)
					{
						_logger.LogError(ex, "Error while do work");
						throw;
					}
					Thread.Sleep(implement.Qualification * _rnd.Next(10, 100));
				}
			});
		}

		private async Task RunOrderInWork(ImplementViewModel implement)
		{
			if (_orderLogic == null || implement == null)
				return;
			try
			{
				var runOrder = await Task.Run(() => _orderLogic.ReadElement(new OrderSearchModel
				{
					ImplementId = implement.Id,
					Status = OrderStatus.Выполняется
				}));
				if (runOrder == null)
					return;
				_logger.LogDebug("DoWork. Worker {Id} back to order {Order}", implement.Id, runOrder.Id);
				Thread.Sleep(implement.WorkExperience * _rnd.Next(100, 300) * runOrder.Count);
				_logger.LogDebug("DoWork. Worker {Id} finish order {Order}", implement.Id, runOrder.Id);
				_orderLogic.FinishOrder(new OrderBindingModel { Id = runOrder.Id });
				Thread.Sleep(implement.Qualification * _rnd.Next(10, 100));
			}
			catch (InvalidOperationException ex)
			{
				_logger.LogWarning("Error try get work");
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "Error while do work");
				throw;
			}
		}
	}
}
