using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Dacryptero.Data;
using Dacryptero.DB;
using LiteDB;

namespace Dacryptero.Search.AST
{
    public abstract class FieldOptionBase : OptionBase
    {
        public string Name { get; }

        public FieldComparer Comparer { get; }

        public string? Value { get; }

        public bool? BoolValue { get; }

        public DateTime? DateTimeValue { get; }

        public double? DoubleValue { get; }

        public long? LongValue { get; }

        protected override string Key => throw new NotImplementedException();

        public FieldOptionBase(string name, FieldComparer comparer, string? value)
        {
            Name = name;
            Comparer = comparer;
            Value = value;
            BoolValue = bool.TryParse(value, out bool bv) ? bv : null;
            DateTimeValue = DateTime.TryParse(value, out DateTime dtv) ? dtv : null;
            DoubleValue = double.TryParse(value, out double dv) ? dv : null;
            LongValue = long.TryParse(value, out long lv) ? lv : null;
        }

        public override string ToString()
        {
            return $"{base.ToString()} {Name} {Comparer} {Value}";
        }

        public override SearchProgress Check(SearchContext context)
        {
            if (context.Part.Name != Name)
            {
                return new SearchProgress();
            }
            if (Value is null)
            {
                var rf = new ResultFound(context.Part.Type, Name);
                var value = context.Part.Value;
                if (value.IsString)
                {
                    rf.Preview.AddRange(SingleFieldSearcher.SlicePreviewAfter(
                        value.AsString,
                        0,
                        value.AsString.Length
                    ));
                }
                else
                {
                    rf.Preview.Add(new ResultFound.FoundPreview(
                        ResultFound.FoundPreviewType.Context,
                        value.Type switch
                        {
                            BsonType.Boolean => value.AsBoolean.ToString(),
                            BsonType.DateTime => value.AsDateTime.ToString("s"),
                            BsonType.Double => value.AsDouble.ToString(),
                            BsonType.Int32 => value.AsInt32.ToString(),
                            BsonType.Int64 => value.AsInt64.ToString(),
                            _ => value.ToString()
                        }
                    ));
                }
                return new SearchProgress(1, new Collections.TreeCollection<ResultFound>(rf));
            }
            else
            {
                var (score, res) = SingleFieldSearcher.Search(
                    context.Part.Type,
                    Name,
                    context.Part.Value,
                    this
                );
                var col = res is null ? new Collections.TreeCollection<ResultFound>()
                    : new Collections.TreeCollection<ResultFound>(res);
                return new SearchProgress(score, col);
            }
        }

        public override bool Equals(object? obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override async Task<SearchIndex> GetSearchIndexAsync(DBSingleController db,
            IndexSource source, CancellationToken cancellationToken
        )
        {
            if (Value is null)
                return SearchIndex.All;
            var index = SearchIndex.All;
            foreach (var token in Data.Index.Tokenify(Value))
            {
                var entry = await db.GetIndex(token, source, cancellationToken).ConfigureAwait(false);
                Serilog.Log.Verbose("Field Search: source={source} token='{token}' index={index}", source, token, entry);
                index.IntersectWith(entry);
            }
            return index;
        }
    }
}