using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using CareFusion.Mosaic.Converters.Wwks2.Types;
using CareFusion.Mosaic.Interfaces.Converters;
using CareFusion.Mosaic.Interfaces.Messages;
using CareFusion.Mosaic.Interfaces.Messages.Input;
using CareFusion.Mosaic.Interfaces.Types.Articles;
using CareFusion.Mosaic.Interfaces.Types.Packs;
using CareFusion.Mosaic.Interfaces.Types.Stock;
using Rowa.Mosaic.Shared.Converters;

namespace CareFusion.Mosaic.Converters.Wwks2.Messages.Input
{
    /// <summary>
    /// Class which represents the WWKS 2.0 InputMessage message envelope.
    /// </summary>
    [XmlType(TypeName = "WWKS")]
    public class InputMessageEnvelope : EnvelopeBase, IMessageConversion
    {
        [XmlElement]
        public InputMessage InputMessage { get; set; }

        /// <summary>
        /// Translates this object instance into a Mosaic message.
        /// </summary>
        /// <param name="converterStream">The converter stream instance which request the message conversion.</param>
        /// <returns>
        /// The Mosaic message representation of this object.
        /// </returns>
        public MosaicMessage ToMosaicMessage(IConverterStream converterStream)
        {
            return this.InputMessage.ToMosaicMessage(converterStream);
        }
    }

    /// <summary>
    /// Class which represents the WWKS 2.0 InputMessage message.
    /// <see cref="https://portalrowa.carefusion.com/Unternehmen/Entwicklung/TechCom/Writing/P-010-044-B-I-388-DEU.docx" />
    /// </summary>
    public class InputMessage : MessageBase, IInputMessage
    {
        #region IInputMessage Specific Properties

        /// <inheritdoc/>
        [XmlAttribute]
        public bool IsNewDelivery { get; set; }

        /// <inheritdoc/>
        [XmlElement(ElementName = "Article")]
        public List<InputMessageArticle> Articles { get; set; } = new List<InputMessageArticle>();

