﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using log4net;
using log4net.Config;
using XMRE.Notify.Lib;

namespace XMRE.OpnodeTracker
{
    public partial class OpnodeTrackerService : ServiceBase
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private readonly string _baseUrl;
        private readonly string _endpointPath;
        private readonly string _pingTarget;

        private Timer _attemptTimer;

        public OpnodeTrackerService()
        {
            InitializeComponent();

            var appSetings = new AppSettingsReader();
            _baseUrl = (string) appSetings.GetValue("baseUrl", typeof (string));
            _endpointPath = (string) appSetings.GetValue("endpointPath", typeof (string));
            _pingTarget = (string) appSetings.GetValue("pingTarget", typeof (string));

            CanHandlePowerEvent = true;
            CanHandleSessionChangeEvent = true;
            CanShutdown = true;

            XmlConfigurator.Configure();
            Log.Debug("log4net initialized");

            AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
        }

        private void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception) (e.ExceptionObject);
            if (ex.InnerException != null)
            {
                Log.Error(ex.InnerException.Message, ex);
                Log.DebugFormat("StackTrace:" + ex.InnerException.StackTrace);
            }
            Log.Error(ex.Message, ex);
            Log.DebugFormat("StackTrace:" + ex.StackTrace);
        }

        protected override void OnShutdown()
        {
            Log.Debug("Shutdown");

            base.OnShutdown();

            try
            {
                var dict = CreateDictionary();
                dict["reason"] = string.Empty;
                dict["event_type"] = "shutdown";

                Notify(dict);
            }
            // ReSharper disable once EmptyGeneralCatchClause
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
        {
            Log.DebugFormat("PowerEvent: {0}", powerStatus);

            var res = base.OnPowerEvent(powerStatus);

            try
            {
                var dict = CreateDictionary();
                dict["reason"] = powerStatus.ToString();
                dict["event_type"] = "power";

                Notify(dict);
            }
            // ReSharper disable once EmptyGeneralCatchClause
            catch (Exception ex)
            {
                Log.Error(ex);
            }

            return res;
        }

        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            Log.DebugFormat("SessionChange: {0}", changeDescription);

            base.OnSessionChange(changeDescription);

            ThreadPool.QueueUserWorkItem(p =>
            {
                try
                {
                    var dict = CreateDictionary();
                    dict["reason"] = changeDescription.Reason.ToString();
                    dict["session_id"] = changeDescription.SessionId.ToString();
                    dict["event_type"] = "session_change";
                    dict["env_username"] = SessionHelper.GetUsernameBySessionId(changeDescription.SessionId, true);

                    try
                    {
                        var ping = new Ping();
                        var response = ping.Send(_pingTarget, 1500);
                        if (response != null && response.Status == IPStatus.Success)
                        {
                            dict["ping_ms"] = response.RoundtripTime.ToString();
                        }
                        ping.Dispose();
                    }
                    // ReSharper disable once EmptyGeneralCatchClause
                    catch (Exception) { }

                    Notify(dict);
                }
                // ReSharper disable once EmptyGeneralCatchClause
                catch (Exception ex)
                {
                    Log.Error(ex.Message, ex);
                }
            });
        }

        protected override void OnStart(string[] args)
        {
            Log.DebugFormat("Started");

            if (_attemptTimer == null)
            {
                _attemptTimer = new Timer(OnAttempt, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
            }

            ThreadPool.QueueUserWorkItem(p => { Thread.Sleep(Int32.MaxValue); });
        }

        private void Notify(IDictionary<string, string> dict)
        {
            var notification = NotifyLib.CreateNotification(_baseUrl, dict);
            ThreadPool.QueueUserWorkItem(p =>
            {
                NotifyLib.Insert(notification);
                NotifyLib.Notify(notification);
            });
        }

        private Dictionary<string, string> CreateDictionary()
        {
            var dict = new Dictionary<string, string>();
            dict["endpoint"] = _endpointPath;
            
            dict["localtime"] = DateTime.Now.ToString("u");
            dict["timestamp"] = DateTime.Now.Ticks.ToString();
            dict["env_machinename"] = Environment.MachineName;

            // ReSharper disable once PossibleNullReferenceException
            dict["dns_hostname"] = Dns.GetHostEntry("").HostName;

            return dict;
        }

        protected override void OnStop()
        {
            if (_attemptTimer != null)
            {
                _attemptTimer.Dispose();
                _attemptTimer = null;
            }

            Log.DebugFormat("Stopped");
        }

        private static void OnAttempt(object state)
        {
            var incomplete = NotifyLib.PickIncomplete();
            if (incomplete != null)
            {
                NotifyLib.Notify(incomplete);
            }
        }
    }
}
