using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Configuration;
using GreenTree.Nachtragsmanagement.Core.Configuration;
using GreenTree.Nachtragsmanagement.Core.Domain.Config;
using GreenTree.Nachtragsmanagement.Core.Data;

namespace GreenTree.Nachtragsmanagement.Services.Configuration
{
    public class ConfigurationService : IConfigurationService
    {
        #region Fields

        private readonly IRepository<ConfigItem> _configItemRepository;
        private readonly IRepository<UserConfigItem> _userConfigItemRepository;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the ConfigurationService class
        /// </summary>
        public ConfigurationService(
            IRepository<ConfigItem> configItemRepository,
            IRepository<UserConfigItem> userConfigItemRepository)
        {
            _configItemRepository = configItemRepository;
            _userConfigItemRepository = userConfigItemRepository;
        }

        #endregion

        /// <summary>
        /// Reads the current configuration from the global config
        /// </summary>
        public AppendixConfigurationSection GetCurrentConfiguration()
        {
            var config = WebConfigurationManager.OpenWebConfiguration("~/");
            var sectionGroup = config.GetSectionGroup("appendixSectionGroup");
            var section = sectionGroup.Sections["appendixConfigSection"];

            return (AppendixConfigurationSection)section;
        }

        #region ConfigItem

        /// <summary>
        /// Gets all configItems
        /// </summary>
        public IList<ConfigItem> GetAllConfigItems()
        {
            return _configItemRepository.Table.ToList();
        }

        /// <summary>
        /// Gets a configItem by specified Id
        /// </summary>
        /// <param name="id">ConfigItem identifier.</param>
        public ConfigItem GetConfigItemById(int id)
        {
            return _configItemRepository.GetById(id);
        }

        /// <summary>
        /// Gets a configItem by specified name
        /// </summary>
        /// <param name="name">ConfigItem name.</param>
        public ConfigItem GetConfigItemByName(string name)
        {
            return _configItemRepository.Table
                .FirstOrDefault(r => r.Name == name);
        }

        /// <summary>
        /// Gets all configItems to the specified ids
        /// </summary>
        public IList<ConfigItem> GetConfigItemsByIds(int[] ids)
        {
            return _configItemRepository.Table
                .Where(r => ids.Contains(r.Id))
                .ToList();
        }

        /// <summary>
        /// Insert a configItem
        /// </summary>
        /// <param name="configItem">ConfigItem.</param>
        public void InsertConfigItem(ConfigItem configItem)
        {
            _configItemRepository.Insert(configItem);
        }

        /// <summary>
        /// Update a configItem
        /// </summary>
        /// <param name="configItem">ConfigItem.</param>
        public void UpdateConfigItem(ConfigItem configItem)
        {
            _configItemRepository.Update(configItem);
        }

        /// <summary>
        /// Delete a configItem
        /// </summary>
        /// <param name="configItem">ConfigItem.</param>
        public void DeleteConfigItem(ConfigItem configItem)
        {
            _configItemRepository.Delete(configItem);
        }

        #endregion

        #region UserConfigItem

        /// <summary>
        /// Gets all userConfigItems
        /// </summary>
        public IList<UserConfigItem> GetAllUserConfigItems()
        {
            return _userConfigItemRepository.Table.ToList();
        }

        /// <summary>
        /// Gets all userConfigItems of corresponding user
        /// </summary>
        /// <param name="userId">User id.</param>
        public IList<UserConfigItem> GetUserConfigItemsByUserId(int userId)
        {
            return _userConfigItemRepository.Table
                .Where(u => u.UserId == userId)
                .ToList();
        }

        /// <summary>
        /// Gets a userConfigItem by specified Id
        /// </summary>
        /// <param name="id">UserConfigItem identifier.</param>
        public UserConfigItem GetUserConfigItemById(int id)
        {
            return _userConfigItemRepository.GetById(id);
        }

