using System;
using System.Collections.Generic;
using DataLayer;
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;

namespace LibationSearchEngine
{
    // field names are case specific and, due to StandardAnalyzer, content is case INspecific
    internal static class LuceneExtensions
	{
		internal static void AddAnalyzed(this Document document, string name, string value)
		{
			if (value is not null)
				document.Add(new Field(name.ToLowerInvariant(), value, Field.Store.YES, Field.Index.ANALYZED));
		}        

        internal static void RemoveRule(this Document document, IndexRule rule)
		{
			// fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY.
			// ie: must remove old before adding new else will create unwanted duplicates.
			foreach (var name in rule.FieldNames)
				document.RemoveFields(name.ToLowerInvariant());
		}

        internal static void AddIndexRule(this Document document, IndexRule rule, LibraryBook libraryBook)
		{
			string value = rule.GetValue(libraryBook);
			if (value is null) return;

			foreach (var name in rule.FieldNames)
			{
				// fields are key value pairs and MULTIPLE FIELDS CAN HAVE THE SAME KEY.
				// splitting authors and narrators and/or tags into multiple fields could be interesting research.
				// it could allow for more advanced searches, or maybe it could break broad searches.

				// all searching should be lowercase
				// external callers have the reasonable expectation that product id will be returned CASE SPECIFIC
				var field = rule.FieldType switch
				{
					FieldType.Bool => new Field(name.ToLowerInvariant(), value, Field.Store.YES, Field.Index.ANALYZED_NO_NORMS),
					FieldType.String => new Field(name.ToLowerInvariant(), value, Field.Store.YES, Field.Index.ANALYZED),
					FieldType.Number => new Field(name.ToLowerInvariant(), value, Field.Store.YES, Field.Index.NOT_ANALYZED),
					FieldType.ID => new Field(name.ToLowerInvariant(), value, Field.Store.YES, Field.Index.NOT_ANALYZED),
					FieldType.Raw => new Field(name, value, Field.Store.YES, Field.Index.NOT_ANALYZED),
					_ => throw new KeyNotFoundException(),
				};

				document.Add(field);
			}
		}

        internal static Query GetQuery(this Analyzer analyzer, string defaultField, string searchString)
            => new QueryParser(SearchEngine.Version, defaultField.ToLowerInvariant(), analyzer).Parse(searchString);

        // put all numbers, including dates, into this format:
        // ########.##
        internal const int PAD_DIGITS = 8;
        internal const string DECIMAL_PRECISION = ".00";
        internal static string ToLuceneString(this int i) => ((double)i).ToLuceneString();
        internal static string ToLuceneString(this float f) => ((double)f).ToLuceneString();
        internal static string ToLuceneString(this DateTime dt)
            => dt.ToString("yyyyMMdd") + DECIMAL_PRECISION;
        internal static string ToLuceneString(this DateTime? dt)
            => dt?.ToLuceneString() ?? "";
        internal static string ToLuceneString(this double d)
            => d.ToString("0" + DECIMAL_PRECISION).PadLeft(PAD_DIGITS + DECIMAL_PRECISION.Length, '0');
    }
}
