using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using SciServer.Logging;
using Newtonsoft.Json;
using System.Data.SqlClient;
using System.Configuration;

namespace SciServer.Logging.Listener
{
    class MessageProcessor
    {
        private static readonly string DatabaseQueueName = ConfigurationManager.AppSettings["Log.DatabaseQueueName"];

        public static void DoProcess()
        {
            var factory = new ConnectionFactory() { HostName = ConfigurationManager.AppSettings["Log.MessagingHost"] };
            
            using (var mqConnection = factory.CreateConnection())
            {
                using (var channel = mqConnection.CreateModel())
                {
                    channel.QueueDeclare(DatabaseQueueName, false, false, false, null);
                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume(DatabaseQueueName, true, consumer);

                    while (true)
                    {
                        var eventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                        var body = eventArgs.Body;
                        var jsonMessage = Encoding.UTF8.GetString(body);

                        var jsonSettings = new JsonSerializerSettings()
                        {
                            TypeNameHandling = TypeNameHandling.Objects
                        };

                        Message msg = JsonConvert.DeserializeObject<Message>(jsonMessage,jsonSettings);

                        
                        using (SqlConnection dbConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Log.Database"].ConnectionString))
                        {
                            dbConnection.Open();

                            using (SqlTransaction transaction = dbConnection.BeginTransaction()) 
                            {
                                try {
                                    using (SqlCommand command = new SqlCommand())
                                    {
                                        command.Connection = dbConnection;
                                        command.Transaction = transaction;

                                        command.CommandText = "INSERT INTO Messages VALUES(@message_id,@message_type,@host,@application,@method,@user_id,@time)";
                                        command.Parameters.AddWithValue("message_id", msg.MessageId);
                                        command.Parameters.AddWithValue("message_type", msg.MessageType);
                                        command.Parameters.AddWithValue("host", msg.Host);
                                        command.Parameters.AddWithValue("application", msg.Application);
                                        command.Parameters.AddWithValue("method", msg.Method);
                                        command.Parameters.AddWithValue("user_id", (object)msg.UserId ?? DBNull.Value);
                                        command.Parameters.AddWithValue("time", msg.Time);
                                        command.ExecuteNonQuery();
                                    }

                                    if (msg.MessageBody is TextMessageBody)
                                    {
                                        using (SqlCommand command = new SqlCommand())
                                        {
                                            command.Connection = dbConnection;
                                            command.Transaction = transaction;

                                            command.CommandText = "INSERT INTO TextMessages VALUES(@message_id,@text)";
                                            command.Parameters.AddWithValue("message_id", msg.MessageId);
                                            command.Parameters.AddWithValue("text", (msg.MessageBody as TextMessageBody).Text);
                                            command.ExecuteNonQuery();
                                        }
                                    }
                                    else if (msg.MessageBody is ExceptionMessageBody)
                                    {
                                        using (SqlCommand command = new SqlCommand())
                                        {
                                            command.Connection = dbConnection;
                                            command.Transaction = transaction;

                                            command.CommandText = "INSERT INTO ExceptionMessages VALUES(@message_id,@exception_type,@exception_text,@stack_trace)";
                                            command.Parameters.AddWithValue("message_id", msg.MessageId);
                                            command.Parameters.AddWithValue("exception_type", (msg.MessageBody as ExceptionMessageBody).ExceptionType);
                                            command.Parameters.AddWithValue("exception_text", (msg.MessageBody as ExceptionMessageBody).ExceptionText);
                                            command.Parameters.AddWithValue("stack_trace", (object)(msg.MessageBody as ExceptionMessageBody).StackTrace ?? DBNull.Value);
                                            command.ExecuteNonQuery();
                                        }
                                    }
                                    else if (msg.MessageBody is CustomMessageBody)
                                    {
                                        using (SqlCommand command = new SqlCommand())
                                        {
                                            command.Connection = dbConnection;
                                            command.Transaction = transaction;

                                            command.CommandText = "INSERT INTO CustomMessages (message_id,custom_message_type,content) SELECT @message_id, id, @content FROM CustomMessageTypes WHERE name = @name";
                                            command.Parameters.AddWithValue("message_id", msg.MessageId);
                                            command.Parameters.AddWithValue("name", (msg.MessageBody as CustomMessageBody).CustomType);
                                            command.Parameters.AddWithValue("content", (msg.MessageBody as CustomMessageBody).Content);

                                            command.ExecuteNonQuery();
                                        }
                                    }
                                    else throw new Exception("Unsupported message type: " + msg.MessageType);

                                    transaction.Commit();
                                }
                                catch 
                                {
                                    try { transaction.Rollback(); } catch {}
                                    throw;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
