using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper.Configuration.Conventions;
using Gebhardt.Shared;
using Gebhardt.Shared.DbAccess;
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.Wcs.InterfaceOldPlc.Services;
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework;
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces;
using Microsoft.EntityFrameworkCore;
using static Gebhardt.StoreWare.Wcs.Common.ConstantsCommon;

namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation
{
	public class ShipmentTransportOrderHandler : IHandleRecord<IShipmentTransportOrder>
	{
		private readonly IDestinationService _destinationService;
		private readonly ITransportOrderService _transportOrderService;
		private IShipmentTransportOrder _shipmentTransportOrder;
		private readonly LeFactory _leFactory = LeFactory.GetInstance();
		private readonly List<string> _allStorageDestinations;

		public ShipmentTransportOrderHandler(IDestinationService destinationService, ITransportOrderService transportOrderService)
		{
			_destinationService = destinationService;
			_transportOrderService = transportOrderService;
			_allStorageDestinations = _destinationService.Where(d => d.IsStorage).Select(d => d.Name).ToList();
		}

		public bool Handle(IShipmentTransportOrder message)
		{
			_shipmentTransportOrder = message;
			using WcsDbContext db = new();
			CheckTransportOrder(db);

			
			string cartonNo = _shipmentTransportOrder.LeNo;
			Le le = db.Le.ByLeNo(cartonNo);
			if (le == null)
			{
				Log.Write(LogLevel.Info, $"Carton number {cartonNo} is unknown. Creating a new carton...");

				// TODO: Add correct prefixes depending on which type of carton? Now it is set as unknown
				LeTypeName type = LeType.GetLeTypeFromLeNo(cartonNo);
				le = db.Add(_leFactory.RegularLe(cartonNo, type)).Entity;
				db.SaveChanges();
			}
			

			le.SetStatus(LeStatus.Created);
			CreateTransportOrder();
			db.SaveChanges();
			return true;
		}

		private void CheckTransportOrder(IWcsDbContext db)
		{
			if (_shipmentTransportOrder.LeNo == null)
			{
				throw new FromWmsException("The carton or EtraBox number is missing.");
			}
			if(_shipmentTransportOrder.Destination == null)
			{
				throw new FromWmsException("No destination set.");
			}
			if (_shipmentTransportOrder.LeNo.StartsWith("B"))
			{
				List<string> allowedEtraBoxDestinations = new List<string>() {"D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D99", "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12" };
				if(_shipmentTransportOrder.Destination.Split('/').ToList().Any(d => !allowedEtraBoxDestinations.Contains(d)))
				{
					throw new FromWmsException("Not allowed destination for EtraBox.");
				}				
			}
			else
			{
				HostEntities dbHost = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create(); ;
				var allowedDestinationsForCartons = dbHost.RouteSetting.Select(r => r.WmsRouteName).Distinct().ToList();
				if (!allowedDestinationsForCartons.Contains(_shipmentTransportOrder.Destination))
				{
					throw new FromWmsException("Not allowed destination for Carton/Paperbag/Pallet.");
				}
			}

			//Check if there is already an open order for the same LE (WMS is sending for each Pick)
			var openOrdes = db.OrdersHost.ByLeNo(_shipmentTransportOrder.LeNo).ByStatus(TransportOrderStatus.Pending, TransportOrderStatus.Initial, TransportOrderStatus.Transmitted).Where(o=>o.Source == _shipmentTransportOrder.Source && o.Destination == _shipmentTransportOrder.Destination);
			if(openOrdes.Any())
			{
				throw new FromWmsException("Order is already created.");
			}
			else
			{
				//Also check Finished orders from the last 2 Minutes, because WMS sometimes sends Departure before the TransportOrder...
				var  age = DBDateTime.Now - TimeSpan.FromMinutes(2);
				openOrdes = db.OrdersHost.ByLeNo(_shipmentTransportOrder.LeNo).ByStatus(TransportOrderStatus.Finished).Where(o => o.Source == _shipmentTransportOrder.Source && o.Destination == _shipmentTransportOrder.Destination && o.Timestamp > age);
				if (openOrdes.Any())
				{
					throw new FromWmsException("Order is already created.");
				}
			}
		}

		private void CreateTransportOrder()
		{
			using WcsDbContext db = new();
			string source = _shipmentTransportOrder.Source;

			OrdersHost ordersHost = OrdersHostFactory.GetInstance().InitialForLe(_shipmentTransportOrder.LeNo, TransportOrderType.TransportHost, source == TestToolConstants.TestToolSource ? Common.Constants.MfcAllDestinations.AKL01 : source,	_shipmentTransportOrder.Destination);
			db.Add(ordersHost);			

			//Only for Etra boxes we transmitt the order here. The other orders are started at the checkResult Telegram.
			
			if (ordersHost.Le.LeType.Name == LeTypeName.EtraBox)
			{				
				//Send Tord to Old PLC if allowed in Settings
				SettingsManager.GetParsedValue(SettingNames.SendTordToOldPlc, out bool sendTord, false, true);
				if (sendTord)
				{
					SendToOldPlcService.SendTord(ordersHost.LeNo, ordersHost.Destination.Split('/').ToList());
				}
				//We set the Status to transmitted here because we have already sent the telegram and we don't want ConveyorDispo to start those orders (OrdersConveyor is not needed, as there will be no feedback from PLC)
				ordersHost.ForceSetStatusTransmitted();
				//The EtraBox order will be closed as soon as we get the Departure message from WMS
			}

			
			db.SaveChanges();
		}
	}
}