using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Text;
using System.Threading;
using NLog;

namespace Mirantis.Murano.WindowsAgent
{
	[DisplayName("Murano Agent")]
	sealed public class Program : WindowsService
	{
		private static readonly Logger Log = LogManager.GetCurrentClassLogger();
		private volatile bool stop;
		private Thread thread;
		private RabbitMqClient rabbitMqClient;
		private int delayFactor = 1;
		private string plansDir;

		static void Main(string[] args)
		{
			Start(new Program(), args);
		}

		protected override void OnStart(string[] args)
		{
			base.OnStart(args);

			Log.Info("Version 0.5.3");

			this.rabbitMqClient = new RabbitMqClient();

			var basePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
			this.plansDir = Path.Combine(basePath, "plans");


			if (!Directory.Exists(plansDir))
			{
				Directory.CreateDirectory(plansDir);
			}

			this.thread = new Thread(Loop);
			this.thread.Start();
		}

		void Loop()
		{
			const string unknownName = "unknown";
			while (!stop)
			{
				try
				{
					foreach (var file in Directory.GetFiles(this.plansDir, "*.json.result")
						.Where(file => !File.Exists(Path.Combine(this.plansDir, Path.GetFileNameWithoutExtension(file)))))
					{
						var id = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file)) ?? unknownName;
						if (id.Equals(unknownName, StringComparison.InvariantCultureIgnoreCase))
						{
							id = "";
						}

						var result = File.ReadAllText(file);
						Log.Info("Sending results for {0}", id ?? unknownName);
						rabbitMqClient.SendResult(new MqMessage { Body = result, Id =  id });
						File.Delete(file);
					}

					var path = Directory.EnumerateFiles(this.plansDir, "*.json").FirstOrDefault();
					if (path == null)
					{
						var message = rabbitMqClient.GetMessage();
						var id = message.Id;
						if(string.IsNullOrEmpty(id))
						{
							id = unknownName;
						}
						
						path = Path.Combine(this.plansDir, string.Format("{0}.json", id));
						File.WriteAllText(path, message.Body);
						Log.Info("Received new execution plan {0}", id);
						message.Ack();
					}
					else
					{
						var id = Path.GetFileNameWithoutExtension(path);
						Log.Info("Executing exising plan {0}", id);
					}
					var executor = new PlanExecutor(path);
					executor.Execute();
					File.Delete(path);
					delayFactor = 1;

					if (stop) break;
					if (executor.RebootNeeded)
					{
						Reboot();
					}
				}
				catch (Exception exception)
				{
					WaitOnException(exception);
				}
				
			}

		}

		private void Reboot()
		{
			Log.Info("Going for reboot!!");
			LogManager.Flush();
			/*try
			{
				System.Diagnostics.Process.Start("shutdown.exe", "-r -t 0");
			}
			catch (Exception ex)
			{
				Log.ErrorException("Cannot execute shutdown.exe", ex);
			}*/

		
			try
			{
				PowerShell.Create().AddCommand("Restart-Computer").AddParameter("Force").Invoke();
			}
			catch (Exception exception)
			{

				Log.FatalException("Reboot exception", exception);
			}
			finally
			{
				Log.Info("Waiting for reboot");
				for (var i = 0; i < 10 * 60 * 5 && !stop; i++)
				{
					Thread.Sleep(100);
				}
				Log.Info("Done waiting for reboot");
			}

		}

		private void WaitOnException(Exception exception)
		{
			if (stop) return;
			Log.WarnException("Exception in main loop", exception);
			var i = 0;
			while (!stop && i < 10 * (delayFactor * delayFactor))
			{
				Thread.Sleep(100);
				i++;
			}
			delayFactor = Math.Min(delayFactor + 1, 6);
		}

		protected override void OnStop()
		{
			stop = true;
			this.rabbitMqClient.Dispose();
			base.OnStop();
		}

	}
}
