using CareFusion.Lib.StorageSystem.Attributes;
using CareFusion.Lib.StorageSystem.Input;
using CareFusion.Lib.StorageSystem.Interfaces.Messages.Input;
using Rowa.Mosaic.Shared.Converters;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Serialization;

namespace CareFusion.Lib.StorageSystem.Wwks2.Messages.Input
{
    public class InputResponsePackHandling : IInputResponsePackHandling
    {
        #region Private members

        private List<string> _allowedSerializationProperties { get; set; }

        private string _input { get; set; }

        private string _text { get; set; }

        #endregion

        #region Constructors

        public InputResponsePackHandling()
        {
        }

        public InputResponsePackHandling(List<string> allowedSerializationProperties)
        {
            if (allowedSerializationProperties != null)
            {
                this._allowedSerializationProperties = allowedSerializationProperties;
            }
        }

        #endregion

        #region IInputResponsePackHandling specific properties

        /// <inheritdoc/>
        [XmlAttribute]
        [UserSelectableProperty(isMandatory: true, propertyType: typeof(InputHandling))]
        public InputHandling Input
        {
            get => TypeConverter.ConvertEnum(this._input, InputHandling.Rejected);
            set
            {
                try
                {
                    this._input = TextConverter.EscapeInvalidXmlChars(value.ToString());
                }
                catch
                {
                    this._input = TextConverter.EscapeInvalidXmlChars(InputHandling.Rejected.ToString());
                }
            }
        }

        /// <inheritdoc/>
        [XmlAttribute]
        [UserSelectableProperty]
        public string Text
        {
            get => TextConverter.EscapeInvalidXmlChars(this._text);
            set => this._text = TextConverter.EscapeInvalidXmlChars(value);
        }

        #endregion

        #region Serialization properties

        [XmlIgnore]
        public bool InputSpecified => this.IsPropertySpecified(nameof(this.Input), this._input != null);

        [XmlIgnore]
        public bool TextSpecified => this.IsPropertySpecified(nameof(this.Text), this._text != null);

        #endregion

        #region Private methods

        /// <summary>
        /// Determines whether a property is specified and thus could be included in <see cref="InputResponse"/> message.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="hasValue">if set to <c>true</c> [has value].</param>
        /// <returns>
        ///   <c>true</c> if [is property specified] [the specified property name]; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>The property's value is checked by its private string property which must not be nullable</remarks>
        private bool IsPropertySpecified(string propertyName, bool hasValue)
        {
            Debug.Assert(!string.IsNullOrEmpty(propertyName));

            var propertyIsUserSelectable = this.GetType()
                .GetProperty(propertyName)
                .GetCustomAttributes(false)
                .Any(i => i.GetType().Name.Equals($"{nameof(UserSelectablePropertyAttribute)}"));

            if (!propertyIsUserSelectable)
            {
                return hasValue;
            }

            return hasValue
                && (this._allowedSerializationProperties == null
                || this._allowedSerializationProperties.Any(p => p.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase)));
        }

        #endregion
    }
}