using ExtensiblePortfolioSite.SDK.Git;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;

namespace ExtensiblePortfolioSite.SDK.Plugins
{
    internal class Plugin : IPluginManagedLibrary
    {
        private class PluginLoadContext : AssemblyLoadContext
        {
            private readonly Plugin Plugin;

            public PluginLoadContext(Plugin Plugin) : base(true) => this.Plugin = Plugin;

            protected override IntPtr LoadUnmanagedDll(String unmanagedDllName)
            {
                IPluginUnmanagedLibrary? Lib = PluginManager.LoadUnmanagedLibrary(unmanagedDllName);
                if (Lib == null)
                    return IntPtr.Zero;

                Plugin.References.Add(Lib);
                return Lib.LibraryPtr;
            }

            protected override Assembly? Load(AssemblyName assemblyName)
            {
                IPluginManagedLibrary? Lib = PluginManager.LoadLibrary(assemblyName);
                if (Lib == null)
                    return PluginManager.GetFixedAssembly(assemblyName);

                Plugin.References.Add(Lib);
                return Lib.Assembly;
            }
        }

        public String Location { get; private set; }
        public Assembly Assembly { get; private set; }
        public EPSPluginAttribute Info { get; private set; }

        public IList<IPluginLibrary> Dependents { get; } = new List<IPluginLibrary>();
        IReadOnlyList<IPluginLibrary> IPluginLibrary.References => References.AsReadOnly();
        public List<IPluginLibrary> References = new();

        public Boolean Initialized { get; private set; }

        private readonly PluginLoadContext LoadContext;
        internal Plugin(String Location)
        {
            this.Location = Location;
            this.LoadContext = new PluginLoadContext(this);
            this.Assembly = this.LoadContext.LoadFromAssemblyPath(this.Location);
            this.Info = this.Assembly.GetCustomAttribute<EPSPluginAttribute>() ?? throw new InvalidOperationException("Target Assembly is not a Plugin Assembly!");
        }


        private readonly List<KeyValuePair<String, IGitProvider>> GitProviders = new();
        public void Init()
        {
            if (Initialized)
                throw new InvalidOperationException();

            Initialized = true;
            foreach (Type t in this.Assembly.ExportedTypes)
            {
                try
                {
                    // Load Git Providers
                    if (t.GetInterface(nameof(IGitProvider)) != null)
                    {
                        GitProviderAttribute? Attr = t.GetCustomAttribute<GitProviderAttribute>();
                        if (Attr != null)
                        {
                            IGitProvider Provider = (IGitProvider)Activator.CreateInstance(t)!;
                            GitManager.Register(Attr.WebSite, Provider);
                            GitProviders.Add(new(Attr.WebSite, Provider));
                        }
                    }


                }
                catch
                {
                    // TODO: Error Logging
                }
            }
        }




        public void Disposing()
        {
            foreach (KeyValuePair<String, IGitProvider> provider in GitProviders)
                GitManager.Unregister(provider.Key, provider.Value);
            this.GitProviders.Clear();

            this.LoadContext.Unload();
        }
    }
}