        [XmlElement]
        public InputComponent InputComponent { get; set; }


        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="InputMessage"/> class.
        /// </summary>
        public InputMessage()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="InputMessage"/> class.
        /// </summary>
        /// <param name="message">The message to use for initialization.</param>
        public InputMessage(MosaicMessage message)
        {
            if (message == null)
            {
                return;
            }

            var stockInputMessage = (StockInputMessage)message;

            this.Id = stockInputMessage.ID;
            this.Source = stockInputMessage.Source;
            this.Destination = stockInputMessage.Destination;
            this.IsNewDelivery = stockInputMessage.IsNewDelivery;
            this.InputComponent = new InputComponent
            {
                Name = stockInputMessage.ComponentName,
                Id = stockInputMessage.ComponentId
            };

            if (stockInputMessage.Articles?.FirstOrDefault() == null)
            {
                return;
            }

            foreach (var robotArticle in stockInputMessage.Articles.Distinct())
            {
                var newArticleEntry = new InputMessageArticle
                {
                    DosageForm = TextConverter.EscapeInvalidXmlChars(robotArticle.DosageForm),
                    Id = TextConverter.EscapeInvalidXmlChars(robotArticle.Code),
                    Name = TextConverter.EscapeInvalidXmlChars(robotArticle.Name),
                    PackagingUnit = TextConverter.EscapeInvalidXmlChars(robotArticle.PackagingUnit),
                    MaxSubItemQuantity = (uint)robotArticle.MaxSubItemQuantity
                };

                for (int i = 0; i < stockInputMessage.Packs.Count; ++i)
                {
                    if (!stockInputMessage.Packs[i].RobotArticleCode.Equals(robotArticle.Code))
                    {
                        continue;
                    }

                    newArticleEntry.Packs.Add(new InputMessagePack
                    {
                        Index = (uint)i,
                        Id = (ulong)stockInputMessage.Packs[i].ID,
                        DeliveryNumber = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].DeliveryNumber),
                        BatchNumber = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].BatchNumber),
                        SerialNumber = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].SerialNumber),
                        ExternalId = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].ExternalID),
                        ExpiryDate = TypeConverter.ConvertDateNull(stockInputMessage.Packs[i].ExpiryDate),
                        ExpiryDateSource = stockInputMessage.Packs[i].ExpiryDateSource,
                        StockInDate = TypeConverter.ConvertDateNull(stockInputMessage.Packs[i].StockInDate),
                        ScanCode = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].ScanCode),
                        Depth = (uint)stockInputMessage.Packs[i].Depth,
                        Width = (uint)stockInputMessage.Packs[i].Width,
                        Height = (uint)stockInputMessage.Packs[i].Height,
                        Shape = stockInputMessage.Packs[i].Shape,
                        State = stockInputMessage.Packs[i].IsBlocked ? PackState.NotAvailable : PackState.Available,
                        IsInFridge = stockInputMessage.Packs[i].IsInFridge,
                        SubItemQuantity = (uint)stockInputMessage.Packs[i].SubItemQuantity,
                        StockLocationId = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].StockLocationID),
                        MachineLocation = TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].MachineLocation),
                        GUID = Guid.Parse(TextConverter.EscapeInvalidXmlChars(stockInputMessage.Packs[i].Guid.ToString())),
                        Handling = new InputMessagePackHandling
                        {
                            Input = stockInputMessage.Handlings[stockInputMessage.Packs[i]],
                            Text = string.Empty
                        }
                    });
                }

                this.Articles.Add(newArticleEntry);
            }
        }

        #endregion

        #region IMessageConversion Specific properties

        /// <inheritdoc/>
        public MosaicMessage ToMosaicMessage(IConverterStream converterStream)
        {
            if (converterStream == null)
            {
                return default;
            }

            var stockInputMessage = new StockInputMessage(converterStream)
            {
                ID = this.Id,
                Source = this.Source,
                Destination = this.Destination,
                IsNewDelivery = this.IsNewDelivery
            };

            if (!this.Articles.Any())
            {
                return stockInputMessage;
            }

            foreach (var article in this.Articles)
            {
                var robotArticle = new RobotArticle
                {
                    DosageForm = TextConverter.UnescapeInvalidXmlChars(article.DosageForm),
                    Code = TextConverter.UnescapeInvalidXmlChars(article.Id),
                    Name = TextConverter.UnescapeInvalidXmlChars(article.Name),
                    PackagingUnit = TextConverter.UnescapeInvalidXmlChars(article.PackagingUnit),
                    MaxSubItemQuantity = (int)article.MaxSubItemQuantity
                };

                stockInputMessage.Articles.Add(robotArticle);

                foreach (var pack in article.Packs)
                {
                    var newRobotPackEntry = new RobotPack
                    {
                        ID = (long)pack.Id,
                        DeliveryNumber = TextConverter.UnescapeInvalidXmlChars(pack.DeliveryNumber),
                        BatchNumber = TextConverter.UnescapeInvalidXmlChars(pack.BatchNumber),
                        ExternalID = TextConverter.UnescapeInvalidXmlChars(pack.ExternalId),
                        ExpiryDate = TypeConverter.ConvertDate(pack.ExpiryDate),
                        ExpiryDateSource=TypeConverter.ConvertEnum(pack.ExpiryDateSource.ToString(), ExpiryDateSource.Unknown),
                        StockInDate = TypeConverter.ConvertDate(pack.StockInDate),
                        ScanCode = TextConverter.UnescapeInvalidXmlChars(pack.ScanCode),
                        Depth = (int)pack.Depth,
                        Width = (int)pack.Width,
                        Height = (int)pack.Height,
                        Shape = TypeConverter.ConvertEnum(pack.Shape.ToString(), PackShape.Cuboid),
                        IsBlocked = TypeConverter.ConvertEnum(pack.State.ToString(), PackState.Available) != PackState.Available,
                        SubItemQuantity = (int)pack.SubItemQuantity,
                        StockLocationID = string.IsNullOrEmpty(pack.StockLocationId) ? string.Empty : TextConverter.UnescapeInvalidXmlChars(pack.StockLocationId),
                        MachineLocation = string.IsNullOrEmpty(pack.MachineLocation) ? string.Empty : TextConverter.UnescapeInvalidXmlChars(pack.MachineLocation),
                        IsInFridge = pack.IsInFridge,
                        Guid = pack.GUID
                    };

                    stockInputMessage.Packs.Add(newRobotPackEntry);
                    stockInputMessage.Handlings.Add(newRobotPackEntry, pack.Handling.Input);
                }
            }

            return stockInputMessage;
        }

        #endregion
    }
}
