using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using RabbitMQ.Client;
using Newtonsoft.Json;
using System.Reflection;
using System.Configuration;

namespace SciServer.Logging
{
    public class Logger : IDisposable
    {
        public string ApplicationHost;
        public string ApplicationName;
        public string ExchangeName;
        public string DatabaseQueueName;
        public string MessagingHost;
        public bool Enabled = true;

        private IConnection MessagingConnection;
        private IModel MessagingChannel;

        public Logger()
        {
        }

        public static Logger FromConfig()
        {
            return new Logger()
            {
                ApplicationHost = ConfigurationManager.AppSettings["Log.ApplicationHost"],
                ApplicationName = ConfigurationManager.AppSettings["Log.ApplicationName"],
                MessagingHost = ConfigurationManager.AppSettings["Log.MessagingHost"],
                DatabaseQueueName = ConfigurationManager.AppSettings["Log.DatabaseQueueName"],
                ExchangeName = ConfigurationManager.AppSettings["Log.ExchangeName"],
                Enabled = bool.Parse(ConfigurationManager.AppSettings["Log.Enabled"])
            };
        }

        public void Connect()
        {
            if (Enabled)
            {
                var factory = new ConnectionFactory() { HostName = MessagingHost };
                MessagingConnection = factory.CreateConnection();
                MessagingChannel = MessagingConnection.CreateModel();
                MessagingChannel.ExchangeDeclare(ExchangeName, "topic");
                MessagingChannel.QueueDeclare(DatabaseQueueName, false, false, false, null);
                MessagingChannel.QueueBind(DatabaseQueueName, ExchangeName, "#");
            }
        }

        public Message CreateFatalMessage(Exception ex)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.FATAL;
            AddExceptionBody(msg, ex);
            return msg;
        }

        public Message CreateErrorMessage(Exception ex)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.ERROR;
            AddExceptionBody(msg, ex);
            return msg;
        }

        private void AddExceptionBody(Message msg, Exception ex)
        {
            ExceptionMessageBody body = new ExceptionMessageBody()
            {
                ExceptionText = ex.Message,
                ExceptionType = ex.GetType().ToString(),
                StackTrace = ex.ToString()
            };
            msg.MessageBody = body;
        }

        public Message CreateWarnMessage(string text)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.WARN;
            AddTextBody(msg, text);
            return msg;
        }

        public Message CreateInfoMessage(string text)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.INFO;
            AddTextBody(msg, text);
            return msg;
        }

        public Message CreateCustomMessage(string customType, string content)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.CUSTOM;
            AddCustomBody(msg, customType, content);
            return msg;
        }

        private void AddCustomBody(Message msg, string customType, string content)
        {
            CustomMessageBody body = new CustomMessageBody
            {
                CustomType = customType,
                Content = content
            };
            msg.MessageBody = body;
        }

        public Message CreateDebugMessage(string text)
        {
            Message msg = CreateMessage();
            msg.MessageType = MessageType.DEBUG;
            AddTextBody(msg, text);
            return msg;
        }

        private void AddTextBody(Message msg, string text)
        {
            TextMessageBody body = new TextMessageBody()
            {
                Text = text
            };
            msg.MessageBody = body;
        }

        private Message CreateMessage()
        {
            Message msg = new Message() 
            {
                Host = this.ApplicationHost,
                Application = this.ApplicationName,
                Time = DateTime.Now,
                MessageId = Guid.NewGuid()
            };
            MethodBase method = new StackFrame(2, true).GetMethod();
            msg.Method = method.DeclaringType.FullName + "." + method.Name;
            return msg;
        }

        public void SendMessage(Message msg)
        {
            if (Enabled)
            {
                var jsonSettings = new JsonSerializerSettings()
                {
                    TypeNameHandling = TypeNameHandling.Objects
                };

                var jsonMessage = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg, jsonSettings));
                MessagingChannel.BasicPublish(ExchangeName, ApplicationName + "." + msg.GetTypeName(), null, jsonMessage);
            }
        }

        public void Dispose()
        {
            if (MessagingConnection != null) MessagingConnection.Dispose();
        }
    }
}
