using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using PSCHelpdesk.Plugins;
using PSCHelpdesk.Shared.Plugin;

namespace PSCHelpdesk.Services;

public class PluginManager
{

    private List<Type> _pluginTypes;
    
    private Dictionary<string, string> foundPlugins = new Dictionary<string, string>();
    
    public Dictionary<string, string> GetPlugins()
    {
        return this.foundPlugins;
    }

    public PluginManager()
    {
        _pluginTypes = new List<Type>();
        LoadPlugins();
    }

    private void LoadPlugins()
    {
        var dllFileNames = Directory.GetFiles(getPluginPath(), "*.dll", SearchOption.TopDirectoryOnly);

        var assemblies = new List<Assembly>(dllFileNames.Length);
        foreach (string dllFile in dllFileNames)
        {
            assemblies.Add(LoadPlugin(dllFile));
        }

        var pluginType = typeof(Contract);
        var pluginTypes = new List<Type>();
        foreach (var assembly in assemblies)
        {
            if (assembly != null)
            {
                try
                {
                    var types = assembly.GetTypes();
                    foreach (var type in types)
                    {
                        if (type.IsInterface || type.IsAbstract)
                        {
                            continue;
                        }
                        else
                        {
                            if (pluginType.IsAssignableFrom(type))
                            {
                                pluginTypes.Add(type);
                                this.foundPlugins.Add(Path.GetFileNameWithoutExtension(assembly.FullName), assembly.Location);
                                
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    Debug.WriteLine($"Failed to load plugin {assembly.FullName}");
                }
            }
        }

        foreach (var type in pluginTypes)
        {
            var plugin = (Contract)Activator.CreateInstance(type);
            plugin.Configure();
            _pluginTypes.AddRange(plugin.LoadViews());
        }

    }
    
    private string getPluginPath()
    {
        var pathToThisProgram = Assembly.GetExecutingAssembly() // this assembly location (/bin/Debug/netcoreapp3.1)
            .Location;
        var pathToExecutingDir = Path.GetDirectoryName(pathToThisProgram);
        //return Path.GetFullPath(Path.Combine(pathToExecutingDir, "../../../../../_dist"));
        //return pathToExecutingDir;
        return Path.Combine(pathToExecutingDir, "plugins");
    }

    private static Assembly LoadPlugin(string fullPath)
    {
        PluginLoadContext loadContext = new PluginLoadContext(fullPath);
        return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(fullPath)));
    }

    public bool ViewExists(string name)
    {
        var found=  _pluginTypes.Exists(t => t.FullName == name);

        return found;
    }
    
    public Type GetView(string viewName)
    {
        return this._pluginTypes.First(e => e.FullName == viewName);
    }
}