using System;
using System.Linq;
using Gebhardt.Shared;
using Gebhardt.StoreWare.Wcs.Common;
using Gebhardt.StoreWare.Wcs.Common.DbAccess;
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Constants;
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;

namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation
{
	public class HuChangeHandler : IHandleRecord<IHuChange>
	{
		private readonly IWcsDbContextFactory _dbContextFactory;
		private readonly LeFactory _leFactory = LeFactory.GetInstance();
		public HuChangeHandler(IWcsDbContextFactory dbContextFactory)
		{
			_dbContextFactory = dbContextFactory;
		}

		public bool Handle(IHuChange message)
		{
			using IWcsDbContext db = _dbContextFactory.GetDbContext();

			Le le = db.Le.ByLeNo(message.LeNo);
			if (le == null)
			{
				Log.Write(LogLevel.Info, $"HuChange - HU {message.LeNo} is unknown. Create HU...");

				LeTypeName type = LeType.GetLeTypeFromLeNo(message.LeNo);
				if (type == null)
				{
					throw new LeNoWrongFormatException($"HuChange - The prefix of {message.LeNo} is not known.");
				}

				le = db.Add(_leFactory.RegularLe(message.LeNo, type)).Entity;
				db.SaveChanges();
			}
			UpdateLeTypeIfGiven(message, le, db);
			le.IsEmpty = message.IsLeEmpty ?? true;
			le.AbcArea = !le.IsEmpty ? SetAbcArea(message, le) : null;
			le.Subdivision = message.Subdivision?.ToEnumFromDescription<SubdivisionType>() ?? le.Subdivision;
			if (le.IsEmpty)
			{
				le.Weight = le.LeType.TareWeight; // Since there is no scale in the upper loops
				le.ArticleTag = null;
			}
			else
			{
				le.ArticleTag = message.ArticleTag;
			}

			db.SaveChanges();
			return true;
		}

		private static string SetAbcArea(IHuChange message, Le le)
		{
			if (message.AbcArea == null)
			{
				return le.AbcArea;
			}
			if (!Area.IsValid(message.AbcArea) && !Area.IsClassificationForOldStorage(message.AbcArea))
			{
				return Area.Z;
			}
			return message.AbcArea;
		}

		private static void UpdateLeTypeIfGiven(IHuChange message, Le le, IWcsDbContext db)
		{
			if (message.LeType == null)
			{
				return;
			}
			if (!Enum.IsDefined(typeof(LeTypeName), message.LeType))
			{
				throw new InvalidOperationException($"LeType={message.LeType} does not match an enum. Message: {message}");
			}

			LeType leType = db.LeType.SingleOrDefault(l => l.Name == (LeTypeName)Enum.Parse(typeof(LeTypeName), message.LeType));
			if (leType == null)
			{
				throw new InvalidOperationException($"LeType={message.LeType} does not match exactly one DB entry -. Message: {message}");
			}

			le.SetLeType(leType);
		}
	}
}