using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.Remoting;
using System.Threading;
using NLog;
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;

        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)
        {
            try
            {
                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);
                }
            }
            catch (Exception e)
            {
                logger.WarnException("Unable to verify priority", e);
            }
        }

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

            try
            {
                ICredentials 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.Error("Application pool is not responding. Count: {0} - {1}", _pingFailCounter, ex.Message);
                if (_pingFailCounter >= 10)
                {
                    _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);

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