using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Pilz.Plugins
{
    public class PluginManager<TPluginInterface, TPluginRuntimeInfo> where TPluginInterface : class where TPluginRuntimeInfo : PluginRuntimeInfo<TPluginInterface>
    {
        public IEnumerable<TPluginRuntimeInfo> LoadPlugins(string[] paths)
        {
            return LoadPlugins(paths, null);
        }

        public IEnumerable<TPluginRuntimeInfo> LoadPlugins(string[] paths, params object?[]? parameters)
        {
            var states = new List<TPluginRuntimeInfo>();

            foreach (string path in paths)
                states.Add(LoadPlugin(path));

            return states;
        }

        public TPluginRuntimeInfo LoadPlugin(string path)
        {
            return LoadPlugin(path, null);
        }

        public TPluginRuntimeInfo LoadPlugin(string path, params object?[]? parameters)
        {
            var info = Activator.CreateInstance<TPluginRuntimeInfo>();
            var irmplugin = typeof(TPluginInterface);
            var loadContext = new PluginLoadContext(path);
            Assembly? assembly = null;

            if (File.Exists(path))
            {
                try
                {
                    assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
                }
                catch
                {
                    info.Status = PluginStatus.ErrorAtLoading;
                }
            }
            else
                info.Status = PluginStatus.FileNotFound;

            if (assembly != null)
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (info.Plugin == null && irmplugin.IsAssignableFrom(type))
                    {
                        try
                        {
                            if (Activator.CreateInstance(type, parameters) is TPluginInterface plugin)
                            {
                                info.Plugin = plugin;
                                info.Status = PluginStatus.Success;
                            }
                        }
                        catch
                        {
                            info.Status = PluginStatus.ErrorAtInitializing;
                        }
                    }
                }

                if (info.Plugin == null)
                    info.Status = PluginStatus.NoValidPlugin;
            }

            return info;
        }
    }
}
