using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using LuhnNet;
using Newtonsoft.Json; //Do not remove - Required for JObject
using Newtonsoft.Json.Linq;
using Shinydocs.CognitiveToolkit.Scripting;
using Shinydocs.CognitiveToolkit.Core.Models.Index;
using Shinydocs.CognitiveToolkit.Core.Tools.ScrollTools.RunScript;
using Shinydocs.CognitiveToolkit.UI.CommandLine;
using Shinydocs.CognitiveToolkit.UI.CommandLine.Progress;
using Shinydocs.CognitiveToolkit.Utilities; //Do not remove - Required for JObject
#pragma warning disable CS8603
#pragma warning disable CS8619
#pragma warning disable CS8604
#pragma warning disable CS8600
#pragma warning disable CS8601
#pragma warning disable CS8618

public class ExportUniqueValues : IScript
{
	// ScriptLogger allows you to log to the same index as the rest of the cognitive toolkit. 
	// See https://github.com/serilog/serilog/wiki/Writing-Log-Events for message formatting information
	private readonly ScriptLogger _log = new ScriptLogger();
	private static string ScriptName = "ExportUniqueValues";
	private static string Version = "1.0.0.1";
	private string _indexName;
	private string _query = string.Empty;
	private string[] _fieldsToReturn = {"id"};
	private string _serverUrl;
	public string _fieldName;
	public string _output;
	private List<string> _uniqueValues = new List<string>();
	private RunScriptDocumentUpdater _documentUpdater;
	private int _threads;
	private int _nodesPerRequest;
	public void SetUp(string[] arguments){
	
		_log.Information(string.Format("{0} version {1}", ScriptName, Version));
		Console.WriteLine(string.Format("{0} version {1}", ScriptName, Version));
		try
		{
			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("--field-name", out _fieldName)) OptionsParser.InputError("The field name (--field-name) is a required parameter.");
				if (!options.TryGetValue("--output-file", out _output)) _output = "UniqueValues.txt";

				_fieldsToReturn = _fieldsToReturn.Append(_fieldName).ToArray();			
				_documentUpdater = new RunScriptDocumentUpdater(_serverUrl, _indexName, _fieldsToReturn, _nodesPerRequest, _threads);
			}
			else
			{
				throw new ArgumentException("Failed to parse arguments. Tool requires exactly the following parameters -q (Query or Path to QueryFile) -u (Index Server Url) -i (IndexName) --field-name.");
			}
		}
		catch (Exception ex)
		{
			var message = ex.Message + "\n\nPlease review the following parameters:"
			                         + "\n" + "-q \tquery or path to queryfile"
			                         + "\n" + "-u \tindex server URL"
			                         + "\n" + "-i \tindex name"
			                         + "\n" + "--field-name \tfield name"
									 + "\n" + "--output-file"
			                         + "\n" + "--threads (Default: 1)"
			                         + "\n" + "--nodes-per-request (Default: 100)";
			_log.Error(ex, message);
			Console.WriteLine(message);
			throw;
		}
	}

	public void Run()
	{
		var processedTotal = 0;
		var updatedTotal = 0;
		using (var progress = new CommandLineProgress())
		{
			_documentUpdater.Progress = progress;
			_documentUpdater.Update(_query,document =>
			{
				// Check if the value is an array or a single value
				var value = document[_fieldName];
				Interlocked.Increment(ref processedTotal);
                if (value is JArray)
                {
                	// If it's an array, process each item
                	foreach (string item in ((JArray)value).ToObject<string[]>())
                	{
                		if (!string.IsNullOrEmpty(item) && !_uniqueValues.Contains(item))
                		{
                			_uniqueValues.Add(item);
							Interlocked.Increment(ref updatedTotal);
                		}
                	}
                }
                else
                {
                	string stringValue = value?.ToString() ?? string.Empty;
                	if (!string.IsNullOrEmpty(stringValue) && !_uniqueValues.Contains(stringValue))
                	{
                		_uniqueValues.Add(stringValue);
						Interlocked.Increment(ref updatedTotal);
                	}
                }
				RunScriptDocumentUpdater.TotalProcessed = processedTotal;
				return new Dictionary<string, object>();
			});
		}
	}

	public void TearDown()
	{
		string filePath = _output;
		JArray jsonArray = new JArray();

		// Add each unique value to the JArray
		foreach (var value in _uniqueValues)
		{
			jsonArray.Add(value);
		}

		// Serialize the JArray to a JSON string
		string jsonString = JsonConvert.SerializeObject(jsonArray, Formatting.Indented);

		// Write the JSON string to a file
		File.WriteAllText(filePath, jsonString);
	}
}