using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.Remoting;
using System.Threading;
using Exceptioneer.WindowsFormsClient;
using NLog;
using Ninject;
using NzbDrone.Common;
using NzbDrone.Common.Model;

namespace NzbDrone.Providers
{
    public class MonitoringProvider
    {
        private static readonly Logger logger = LogManager.GetLogger("Host.MonitoringProvider");

        private readonly IISProvider _iisProvider;
        private readonly ProcessProvider _processProvider;
        private readonly HttpProvider _httpProvider;
        private readonly ConfigFileProvider _configFileProvider;

        private int _pingFailCounter;
        private Timer _pingTimer;
        private Timer _processPriorityCheckTimer;

        [Inject]
        public MonitoringProvider(ProcessProvider processProvider, IISProvider iisProvider,
                                  HttpProvider httpProvider, ConfigFileProvider configFileProvider)
        {
            _processProvider = processProvider;
            _iisProvider = iisProvider;
            _httpProvider = httpProvider;
            _configFileProvider = configFileProvider;
        }

        public MonitoringProvider()
        {
        }

        public void Start()
        {
            AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e.ExceptionObject as Exception));

            AppDomain.CurrentDomain.ProcessExit += ProgramExited;
            AppDomain.CurrentDomain.DomainUnload += ProgramExited;
            
            _processPriorityCheckTimer = new Timer(EnsurePriority);
            _processPriorityCheckTimer.Change(TimeSpan.FromSeconds(15), TimeSpan.FromMinutes(30));

            _pingTimer = new Timer(PingServer);
            _pingTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(1));
        }


        public virtual void EnsurePriority(object sender)
        {
            var currentProcess = _processProvider.GetCurrentProcess();
            if (currentProcess.Priority != ProcessPriorityClass.Normal)
            {
                _processProvider.SetPriority(currentProcess.Id, ProcessPriorityClass.Normal);
            }

            var iisProcess = _processProvider.GetProcessById(_iisProvider.IISProcessId);
            if (iisProcess != null && iisProcess.Priority != ProcessPriorityClass.Normal &&
                iisProcess.Priority != ProcessPriorityClass.AboveNormal)
            {
                _processProvider.SetPriority(iisProcess.Id, ProcessPriorityClass.Normal);
            }
        }

        public virtual void PingServer(object sender)
        {
            if (!_iisProvider.ServerStarted) return;

            try
            {
                ICredentials identity = null;

                if (_configFileProvider.AuthenticationType == AuthenticationType.Windows)
                {
                    identity = CredentialCache.DefaultCredentials;
                }

                _httpProvider.DownloadString(_iisProvider.AppUrl, identity); //This should preload the home page, making the first load faster.
                string response = _httpProvider.DownloadString(_iisProvider.AppUrl + "/health", identity);

                if (!response.Contains("OK"))
                {
                    throw new ServerException("Health services responded with an invalid response.");
                }

                if (_pingFailCounter > 0)
                {
                    logger.Info("Application pool has been successfully recovered.");
                }

                _pingFailCounter = 0;
            }
            catch (Exception ex)
            {
                _pingFailCounter++;
                logger.ErrorException("Application pool is not responding. Count " + _pingFailCounter, ex);
                if (_pingFailCounter > 4)
                {
                    _pingFailCounter = 0;
                    //_iisProvider.RestartServer();
                }
            }
        }

        private void ProgramExited(object sender, EventArgs e)
        {
            _iisProvider.StopServer();
        }


        public static void AppDomainException(Exception excepion)
        {
            Console.WriteLine("EPIC FAIL: {0}", excepion);

            if (EnviromentProvider.IsProduction)
            {
                new Client
                    {
                        ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
                        ApplicationName = "NzbDrone",
                        CurrentException = excepion as Exception
                    }.Submit();
            }

            logger.FatalException("EPIC FAIL: " + excepion.Message, excepion);
        }
    }
}