using System.Linq;
using Gebhardt.Shared;
using Gebhardt.StoreWare.Wcs.Common;
using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.Interfaces;
using Gebhardt.StoreWare.Wcs.Common.DbAccess;
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Factories;
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model;
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums;
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries;
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces;
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces;
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models;

namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation
{
	public class DepartureNotificationHandler : IHandleRecord<IDepartureNotification>
	{
		private readonly IDestinationService _destinationService;
		private readonly ITransportOrderService _transportOrderService;
		private readonly IWcsDbContextFactory _wcsDbContextFactory;

		public DepartureNotificationHandler(IDestinationService destinationService, ITransportOrderService transportOrderService, IWcsDbContextFactory wcsDbContextFactory)
		{
			_destinationService = destinationService;
			_transportOrderService = transportOrderService;
			_wcsDbContextFactory = wcsDbContextFactory;
		}

		public bool Handle(IDepartureNotification message)
		{
			using IWcsDbContext db = _wcsDbContextFactory.GetDbContext();

			// If we don't have the LE, we need to depart. Otherwise we can't get rid of it.
			if (db.Le.ByLeNo(message.LeNo) == null || message.LeNo.StartsWith("B") || message.StorageArea.ToLower() == "dummy")
			{
				ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position);
				//For EtraBoxes we can Finish the OrdersHost here As there is no feedback anymore for this boxes:
				if (message.LeNo.StartsWith("B") && message.Position.Contains("PTL"))
				{
					//Finish all TransportOrders from this PTL Place as the place is empty now.
					var etraBoxOrder = db.OrdersHost.ByStatus(TransportOrderStatus.Transmitted).Where(o => o.Source == message.Position);
					if (etraBoxOrder.Any())
					{
						foreach (var oh in etraBoxOrder)
						{
							oh.Finish();
							if (oh.LeNo == message.LeNo)
							{
								Log.Write(LogLevel.Info, $"EtraBox {oh.LeNo} is pushed off, finish orders host {oh.Id}.");
							}
							else
							{
								oh.Info = $"CleanedUp by Departure for {message.LeNo}";
								Log.Write(LogLevel.Info, $"EtraBox {oh.LeNo} orders host {oh.Id} is finished because EtraBox {message.LeNo} was pushed off from the same PTL place.");
							}
						}
					}
				}
				db.SaveChanges();
				return true;
			}
			var openOH = db.OrdersHost.OpenByLeNo(message.LeNo).ToList();
			if (!openOH.Any())
			{
				//Receive and Replenishment places don't need to create a WCS Transport.
				if(message.Position.StartsWith(Constants.MfcAllDestinationsOldSystem.IPT01.Substring(0,3)) || message.Position.StartsWith(Constants.MfcAllDestinations.RCV01.Substring(0, 3)) || message.Position== Constants.MfcAllDestinationsOldSystem.TOPUP)
				{
					//Ignore and go on
					return true;
				}
					OrdersHost ordersHost = OrdersHostFactory.GetInstance()
						.InitialForLe(message.LeNo, TransportOrderType.Transport, message.Position, _destinationService.GetDefaultStorage(message.Position));
				db.Add(ordersHost);

				// Always depart a box on the error stations to prevent blocks
				IDestinationProperties destination = _destinationService.Get(message.Position);
				if (destination != null && destination.IsNio)
				{
					ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position);
					ordersHost.Le.SetStatus(LeStatus.OnConveyor);
				}

				Log.Write(LogLevel.Info, $"Added OrdersHost for {message.LeNo} from {message.Position} to {ordersHost.Destination}.");
			}
			else
			{
				// Is there an order to this place InProgress or InDestinationZone? => Start again by setting this order to pending
				var currentOrderToPlace = db.OrdersHost
						.ByLeNo(message.LeNo)
						.ByDestination(message.Position)
						.ByStatus(TransportOrderStatus.InProgress, TransportOrderStatus.InDestinationZone)
						.FirstOrDefault();
				if (currentOrderToPlace != null)
				{
					if (currentOrderToPlace.Status == TransportOrderStatus.InDestinationZone)
					{
						IDestinationProperties destination = _destinationService.Get(currentOrderToPlace.Destination);
						if (currentOrderToPlace.HostDestination != null)
						{
							currentOrderToPlace.Destination = destination.ConnectedSequencer;
						}
						ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position);
						currentOrderToPlace.UpdateResources(TransportOrderStatus.InProgress, null);
						currentOrderToPlace.ForceSetStatusInProgress();
						currentOrderToPlace.Le.SetStatus(LeStatus.OnConveyor);
						db.SaveChanges();
						Log.Write(LogLevel.Info, $"OrdersHost for {message.LeNo} to {message.Position} started again by DepartureNotification.");
					}
				}
				else
				{
					//Todo: Wouldn't it be better to do this in StartInitialOrders?
					/*var otherPicOrder = db.OrdersHost.ByLeNo(message.LeNo)
						.ByStatus(TransportOrderStatus.Initial)
						.ApplyWmsOrdering()
						.FirstOrDefault();
					if (otherPicOrder != null)
					{
						_transportOrderService.StartNextTransport(otherPicOrder.Id);
					}*/
					//Set source to TOPUP in case Departure has been sent from TopUP or IPT stations, to be able to accept the box on scale
					if (message.Position == Constants.MfcAllDestinationsOldSystem.TOPUP || message.Position.StartsWith(Constants.MfcAllDestinationsOldSystem.IPT01.Substring(0, 3)))
					{
						var activeOH = openOH.Where(o=>o.Status == TransportOrderStatus.InProgress).FirstOrDefault();
						if (activeOH != null)
						{
							activeOH.Source = Constants.MfcAllDestinationsOldSystem.TOPUP;
						}
					}
					else
					{
						_destinationService.SetLeRequestDeparture(message.LeNo, message.Position);
						ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position);
						db.Le.FirstOrDefault(l => l.LeNo == message.LeNo)?.SetStatus(LeStatus.OnConveyor);
					}
					db.SaveChanges();
					return true;
				}
			}

			// update destination status!
			_destinationService.SetLeRequestDeparture(message.LeNo, message.Position);
			db.SaveChanges(true);
			return true;
		}
	}
}