using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Decal.DenAgent
{
    /// <summary>
    /// Main plugin management dialog. Shows all registered Decal plugins/services
    /// with enable/disable toggle, drag-drop reordering, and version display.
    /// Replaces the MFC CDenAgentDlg class.
    /// </summary>
    internal sealed class AgentForm : Form
    {
        private readonly ListView _pluginList;
        private readonly ImageList _imageList;
        private readonly Button _btnClose;
        private readonly Button _btnAdd;
        private readonly Button _btnRemove;
        private readonly Button _btnRefresh;
        private readonly Button _btnOptions;
        private readonly Label _lblMessages;
        private readonly Label _lblMemLocs;

        // Plugin group definitions matching the original DenAgent
        private static readonly PluginGroup[] Groups =
        {
            new("Plugins", "Plugins", 0),
            new("Network Filters", "NetworkFilters", 1),
            new("File Filters", "FileFilters", 1),
            new("Services", "Services", 2),
            new("Surrogates", "Surrogates", 3),
            new("Input Actions", "InputActions", 4),
        };

        public AgentForm()
        {
            Text = "Decal Agent " + GetAgentVersion();
            Size = new System.Drawing.Size(420, 360);
            FormBorderStyle = FormBorderStyle.FixedDialog;
            MaximizeBox = false;
            StartPosition = FormStartPosition.CenterScreen;

            // Image list for enabled/disabled/group icons
            _imageList = new ImageList { ImageSize = new System.Drawing.Size(16, 16) };
            // Index 0 = group (folder), 1 = enabled (check), 2 = disabled (x)
            _imageList.Images.Add(CreateColorIcon(System.Drawing.Color.SteelBlue));   // 0: group
            _imageList.Images.Add(CreateColorIcon(System.Drawing.Color.ForestGreen)); // 1: enabled
            _imageList.Images.Add(CreateColorIcon(System.Drawing.Color.Gray));        // 2: disabled

            // Plugin list view
            _pluginList = new ListView
            {
                Location = new System.Drawing.Point(10, 35),
                Size = new System.Drawing.Size(295, 230),
                View = View.Details,
                FullRowSelect = true,
                SmallImageList = _imageList,
                HeaderStyle = ColumnHeaderStyle.None,
                AllowDrop = true
            };
            _pluginList.Columns.Add("Component", 210);
            _pluginList.Columns.Add("Version", 80, HorizontalAlignment.Right);
            _pluginList.MouseClick += OnPluginListClick;
            Controls.Add(_pluginList);

            // Description label
            var lblDesc = new Label
            {
                Text = "Choose the Plugins you'd like to run.",
                Location = new System.Drawing.Point(10, 10),
                Size = new System.Drawing.Size(390, 20)
            };
            Controls.Add(lblDesc);

            // Buttons
            int btnX = 320;
            _btnClose = AddButton("Close", btnX, 300, (s, e) => Close());
            _btnRefresh = AddButton("Refresh", btnX, 270, (s, e) => LoadPluginList());
            _btnAdd = AddButton("Add", btnX, 35, (s, e) => OnAddPlugin());
            _btnRemove = AddButton("Remove", btnX, 65, (s, e) => OnRemovePlugin());
            _btnOptions = AddButton("Options", btnX, 105, (s, e) => OnShowOptions());

            // Status labels
            _lblMessages = new Label
            {
                Text = "Messages: -",
                Location = new System.Drawing.Point(10, 275),
                Size = new System.Drawing.Size(300, 16),
                Font = new System.Drawing.Font("Segoe UI", 8f)
            };
            Controls.Add(_lblMessages);

            _lblMemLocs = new Label
            {
                Text = "Memory Locations: -",
                Location = new System.Drawing.Point(10, 293),
                Size = new System.Drawing.Size(300, 16),
                Font = new System.Drawing.Font("Segoe UI", 8f)
            };
            Controls.Add(_lblMemLocs);

            AcceptButton = _btnClose;

            LoadPluginList();
            UpdateFileInfo();
        }

        private Button AddButton(string text, int x, int y, EventHandler click)
        {
            var btn = new Button
            {
                Text = text,
                Location = new System.Drawing.Point(x, y),
                Size = new System.Drawing.Size(80, 25)
            };
            btn.Click += click;
            Controls.Add(btn);
            return btn;
        }

        private void LoadPluginList()
        {
            _pluginList.BeginUpdate();
            _pluginList.Items.Clear();

            foreach (var group in Groups)
            {
                // Add group header
                var groupItem = new ListViewItem(group.DisplayName, 0)
                {
                    Tag = null // Group headers have no tag
                };
                groupItem.SubItems.Add("");
                _pluginList.Items.Add(groupItem);

                // Enumerate plugins in this group from registry
                string keyPath = $@"SOFTWARE\Decal\{group.RegistryKey}";
                try
                {
                    using var key = Registry.LocalMachine.OpenSubKey(keyPath);
                    if (key == null) continue;

                    foreach (string clsid in key.GetSubKeyNames())
                    {
                        using var pluginKey = key.OpenSubKey(clsid);
                        if (pluginKey == null) continue;

                        string name = pluginKey.GetValue("FriendlyName") as string
                                   ?? pluginKey.GetValue("") as string
                                   ?? clsid;

                        bool enabled = true;
                        object enabledVal = pluginKey.GetValue("Enabled");
                        if (enabledVal is int ei)
                            enabled = ei != 0;

                        var info = new PluginInfo(clsid, group.RegistryKey, name, enabled);
                        int imageIdx = enabled ? 1 : 2;

                        var item = new ListViewItem("    " + name, imageIdx)
                        {
                            Tag = info,
                            IndentCount = 1
                        };

                        // Get version from COM registration
                        string version = GetPluginVersion(clsid);
                        item.SubItems.Add(version);

                        _pluginList.Items.Add(item);
                    }
                }
                catch { /* Skip groups we can't read */ }
            }

            _pluginList.EndUpdate();
        }

        private void OnPluginListClick(object sender, MouseEventArgs e)
        {
            var hitTest = _pluginList.HitTest(e.Location);
            if (hitTest.Item == null) return;

            var info = hitTest.Item.Tag as PluginInfo;
            if (info == null) return; // Group header

            // Toggle enabled state when clicking the icon area
            if (e.X < 40)
            {
                info.Enabled = !info.Enabled;
                hitTest.Item.ImageIndex = info.Enabled ? 1 : 2;

                // Save to registry
                try
                {
                    string keyPath = $@"SOFTWARE\Decal\{info.GroupKey}\{info.Clsid}";
                    using var key = Registry.LocalMachine.OpenSubKey(keyPath, writable: true);
                    key?.SetValue("Enabled", info.Enabled ? 1 : 0, RegistryValueKind.DWord);
                }
                catch { /* May need admin rights */ }
            }
        }

        private void OnAddPlugin()
        {
            using var dlg = new OpenFileDialog
            {
                Title = "Select Plugin DLL",
                Filter = "DLL files (*.dll)|*.dll|All files (*.*)|*.*",
                CheckFileExists = true
            };

            if (dlg.ShowDialog(this) != DialogResult.OK)
                return;

            // Register the DLL as a COM server and add to Decal registry
            string dllPath = dlg.FileName;
            try
            {
                var psi = new ProcessStartInfo
                {
                    FileName = "regsvr32.exe",
                    Arguments = $"/s \"{dllPath}\"",
                    UseShellExecute = true,
                    Verb = "runas"
                };
                Process.Start(psi)?.WaitForExit();
                LoadPluginList();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Failed to register plugin: {ex.Message}", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void OnRemovePlugin()
        {
            if (_pluginList.SelectedItems.Count == 0) return;
            var item = _pluginList.SelectedItems[0];
            var info = item.Tag as PluginInfo;
            if (info == null) return;

            var result = MessageBox.Show(
                $"Remove plugin '{info.Name}'?\nThis will unregister it from Decal.",
                "Remove Plugin",
                MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);

            if (result != DialogResult.OK) return;

            try
            {
                // Check for uninstaller
                string keyPath = $@"SOFTWARE\Decal\{info.GroupKey}\{info.Clsid}";
                using var key = Registry.LocalMachine.OpenSubKey(keyPath);
                string uninstaller = key?.GetValue("Uninstaller") as string;

                if (!string.IsNullOrEmpty(uninstaller))
                {
                    Process.Start(uninstaller);
                }
                else
                {
                    // Just delete the registry entry
                    string parentPath = $@"SOFTWARE\Decal\{info.GroupKey}";
                    using var parentKey = Registry.LocalMachine.OpenSubKey(parentPath, writable: true);
                    parentKey?.DeleteSubKeyTree(info.Clsid, false);
                }

                LoadPluginList();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Failed to remove plugin: {ex.Message}", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void OnShowOptions()
        {
            using var dlg = new OptionsForm();
            dlg.ShowDialog(this);
        }

        private static string GetPluginVersion(string clsid)
        {
            try
            {
                string keyPath = $@"CLSID\{clsid}\InprocServer32";
                using var key = Registry.ClassesRoot.OpenSubKey(keyPath);
                if (key == null) return "<Not registered>";

                string dllPath = key.GetValue("") as string ?? "";

                // .NET assemblies register as mscoree.dll; get the CodeBase instead
                if (dllPath.EndsWith("mscoree.dll", StringComparison.OrdinalIgnoreCase))
                {
                    string codeBase = key.GetValue("CodeBase") as string ?? "";
                    if (codeBase.StartsWith("file:///", StringComparison.OrdinalIgnoreCase))
                        dllPath = codeBase.Substring(8).Replace('/', '\\');
                }

                if (!File.Exists(dllPath))
                    return "<No DLL>";

                var vi = FileVersionInfo.GetVersionInfo(dllPath);
                return vi.FileVersion ?? "<No Version>";
            }
            catch
            {
                return "<Error>";
            }
        }

        private void UpdateFileInfo()
        {
            // Check for XML data files used by Decal
            string agentPath = GetAgentPath();
            if (string.IsNullOrEmpty(agentPath)) return;

            string messagesFile = Path.Combine(agentPath, "messages.xml");
            string memlocsFile = Path.Combine(agentPath, "memlocs.xml");

            _lblMessages.Text = "Messages: " + (File.Exists(messagesFile)
                ? File.GetLastWriteTime(messagesFile).ToString("yyyy-MM-dd HH:mm")
                : "Not found");

            _lblMemLocs.Text = "Memory Locations: " + (File.Exists(memlocsFile)
                ? File.GetLastWriteTime(memlocsFile).ToString("yyyy-MM-dd HH:mm")
                : "Not found");
        }

        private static string GetAgentPath()
        {
            try
            {
                using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Decal\Agent");
                return key?.GetValue("AgentPath") as string ?? "";
            }
            catch { return ""; }
        }

        private static string GetAgentVersion()
        {
            try
            {
                var vi = FileVersionInfo.GetVersionInfo(
                    System.Reflection.Assembly.GetExecutingAssembly().Location);
                return vi.FileVersion ?? "0.0.0.0";
            }
            catch { return "0.0.0.0"; }
        }

        private static System.Drawing.Icon CreateColorIcon(System.Drawing.Color color)
        {
            using var bmp = new System.Drawing.Bitmap(16, 16);
            using var g = System.Drawing.Graphics.FromImage(bmp);
            g.Clear(System.Drawing.Color.Transparent);
            using var brush = new System.Drawing.SolidBrush(color);
            g.FillEllipse(brush, 2, 2, 12, 12);
            return System.Drawing.Icon.FromHandle(bmp.GetHicon());
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                Hide();
            }
            else
            {
                base.OnFormClosing(e);
            }
        }

        private class PluginGroup
        {
            public string DisplayName { get; }
            public string RegistryKey { get; }
            public int IconIndex { get; }
            public PluginGroup(string displayName, string registryKey, int iconIndex)
            {
                DisplayName = displayName;
                RegistryKey = registryKey;
                IconIndex = iconIndex;
            }
        }

        private class PluginInfo
        {
            public string Clsid { get; }
            public string GroupKey { get; }
            public string Name { get; }
            public bool Enabled { get; set; }

            public PluginInfo(string clsid, string groupKey, string name, bool enabled)
            {
                Clsid = clsid;
                GroupKey = groupKey;
                Name = name;
                Enabled = enabled;
            }
        }
    }
}
