using Dinah.Core.Collections.Generic;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

#nullable enable
namespace LibationFileManager
{
    public static class QuickFilters
    {
        public static event EventHandler? Updated;

		public static event EventHandler? UseDefaultChanged;

        public class FilterState
        {
            public bool UseDefault { get; set; }
            public List<NamedFilter> Filters { get; set; } = new();
        }

        public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles.Location, "QuickFilters.json");


        // load json into memory. if file doesn't exist, nothing to do. save() will create if needed
        public static FilterState? InMemoryState { get; set; }

        public static bool UseDefault
        {
            get => InMemoryState?.UseDefault ?? false;
            set
            {
                if (InMemoryState is null || UseDefault == value)
                    return;

                lock (locker)
                {
                    InMemoryState.UseDefault = value;
                    save(false);
                }

                UseDefaultChanged?.Invoke(null, EventArgs.Empty);
            }
        }

        // Note that records overload equality automagically, so should be able to
        // compare these the same way as comparing simple strings.
        public record NamedFilter(string Filter, string? Name)
        {
            public string Filter { get; set; } = Filter;
            public string? Name { get; set; } = Name;
        }

        public static IEnumerable<NamedFilter> Filters
            => InMemoryState?.Filters.AsReadOnly() ?? Enumerable.Empty<NamedFilter>();

        public static void Add(NamedFilter namedFilter)
        {
            if (namedFilter == null) 
                throw new ArgumentNullException(nameof(namedFilter));

            if (string.IsNullOrWhiteSpace(namedFilter.Filter))
                return;
            namedFilter.Filter = namedFilter.Filter?.Trim() ?? string.Empty;
            namedFilter.Name = namedFilter.Name?.Trim() ?? null;

			lock (locker)
			{
				InMemoryState ??= new();
				// check for duplicates
				if (InMemoryState.Filters.Select(x => x.Filter).ContainsInsensative(namedFilter.Filter))
                    return;

                InMemoryState.Filters.Add(namedFilter);
                save();
            }
        }

        public static void Remove(NamedFilter filter)
        {
            lock (locker)
            {
                if (InMemoryState is null)
                    return;
                InMemoryState.Filters.Remove(filter);
                save();
            }
        }

        public static void Edit(NamedFilter oldFilter, NamedFilter newFilter)
        {
            lock (locker)
            {
                if (InMemoryState is null || InMemoryState.Filters.IndexOf(oldFilter) < 0)
                    return;

                InMemoryState.Filters = InMemoryState.Filters.Select(f => f == oldFilter ? newFilter : f).ToList();

                save();
            }
        }

        public static void ReplaceAll(IEnumerable<NamedFilter> filters)
        {
            filters = filters
                .Where(f => !string.IsNullOrWhiteSpace(f.Filter))
                .Distinct();
            foreach (var filter in filters)
                filter.Filter = filter.Filter.Trim();
            lock (locker)
            {
                InMemoryState ??= new();
				InMemoryState.Filters = new List<NamedFilter>(filters);
                save();
            }
        }

        private static object locker { get; } = new();

        // ONLY call this within lock()
        private static void save(bool invokeUpdatedEvent = true)
        {
            // create json if not exists
            void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(InMemoryState, Formatting.Indented));
            try { resave(); }
            catch (IOException)
            {
                try { resave(); }
				catch (IOException ex)
				{
					Serilog.Log.Logger.Error(ex, "Error saving QuickFilters.json");
					throw;
				}
			}

            if (invokeUpdatedEvent)
                Updated?.Invoke(null, EventArgs.Empty);
        }
    }
}
