using IPA.Config;
using IPA.Config.Stores;
using IPA.Config.Stores.Attributes;
using IPA.Config.Stores.Converters;
using IPA.Logging;
using IPA.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#if NET4
using Task = System.Threading.Tasks.Task;
using TaskEx = System.Threading.Tasks.Task;
#endif
#if NET3
using Net3_Proxy;
#endif

namespace IPA.Loader
{
    internal class DisabledConfig
    {
        public static Config.Config Disabled { get; set; }

        public static DisabledConfig Instance;

        public static void Load()
        {
            Disabled = Config.Config.GetConfigFor("Disabled Mods", "json");
            Instance = Disabled.Generated<DisabledConfig>();
        }

        public virtual bool Reset { get; set; } = true;

        [NonNullable]
        [UseConverter(typeof(CollectionConverter<string, HashSet<string>>))]
        public virtual HashSet<string> DisabledModIds { get; set; } = new HashSet<string>();

        protected internal virtual void Changed() { }
        protected internal virtual IDisposable ChangeTransaction() => null;

        private Task disableUpdateTask = null;
        private int updateState = 0;

        protected virtual void OnReload()
        {
            if (DisabledModIds == null || Reset)
            {
                DisabledModIds = new HashSet<string>();
                Reset = false;
            }

            if (!PluginLoader.IsFirstLoadComplete) return; // if the first load isn't complete, skip all of this

            var referToState = unchecked(++updateState);
            var copy = DisabledModIds.ToArray();
            if (disableUpdateTask == null || disableUpdateTask.IsCompleted)
            {
                disableUpdateTask = UpdateDisabledMods(copy);
            }
            else
            {
                disableUpdateTask = disableUpdateTask.ContinueWith(t =>
                {
                    // skip if another got here before the last finished
                    if (referToState != updateState) return TaskEx.WhenAll();
                    else return UpdateDisabledMods(copy);
                });
            }
        }

        private Task UpdateDisabledMods(string[] updateWithDisabled)
        {
            do
            {
                using var transaction = PluginManager.PluginStateTransaction();
                var disabled = transaction.DisabledPlugins.ToArray();
                foreach (var plugin in disabled)
                    transaction.Enable(plugin, autoDeps: true);

                var all = transaction.EnabledPlugins.ToArray();
                foreach (var plugin in all.Where(m => updateWithDisabled.Contains(m.Id)))
                    transaction.Disable(plugin, autoDependents: true);

                try
                {
                    if (transaction.WillNeedRestart)
                        Logger.Loader.Warn("Runtime disabled config reload will need game restart to apply");
                    return transaction.Commit().ContinueWith(t =>
                    {
                        if (t.IsFaulted)
                        {
                            Logger.Loader.Error("Error changing disabled plugins");
                            Logger.Loader.Error(t.Exception);
                        }
                    });
                }
                catch (InvalidOperationException)
                {
                    continue;
                }
            }
            while (true);
        }
    }
}
