using System;
using System.IO;
using TweetLib.Core.Serialization;

namespace TweetLib.Core.Features.Configuration{
    public sealed class FileConfigInstance<T> : IConfigInstance<T> where T : BaseConfig{
        public T Instance { get; }
        public FileSerializer<T> Serializer { get; }

        private readonly string filenameMain;
        private readonly string filenameBackup;
        private readonly string identifier;

        public FileConfigInstance(string filename, T instance, string identifier){
            this.filenameMain = filename;
            this.filenameBackup = filename + ".bak";
            this.identifier = identifier;

            this.Instance = instance;
            this.Serializer = new FileSerializer<T>();
        }

        private void LoadInternal(bool backup){
            Serializer.Read(backup ? filenameBackup : filenameMain, Instance);
        }

        public void Load(){
            Exception? firstException = null;
            
            for(int attempt = 0; attempt < 2; attempt++){
                try{
                    LoadInternal(attempt > 0);

                    if (firstException != null){ // silently log exception that caused a backup restore
                        App.ErrorHandler.Log(firstException.ToString());
                    }

                    return;
                }catch(FileNotFoundException){
                }catch(DirectoryNotFoundException){
                    break;
                }catch(Exception e){
                    if (firstException == null){
                        firstException = e;
                    }
                }
            }
            
            if (firstException is FormatException){
                OnException($"The configuration file for {identifier} is outdated or corrupted. If you continue, your {identifier} will be reset.", firstException);
            }
            else if (firstException is SerializationSoftException sse){
                OnException($"{sse.Errors.Count} error{(sse.Errors.Count == 1 ? " was" : "s were")} encountered while loading the configuration file for {identifier}. If you continue, some of your {identifier} will be reset.", firstException);
            }
            else if (firstException != null){
                OnException($"Could not open the configuration file for {identifier}. If you continue, your {identifier} will be reset.", firstException);
            }
        }

        public void Save(){
            try{
                if (File.Exists(filenameMain)){
                    File.Delete(filenameBackup);
                    File.Move(filenameMain, filenameBackup);
                }

                Serializer.Write(filenameMain, Instance);
            }catch(SerializationSoftException e){
                OnException($"{e.Errors.Count} error{(e.Errors.Count == 1 ? " was" : "s were")} encountered while saving the configuration file for {identifier}.", e);
            }catch(Exception e){
                OnException($"Could not save the configuration file for {identifier}.", e);
            }
        }

        public void Reload(){
            try{
                LoadInternal(false);
            }catch(FileNotFoundException){
                try{
                    Serializer.Write(filenameMain, Instance.ConstructWithDefaults<T>());
                    LoadInternal(false);
                }catch(Exception e){
                    OnException($"Could not regenerate the configuration file for {identifier}.", e);
                }
            }catch(Exception e){
                OnException($"Could not reload the configuration file for {identifier}.", e);
            }
        }

        public void Reset(){
            try{
                File.Delete(filenameMain);
                File.Delete(filenameBackup);
            }catch(Exception e){
                OnException($"Could not delete configuration files to reset {identifier}.", e);
                return;
            }
            
            Reload();
        }

        private static void OnException(string message, Exception e){
            App.ErrorHandler.HandleException("Configuration Error", message, true, e);
        }
    }
}