using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using Newtonsoft.Json; //Do not remove - Required for JObject
using Newtonsoft.Json.Linq;
using Shinydocs.CognitiveToolkit.Core.Tools.ScrollTools.RunScript; //Do not remove - Required for JObject
using Shinydocs.CognitiveToolkit.Scripting;
using Shinydocs.CognitiveToolkit.UI.CommandLine.Progress;

public class MoveValues : IScript
{
	private static string Version = "2.6.0";
	private readonly ScriptLogger _log = new ScriptLogger();
	public string[] _fieldsToReturn = { "id" };
	private string _indexName;
	public string _oldFieldName;
	public string _newFieldName;
	private string _query;
	private string _serverUrl;
	public string _clearInput;
	public bool _clearOldValue;
	private RunScriptDocumentUpdater _documentUpdater;
	private int _threads;
	private int _nodesPerRequest;

	public void SetUp(string[] arguments)
	{
		try
		{
			_log.Information(string.Format("Using version {0}", Version));
			Console.WriteLine(string.Format("Using version {0}", Version));
			Dictionary<string, string> options;
			// OptionsParser will parse an array of arguments into a dictionary of flags and values
			if (OptionsParser.TryParse(arguments, out options))
			{
				
				OptionsParser.ParseStandardOptions(options, out _serverUrl, out _indexName, out _threads, out _nodesPerRequest);
				OptionsParser.ParseQueryOption(options, out _query);
				
				if (!options.TryGetValue("--old-field-name", out _oldFieldName)) InputError("The old field name parameter(--old-field-name) is a required");
				if (!options.TryGetValue("--new-field-name", out _newFieldName)) InputError("The new field name parameter(--new-field-name) is a required");
				
				if (!options.TryGetValue("--clear", out _clearInput)) _clearInput = "false";
				
				_fieldsToReturn = _fieldsToReturn.Append(_oldFieldName).ToArray();

				_log.Information((string.Format("MoveValues tool setup method -q {0}, -u {1}, -i {2}, " +
				                                "--oldFieldName {3}, --newFieldName {4}, --clear {5}",
					_query, _serverUrl, _indexName, _oldFieldName, _newFieldName, _clearInput)));

				_documentUpdater = new RunScriptDocumentUpdater(_serverUrl, _indexName, _fieldsToReturn, _nodesPerRequest, _threads)
				{
					RemoveNullValues = false
				};
				_clearOldValue = Convert.ToBoolean(_clearInput);
			}
			else
			{
				throw new ArgumentException("Failed to parse arguments");
			}

		}
		catch (Exception)
		{
			Console.WriteLine("MoveValues tool requires the following parameters -q for query, -u for Index server Url, -i for IndexName, " +
							  "--oldFieldName for previous fieldName, --clear to clear the values from source field and --newFieldName for the new fieldName ");
			throw;
		}
	}
	
	private static void InputError(string message)
	{
		Console.WriteLine(message);
		throw new ArgumentException(message);
	}

	public void Run()
	{
		using (var progress = new CommandLineProgress())
		{
			var totalProcessed = 0;
			var totalUpdated = 0;
			_documentUpdater.Progress = progress;
			_documentUpdater.Update(_query,document =>
			{
				var id = document["id"];
				Interlocked.Increment(ref totalProcessed);
				try
				{
					var update = MoveValueFromField(document, _newFieldName, _oldFieldName);
					if (update)
					{
						Interlocked.Increment(ref totalUpdated);
						return document.ToObject<Dictionary<string, object>>();
					}
				}
				catch (Exception ex)
				{
					_log.Error(ex, string.Format("Error processing {0}, {1}", id, ex.Message));
				}

				return new Dictionary<string, object>();
			});
		}
		
	}

	public bool MoveValueFromField(JObject document, string newFieldName, string oldFieldName)
	{
		if (document.ContainsKey(oldFieldName))
		{
			
			document[newFieldName] = document[oldFieldName];
			if (_clearOldValue)
			{
				document[oldFieldName] = null;
			}

			return true;
		}

		return false;
	}


	public void TearDown()
	{

	}
}