        /// <summary>
        /// Gets a userConfigItem by specified name and user id
        /// </summary>
        /// <param name="name">UserConfigItem name.</param>
        /// <param name="userId">User Id.</param>
        public UserConfigItem GetUserConfigItemByNameAndUserId(string name, int userId)
        {
            return _userConfigItemRepository.Table
                .FirstOrDefault(u => u.UserId == userId && u.Name == name);
        }

        /// <summary>
        /// Insert or update a userConfigItem
        /// </summary>
        /// <param name="userConfigItem">UserConfigItem.</param>
        public void InsertOrUpdateUserConfigItem(UserConfigItem userConfigItem)
        {
            if (userConfigItem == null)
                return;

            var existingConfig = GetUserConfigItemByNameAndUserId(userConfigItem.Name, userConfigItem.UserId);

            if (existingConfig != null)
            {
                existingConfig.Value = userConfigItem.Value;

                _userConfigItemRepository.Update(existingConfig);
            }
            else
                _userConfigItemRepository.Insert(userConfigItem);
        }

        /// <summary>
        /// Insert or update a userConfigItem
        /// </summary>
        /// <param name="name">UserConfigItem name.</param>
        /// <param name="userId">User Id.</param>
        /// <param name="value">New value.</param>
        public void InsertOrUpdateUserConfigItem(string name, int userId, string value)
        {
            var existingConfig = GetUserConfigItemByNameAndUserId(name, userId);

            if (existingConfig != null)
                existingConfig.Value = value;

            if (existingConfig != null)
            {
                existingConfig.Value = value;

                _userConfigItemRepository.Update(existingConfig);
            }
            else
                _userConfigItemRepository.Insert(new UserConfigItem
                {
                    Name = name,
                    UserId = userId,
                    Value = value
                });
        }

        /// <summary>
        /// Delete a userConfigItem
        /// </summary>
        /// <param name="userConfigItem">UserConfigItem.</param>
        public void DeleteUserConfigItem(UserConfigItem userConfigItem)
        {
            _userConfigItemRepository.Delete(userConfigItem);
        }

        #endregion

        #region Casting

        /// <summary>
        /// Trys to convert the config items value type and returns its actual value, otherwise NULL
        /// </summary>
        /// <typeparam name="T">The actual type.</typeparam>
        /// <param name="configItem">ConfigItem.</param>
        /// <param name="defaultValue">Value, when conversion fails.</param>
        public T TryGetConfigItemValue<T>(ConfigItem configItem, T defaultValue)
        {
            if (configItem == null)
                return defaultValue;

            object result = null;

            try
            {
                if (configItem.TypeFullName.StartsWith("ConfigurationReference"))
                {
                    var configurationReference = ConfigurationHelper.GetConfigurationReferenceByName(configItem.TypeFullName);

                    if (configurationReference != null)
                    {
                        if (configurationReference.IsMultipleSelection)
                        {
                            var vals = configurationReference.TransformValueToValueCollection(configItem.Value);

                            result = configurationReference.GetValues<T>(vals);
                        }
                        else
                            result = configurationReference.GetValue<T>(configItem.Value);
                    }
                }
                else
                {
                    result = Convert.ChangeType(configItem.Value, typeof(T));
                }
            }
            catch
            {
                result = defaultValue;
            }

            return (T)result;
        }

        /// <summary>
        /// Trys to convert the config items value type and returns its actual value, otherwise NULL
        /// </summary>
        /// <typeparam name="T">The actual type.</typeparam>
        /// <param name="configItemName">ConfigItem name.</param>
        /// <param name="defaultValue">Value, when conversion fails.</param>
        public T TryGetConfigItemValue<T>(string configItemName, T defaultValue)
        {
            if (String.IsNullOrEmpty(configItemName))
                return defaultValue;

            var configItem = GetConfigItemByName(configItemName);

            return TryGetConfigItemValue(configItem, defaultValue);
        }

        #endregion
    }
}
