using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Decal.Adapter;

namespace MosswartMassacre.Loader
{
    [FriendlyName("MosswartMassacre.Loader")]
    public class LoaderCore : FilterBase
    {
        private Assembly pluginAssembly;
        private Type pluginType;
        private object pluginInstance;
        private FileSystemWatcher pluginWatcher;
        private bool isSubscribedToRenderFrame = false;
        private bool needsReload;

        public static string PluginAssemblyNamespace => "MosswartMassacre";
        public static string PluginAssemblyName => $"{PluginAssemblyNamespace}.dll";
        public static string PluginAssemblyGuid => "{8C97E839-4D05-4A5F-B0C8-E8E778654322}";

        public static bool IsPluginLoaded { get; private set; }

        /// <summary>
        /// Assembly directory (contains both loader and plugin dlls)
        /// </summary>
        public static string AssemblyDirectory => System.IO.Path.GetDirectoryName(Assembly.GetAssembly(typeof(LoaderCore)).Location);

        public DateTime LastDllChange { get; private set; }

        #region Event Handlers
        protected override void Startup()
        {
            try
            {
                Core.PluginInitComplete += Core_PluginInitComplete;
                Core.PluginTermComplete += Core_PluginTermComplete;
                Core.FilterInitComplete += Core_FilterInitComplete;

                // Set up assembly resolution for hot-loaded plugin dependencies
                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

                // watch the AssemblyDirectory for any .dll file changes
                pluginWatcher = new FileSystemWatcher();
                pluginWatcher.Path = AssemblyDirectory;
                pluginWatcher.NotifyFilter = NotifyFilters.LastWrite;
                pluginWatcher.Filter = "*.dll";
                pluginWatcher.Changed += PluginWatcher_Changed;
                pluginWatcher.EnableRaisingEvents = true;
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void Core_FilterInitComplete(object sender, EventArgs e)
        {
            Core.EchoFilter.ClientDispatch += EchoFilter_ClientDispatch;
        }

        private void EchoFilter_ClientDispatch(object sender, NetworkMessageEventArgs e)
        {
            try
            {
                // Login_SendEnterWorldRequest
                if (e.Message.Type == 0xF7C8)
                {
                    //EnsurePluginIsDisabledInRegistry();
                }
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void Core_PluginInitComplete(object sender, EventArgs e)
        {
            try
            {
                LoadPluginAssembly();
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void Core_PluginTermComplete(object sender, EventArgs e)
        {
            try
            {
                UnloadPluginAssembly();
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        protected override void Shutdown()
        {
            try
            {
                Core.PluginInitComplete -= Core_PluginInitComplete;
                Core.PluginTermComplete -= Core_PluginTermComplete;
                Core.FilterInitComplete -= Core_FilterInitComplete;
                AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
                UnloadPluginAssembly();
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void Core_RenderFrame(object sender, EventArgs e)
        {
            try
            {
                if (IsPluginLoaded && needsReload && DateTime.UtcNow - LastDllChange > TimeSpan.FromSeconds(1))
                {
                    needsReload = false;
                    Core.RenderFrame -= Core_RenderFrame;
                    isSubscribedToRenderFrame = false;
                    LoadPluginAssembly();
                }
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void PluginWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                // Only reload if it's the main plugin DLL
                if (e.Name == PluginAssemblyName)
                {
                    LastDllChange = DateTime.UtcNow;
                    needsReload = true;

                    if (!isSubscribedToRenderFrame)
                    {
                        isSubscribedToRenderFrame = true;
                        Core.RenderFrame += Core_RenderFrame;
                    }
                }
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            try
            {
                // Extract just the assembly name (without version info)
                string assemblyName = args.Name.Split(',')[0] + ".dll";
                string assemblyPath = System.IO.Path.Combine(AssemblyDirectory, assemblyName);
                
                // If the dependency exists in our plugin directory, load it
                if (File.Exists(assemblyPath))
                {
                    return Assembly.LoadFrom(assemblyPath);
                }
            }
            catch (Exception ex)
            {
                Log($"AssemblyResolve failed for {args.Name}: {ex.Message}");
            }
            
            // Return null to let default resolution continue
            return null;
        }
        #endregion

        #region Plugin Loading/Unloading
        internal void LoadPluginAssembly()
        {
            try
            {
                if (IsPluginLoaded)
                {
                    UnloadPluginAssembly();
                    try
                    {
                        CoreManager.Current.Actions.AddChatText($"[MosswartMassacre] Reloading {PluginAssemblyName}", 5);
                    }
                    catch { }
                }

                pluginAssembly = Assembly.Load(File.ReadAllBytes(System.IO.Path.Combine(AssemblyDirectory, PluginAssemblyName)));
                pluginType = pluginAssembly.GetType($"{PluginAssemblyNamespace}.PluginCore");
                pluginInstance = Activator.CreateInstance(pluginType);

                // Set the AssemblyDirectory property if it exists
                var assemblyDirProperty = pluginType.GetProperty("AssemblyDirectory", BindingFlags.Public | BindingFlags.Static);
                assemblyDirProperty?.SetValue(null, AssemblyDirectory);

                // Set the IsHotReload flag if it exists
                var isHotReloadProperty = pluginType.GetProperty("IsHotReload", BindingFlags.Public | BindingFlags.Static);
                isHotReloadProperty?.SetValue(null, true);

                // The original template doesn't set up Host - it just calls Startup
                // The plugin should use CoreManager.Current.Actions instead of MyHost for hot reload scenarios
                // We'll set a flag so the plugin knows it's being hot loaded

                // Call Startup method
                var startupMethod = pluginType.GetMethod("Startup", BindingFlags.NonPublic | BindingFlags.Instance);
                startupMethod.Invoke(pluginInstance, new object[] { });

                IsPluginLoaded = true;
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }

        private void UnloadPluginAssembly()
        {
            try
            {
                if (pluginInstance != null && pluginType != null)
                {
                    MethodInfo shutdownMethod = pluginType.GetMethod("Shutdown", BindingFlags.NonPublic | BindingFlags.Instance);
                    shutdownMethod.Invoke(pluginInstance, null);
                    pluginInstance = null;
                    pluginType = null;
                    pluginAssembly = null;
                }
                IsPluginLoaded = false;
            }
            catch (Exception ex)
            {
                Log(ex);
            }
        }
        #endregion

        private void Log(Exception ex)
        {
            Log(ex.ToString());
        }

        private void Log(string message)
        {
            File.AppendAllText(System.IO.Path.Combine(AssemblyDirectory, "loader_log.txt"), $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}\n");
            try
            {
                CoreManager.Current.Actions.AddChatText($"[MosswartMassacre.Loader] {message}", 3);
            }
            catch { }
        }
    }
}