using Newtonsoft.Json;
using System;
using System.IO;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;

namespace Pilz.Configuration;

public class SettingsManager : ISettingsManager
{
    public event EventHandler AutoSavingSettings;
    public event EventHandler SavingSettings;
    public event EventHandler SavedSettings;
    public event EventHandler<ErrorEventArgs> OnSerializationError;

    protected ISettings defaultInstance = null;
    protected bool enableAutoSave = false;
    protected bool addedHandler = false;

    public string ConfigFilePath { get; set; }

    public ISettings Instance
    {
        get
        {
            if (defaultInstance is null)
                Load();
            return defaultInstance;
        }
    }

    public bool AutoSaveOnExit
    {
        get => enableAutoSave;
        set
        {
            if (enableAutoSave != value)
            {
                enableAutoSave = value;
                if (enableAutoSave)
                {
                    if (!addedHandler)
                        AddAutoSaveHandler();
                }
                else
                {
                    if (addedHandler)
                        RemoveAutoSaveHandler();
                }
            }
        }
    }

    public SettingsManager()
    {
    }

    public SettingsManager(string fileName, bool autoSaveOnExit) : this()
    {
        ConfigFilePath = fileName;
        AutoSaveOnExit = autoSaveOnExit;
    }

    protected void AddAutoSaveHandler()
    {
        AppDomain.CurrentDomain.ProcessExit += AutoSaveSettingsOnExit;
        addedHandler = true;
    }

    protected void RemoveAutoSaveHandler()
    {
        AppDomain.CurrentDomain.ProcessExit -= AutoSaveSettingsOnExit;
        addedHandler = false;
    }

    private void AutoSaveSettingsOnExit(object sender, EventArgs e)
    {
        AutoSavingSettings?.Invoke(this, new EventArgs());
        Save();
    }

    public void Save()
    {
        if (!string.IsNullOrEmpty(ConfigFilePath) && defaultInstance is not null)
            SaveInternal();
    }

    public void Load()
    {
        if (!string.IsNullOrEmpty(ConfigFilePath) && File.Exists(ConfigFilePath))
            LoadInternal();
        else
            CreateNewInstance();
    }

    public void Reset()
    {
        Instance.Reset();
    }

    protected virtual ISettings CreateInstance()
    {
        return new Settings();
    }

    protected virtual void CreateNewInstance()
    {
        defaultInstance = CreateInstance();
        defaultInstance.Reset();
    }

    protected virtual void LoadInternal()
    {
        defaultInstance = CreateInstance();
        defaultInstance.Load(CreateJsonSerializerSettings(), File.ReadAllText(ConfigFilePath));
    }

    protected virtual void SaveInternal()
    {
        SavingSettings?.Invoke(this, EventArgs.Empty);
        File.WriteAllText(ConfigFilePath, defaultInstance.Save(CreateJsonSerializerSettings()));
        SavedSettings?.Invoke(this, EventArgs.Empty);
    }

    public virtual JsonSerializerSettings CreateJsonSerializerSettings()
    {
        return new JsonSerializerSettings
        {
            Formatting = Formatting.Indented,
            TypeNameHandling = TypeNameHandling.Auto,
            Error = JsonSerializer_OnError,
        };
    }

    protected virtual void JsonSerializer_OnError(object sender, ErrorEventArgs e)
    {
        const string errorResolvingType = "Error resolving type specified in JSON";

        // Invoke event
        OnSerializationError?.Invoke(sender, e);

        // Handle ourself
        if (!e.ErrorContext.Handled && e.ErrorContext.Error is JsonSerializationException serializationException && serializationException.Message.StartsWith(errorResolvingType))
            e.ErrorContext.Handled = true;
    }
}