using System;
using System.Collections.Generic;
using System.Linq;
using InABox.Clients;
using InABox.Core;

namespace InABox.Configuration
{
    public interface GlobalConfigurationSettings : IConfigurationSettings
    {
    }

    [UserTracking(false)]
    public class GlobalSettings : Entity, IPersistent, IRemotable, ILicense<CoreLicense>
    {
        public string Section { get; set; }

        public string Key { get; set; }

        public string Contents { get; set; }
    }

    public class GlobalConfiguration<T> : Configuration<T> where T : GlobalConfigurationSettings, new()
    {
        public GlobalConfiguration(string section = "") : base(section)
        {
        }

        private GlobalSettings NewSettings(string? section = null)
        {
            return new GlobalSettings { Section = typeof(T).Name, Key = section ?? Section };
        }

        private GlobalSettings GetSettings()
        {
            GlobalSettings result = null;
            if (ClientFactory.GetClientType() != null)
            {
                var client = new Client<GlobalSettings>();
                result = client.Load(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(typeof(T).Name).And(x => x.Key).IsEqualTo(Section))
                    .FirstOrDefault();
            }
            return result ?? NewSettings();
        }

        public override T Load(bool useCache = true)
        {
            if (useCache)
            {
                var cached = ConfigurationCache.Check<T>(ConfigurationCacheType.Global, Section);
                if (cached != null)
                    //Logger.Send(LogType.Information, "", "Global Config: Returning Cached Configuration Data");
                    return cached;
            }

            var result = new T();

            var setting = GetSettings();

            if (!string.IsNullOrEmpty(setting.Contents))
                result = Serialization.Deserialize<T>(setting.Contents);

            ConfigurationCache.Add(ConfigurationCacheType.Global, Section, result);
            return result;
        }

        public override Dictionary<string, T> LoadAll()
        {
            var result = new Dictionary<string, T>();
            var data = new Client<GlobalSettings>().Query(
                new Filter<GlobalSettings>(x => x.Section).IsEqualTo(typeof(T).Name),
                new Columns<GlobalSettings>(x => x.Key, x => x.Contents),
                new SortOrder<GlobalSettings>(x => x.Key)
            );
            foreach (var row in data.Rows)
                result[row.Get<GlobalSettings, string>(c => c.Key)] = Serialization.Deserialize<T>(row.Get<GlobalSettings, string>(c => c.Contents));
            return result;
        }

        public override void Save(T obj)
        {
            ConfigurationCache.Add(ConfigurationCacheType.Global, Section, obj);

            var setting = GetSettings();

            setting.Contents = Serialization.Serialize(obj);

            new Client<GlobalSettings>().Save(setting, "", (o, e) => { });
        }

        public override void SaveAll(Dictionary<string, T> objs)
        {
            foreach (var (section, obj) in objs)
            {
                ConfigurationCache.Add(ConfigurationCacheType.Global, section, obj);
            }

            var client = new Client<GlobalSettings>();
            var data = client.Load(
                new Filter<GlobalSettings>(x => x.Section).IsEqualTo(typeof(T).Name)
                    .And(x => x.Key).InList(objs.Keys.ToArray())).ToDictionary(x => x.Key, x => x);

            var settings = new List<GlobalSettings>(objs.Count);
            foreach(var (section, obj) in objs)
            {
                var contents = Serialization.Serialize(obj);
                if (!data.TryGetValue(section, out var setting))
                {
                    setting = NewSettings(section);
                }
                setting.Contents = contents;
                settings.Add(setting);
            }
            client.Save(settings, "", (o, e) => { });
        }

        public override void Delete(Action<Exception?>? callback = null)
        {
            ConfigurationCache.Clear<T>(ConfigurationCacheType.Global, Section);

            var client = new Client<GlobalSettings>();

            var result = client.Load(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(typeof(T).Name).And(x => x.Key).IsEqualTo(Section))
                .FirstOrDefault();

            if (result != null)
            {
                if(callback != null)
                {
                    client.Delete(result, "", (o, e) => { callback(e); });
                }
                else
                {
                    client.Delete(result, "");
                }
            }
        }

        public override string[] Sections()
        {
            var result = new Client<GlobalSettings>().Query(
                new Filter<GlobalSettings>(x => x.Section).IsEqualTo(typeof(T).Name),
                new Columns<GlobalSettings>(x => x.Key),
                new SortOrder<GlobalSettings>(x => x.Key)
            ).Rows.Select(r => r.Get<GlobalSettings, string>(c => c.Key)).Distinct().ToArray();
            return result;
        }
    }
}