using GreenTree.Nachtragsmanagement.Core;
using GreenTree.Nachtragsmanagement.Core.Domain.Logging;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GreenTree.Nachtragsmanagement.Services.Logging
{
    public static class LoggingExtensions
    {
        public static void Debug(this IBaseLogger logger, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            FilteredLog(logger, LogLevel.Debug, message, exception, user);
        }

        public static void Information(this IBaseLogger logger, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            FilteredLog(logger, LogLevel.Information, message, exception, user);
        }

        public static void Warning(this IBaseLogger logger, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            FilteredLog(logger, LogLevel.Warning, message, exception, user);
        }

        public static void Error(this IBaseLogger logger, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            FilteredLog(logger, LogLevel.Error, message, exception, user);
        }

        public static void Fatal(this IBaseLogger logger, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            FilteredLog(logger, LogLevel.Fatal, message, exception, user);
        }

        private static void FilteredLog(IBaseLogger logger, LogLevel level, string message, Exception exception = null,
            Core.Domain.User.User user = null)
        {
            //don't log thread abort exception
            if (exception is System.Threading.ThreadAbortException)
                return;

            if (logger.IsEnabled(level))
            {
                var fullMessage = exception == null 
                    ? string.Empty 
                    : exception.ToString();

                logger.InsertLog(level, message, null, fullMessage, user);
            }
        }

        public static void Entity(this IBaseLogger logger, BaseEntity entity, LogEntityActivity activity, Core.Domain.User.User user = null)
        {
            if (entity == null)
                return;

            var header = String.Empty;
            var body = String.Empty;
            var entityType = GetTypeNameFromEfEntityType(entity);

            switch (activity)
            {
                case LogEntityActivity.Insert:
                    header = String.Format("Entität \"{0}\" wurde hinzugefügt", entityType);
                    break;
                case LogEntityActivity.Update:
                    header = String.Format("Entität \"{0}\" mit der ID \"{1}\" wurde geändert", entityType, entity.Id);
                    break;
                case LogEntityActivity.Delete:
                    header = String.Format("Entität \"{0}\" mit der ID \"{1}\" wurde gelöscht", entityType, entity.Id);
                    break;
            }

            if (activity == LogEntityActivity.Delete)
                logger.InsertLog(LogLevel.Information, header, entity, String.Empty, user);
            else
            {
                var properties = entity.GetType().GetProperties();

                foreach (var property in properties)
                {
                    if (!property.PropertyType.Namespace.StartsWith("System")) continue;

                    if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string))
                    {
                        var collection = property.GetValue(entity, null) as IEnumerable;
                        var collectionType = collection.GetType().GetGenericArguments()[0];

                        if (typeof(BaseEntity).IsAssignableFrom(collectionType))
                        {
                            var collectionValues = new List<object>();

                            foreach (var value in collection)
                                collectionValues.Add(value.GetType().GetProperty("Id").GetValue(value));

                            body += String.Format("\t{0} = {1}\n", property.Name, String.Join(", ", collectionValues));
                        }
                        else
                            body += String.Format("\t{0} = {1}\n", property.Name, String.Join(", ", collection));
                    }
                    else
                        body += String.Format("\t{0} = {1}\n", property.Name, property.GetValue(entity));
                }

                var fullMessage = String.Format("{0}:\n{1}", header, body);

                logger.InsertLog(LogLevel.Information, header, entity, fullMessage, user);
            }
        }

        public static string GetTypeNameFromEfEntityType(BaseEntity entity)
        {
            if (entity == null) return null;

            var type = entity.GetType();

            if (type.Namespace == "System.Data.Entity.DynamicProxies")
                return String.Join("_",
                    type.Name.Split('_')
                        .TakeWhile(s => s != type.Name.Split('_').Last()));

            return type.Name;
        }
    }
}