using System.Reflection;
using System.Runtime.InteropServices;
using Sbroenne.ExcelMcp.ComInterop.Session;

namespace Sbroenne.ExcelMcp.CLI.Infrastructure;

/// <summary>
/// System tray icon for the ExcelMCP CLI daemon process.
/// Shows running sessions and allows closing them or stopping the service.
/// Ported from the old ExcelMcp.Service.ServiceTray with auto-update removed.
/// </summary>
internal sealed class CliServiceTray : IDisposable
{
    private readonly NotifyIcon _notifyIcon;
    private readonly ContextMenuStrip _contextMenu;
    private readonly ToolStripMenuItem _sessionsMenu;
    private readonly SessionManager _sessionManager;
    private readonly Action _requestShutdown;
    private readonly System.Windows.Forms.Timer _refreshTimer;
    private readonly TaskbarNotificationWindow _taskbarWindow;
    private bool _disposed;
    private DateTime _lastBalloonShown = DateTime.MinValue;

    public CliServiceTray(SessionManager sessionManager, Action requestShutdown)
    {
        _sessionManager = sessionManager;
        _requestShutdown = requestShutdown;

        _contextMenu = new ContextMenuStrip();

        // Sessions submenu (Alt+S mnemonic)
        _sessionsMenu = new ToolStripMenuItem("&Sessions (0)");
        _sessionsMenu.AccessibleDescription = "Lists active Excel sessions";
        _sessionsMenu.DropDownItems.Add(new ToolStripMenuItem("No active sessions") { Enabled = false });
        _contextMenu.Items.Add(_sessionsMenu);

        _contextMenu.Items.Add(new ToolStripSeparator());

        // About (Alt+A mnemonic)
        var aboutItem = new ToolStripMenuItem("&About...");
        aboutItem.AccessibleDescription = "Show version and project information";
        aboutItem.Click += (_, _) => ShowAbout();
        _contextMenu.Items.Add(aboutItem);

        _contextMenu.Items.Add(new ToolStripSeparator());

        // Exit (Alt+X mnemonic)
        var exitItem = new ToolStripMenuItem("E&xit");
        exitItem.AccessibleDescription = "Stop the ExcelMCP CLI service and exit";
        exitItem.Click += (_, _) => ExitService();
        _contextMenu.Items.Add(exitItem);

        // Load icon
        var icon = LoadEmbeddedIcon();

        _notifyIcon = new NotifyIcon
        {
            Icon = icon,
            Text = "ExcelMCP CLI Service",
            ContextMenuStrip = _contextMenu,
            Visible = true
        };

        _notifyIcon.DoubleClick += (_, _) => ShowSessions();

        // Refresh timer
        _refreshTimer = new System.Windows.Forms.Timer { Interval = 2000 };
        _refreshTimer.Tick += (_, _) => RefreshSessionsMenu();
        _refreshTimer.Start();

        // Listen for explorer.exe restarts so we can re-register the tray icon
        _taskbarWindow = new TaskbarNotificationWindow(_notifyIcon);

        RefreshSessionsMenu();

        // Check for updates after a short delay so the UI is responsive at startup
        CheckForUpdateAsync();
    }

    private static Icon LoadEmbeddedIcon()
    {
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "Sbroenne.ExcelMcp.CLI.Resources.excelcli.ico";

        using var stream = assembly.GetManifestResourceStream(resourceName);
        if (stream != null)
        {
            return new Icon(stream);
        }

        return SystemIcons.Application;
    }

    /// <summary>
    /// Checks NuGet for a newer version after a 5-second delay and shows a balloon tip if available.
    /// </summary>
    private async void CheckForUpdateAsync()
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(5));

            if (_disposed) return;

            var currentVersion = GetCurrentVersion();
            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
            var latestVersion = await NuGetVersionChecker.GetLatestVersionAsync(cts.Token);

            if (_disposed || latestVersion == null) return;

            if (CompareVersions(currentVersion, latestVersion) < 0)
            {
                ShowBalloon(
                    "Update Available",
                    $"ExcelMCP CLI {latestVersion} is available (current: {currentVersion}).\n" +
                    "Download: github.com/sbroenne/mcp-server-excel/releases/latest");
            }
        }
        catch
        {
            // Version check should never crash the service
        }
    }

    private void RefreshSessionsMenu()
    {
        if (_disposed) return;

        try
        {
            var sessions = _sessionManager.GetActiveSessions();

            if (_contextMenu.InvokeRequired)
            {
                _contextMenu.Invoke(RefreshSessionsMenu);
                return;
            }

            _sessionsMenu.Text = $"&Sessions ({sessions.Count})";
            _sessionsMenu.DropDownItems.Clear();

            if (sessions.Count == 0)
            {
                _sessionsMenu.DropDownItems.Add(new ToolStripMenuItem("No active sessions") { Enabled = false });
            }
            else
            {
                foreach (var session in sessions)
                {
                    var fileName = Path.GetFileName(session.FilePath);
                    var sessionMenu = new ToolStripMenuItem(fileName);
                    sessionMenu.AccessibleName = $"Session: {fileName}";
                    sessionMenu.AccessibleDescription = $"Excel session for {session.FilePath}";
                    sessionMenu.ToolTipText = $"Session: {session.SessionId}\nPath: {session.FilePath}";

                    // Close session with save prompt
                    var closeItem = new ToolStripMenuItem("&Close Session...");
                    closeItem.AccessibleDescription = $"Close {fileName} with option to save";
                    closeItem.Click += (_, _) => PromptCloseSession(session.SessionId, fileName);
                    sessionMenu.DropDownItems.Add(closeItem);

                    _sessionsMenu.DropDownItems.Add(sessionMenu);
                }

                _sessionsMenu.DropDownItems.Add(new ToolStripSeparator());

                var closeAllItem = new ToolStripMenuItem("Close &All Sessions");
                closeAllItem.AccessibleDescription = "Close all active sessions without saving";
                closeAllItem.Click += (_, _) => CloseAllSessions();
                _sessionsMenu.DropDownItems.Add(closeAllItem);
            }

            _notifyIcon.Text = sessions.Count > 0
                ? $"ExcelMCP CLI - {sessions.Count} session(s)"
                : "ExcelMCP CLI Service";
        }
        catch (Exception)
        {
            // UI refresh errors should not crash the service
        }
    }

    private void PromptCloseSession(string sessionId, string fileName)
    {
        var result = MessageBox.Show(
            $"Do you want to save changes to '{fileName}' before closing?",
            "Close Session",
            MessageBoxButtons.YesNoCancel,
            MessageBoxIcon.Question);

        if (result == System.Windows.Forms.DialogResult.Cancel)
            return;

        CloseSession(sessionId, save: result == System.Windows.Forms.DialogResult.Yes);
    }

    private void CloseSession(string sessionId, bool save)
    {
        try
        {
            _sessionManager.CloseSession(sessionId, save: save);
            RefreshSessionsMenu();
            ShowBalloon("Session Closed",
                save ? "Session saved and closed." : "Session closed without saving.");
        }
        catch (Exception ex)
        {
            ShowBalloon("Error", $"Failed to close session: {ex.Message}", ToolTipIcon.Error);
        }
    }

    private void CloseAllSessions()
    {
        try
        {
            var sessions = _sessionManager.GetActiveSessions().ToList();
            foreach (var session in sessions)
            {
                _sessionManager.CloseSession(session.SessionId, save: false);
            }
            RefreshSessionsMenu();
            ShowBalloon("Sessions Closed", $"Closed {sessions.Count} session(s).");
        }
        catch (Exception ex)
        {
            ShowBalloon("Error", $"Failed to close sessions: {ex.Message}", ToolTipIcon.Error);
        }
    }

    private void ShowSessions()
    {
        // Debounce: prevent duplicate balloons when clicking on/near balloon tips
        if ((DateTime.Now - _lastBalloonShown).TotalSeconds < 2)
            return;

        var sessions = _sessionManager.GetActiveSessions();
        if (sessions.Count == 0)
        {
            ShowBalloon("ExcelMCP CLI Service", "No active sessions.");
        }
        else
        {
            var message = string.Join("\n", sessions.Select(s => $"• {Path.GetFileName(s.FilePath)}"));
            ShowBalloon($"Active Sessions ({sessions.Count})", message);
        }
    }

    private void ShowBalloon(string title, string message, ToolTipIcon icon = ToolTipIcon.Info)
    {
        _lastBalloonShown = DateTime.Now;
        _notifyIcon.ShowBalloonTip(3000, title, message, icon);
    }

    private static async void ShowAbout()
    {
        var version = GetCurrentVersion();

        string? latestVersion = null;
        try
        {
            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
            latestVersion = await NuGetVersionChecker.GetLatestVersionAsync(cts.Token);
        }
        catch
        {
            // Version check failed — show dialog without update info
        }

        var updateAvailable = latestVersion != null && CompareVersions(version, latestVersion) < 0;

        using var form = new Form
        {
            Text = "About ExcelMCP CLI",
            Size = new Size(420, updateAvailable ? 300 : 260),
            FormBorderStyle = FormBorderStyle.FixedDialog,
            StartPosition = FormStartPosition.CenterScreen,
            MaximizeBox = false,
            MinimizeBox = false,
            ShowInTaskbar = false,
            KeyPreview = true
        };

        // Allow Escape to close the dialog
        form.KeyDown += (_, e) =>
        {
            if (e.KeyCode == Keys.Escape) form.Close();
        };

        var iconBox = new PictureBox
        {
            Image = SystemIcons.Information.ToBitmap(),
            SizeMode = PictureBoxSizeMode.AutoSize,
            Location = new Point(20, 20),
            AccessibleName = "ExcelMCP CLI icon",
            AccessibleRole = AccessibleRole.Graphic,
            TabStop = false
        };

        var nameLabel = new Label
        {
            Text = "ExcelMCP CLI Service",
            Font = new Font(Control.DefaultFont.FontFamily, 10, FontStyle.Bold),
            AutoSize = true,
            Location = new Point(70, 20),
            AccessibleName = "ExcelMCP CLI Service",
            AccessibleRole = AccessibleRole.StaticText
        };

        var versionLabel = new Label
        {
            Text = $"Version: {version}",
            AutoSize = true,
            Location = new Point(70, 45),
            AccessibleName = $"Version {version}",
            AccessibleRole = AccessibleRole.StaticText
        };

        var descLabel = new Label
        {
            Text = "Excel automation for coding agents.",
            AutoSize = true,
            Location = new Point(70, 75),
            AccessibleName = "Excel automation for coding agents",
            AccessibleRole = AccessibleRole.StaticText
        };

        const string githubUrl = "https://github.com/sbroenne/mcp-server-excel";
        const string docsUrl = "https://excelmcpserver.dev/";

        var githubLabel = new Label
        {
            Text = "GitHub:",
            AutoSize = true,
            Location = new Point(70, 105),
            AccessibleRole = AccessibleRole.StaticText
        };
        var githubLink = new LinkLabel
        {
            Text = githubUrl,
            AutoSize = true,
            Location = new Point(125, 105),
            TabIndex = 0,
            AccessibleName = "GitHub repository link",
            AccessibleDescription = $"Opens {githubUrl} in browser"
        };
        githubLink.Click += (_, _) =>
        {
            try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(githubUrl) { UseShellExecute = true }); }
            catch { /* Ignore navigation errors */ }
        };

        var docsLabel = new Label
        {
            Text = "Docs:",
            AutoSize = true,
            Location = new Point(70, 130),
            AccessibleRole = AccessibleRole.StaticText
        };
        var docsLink = new LinkLabel
        {
            Text = docsUrl,
            AutoSize = true,
            Location = new Point(125, 130),
            TabIndex = 1,
            AccessibleName = "Documentation link",
            AccessibleDescription = $"Opens {docsUrl} in browser"
        };
        docsLink.Click += (_, _) =>
        {
            try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(docsUrl) { UseShellExecute = true }); }
            catch { /* Ignore navigation errors */ }
        };

        var tabIndex = 2;
        var buttonY = 165;
        form.Controls.AddRange([iconBox, nameLabel, versionLabel, descLabel, githubLabel, githubLink, docsLabel, docsLink]);

        if (updateAvailable)
        {
            var updateLabel = new Label
            {
                Text = $"Update available: {version} \u2192 {latestVersion}",
                ForeColor = SystemColors.HotTrack,
                Font = new Font(Control.DefaultFont, FontStyle.Bold),
                AutoSize = true,
                Location = new Point(70, 160),
                AccessibleName = $"Update available from version {version} to {latestVersion}",
                AccessibleRole = AccessibleRole.StaticText
            };

            var updateCmd = new TextBox
            {
                Text = "https://github.com/sbroenne/mcp-server-excel/releases/latest",
                ReadOnly = true,
                BorderStyle = BorderStyle.None,
                BackColor = form.BackColor,
                Location = new Point(70, 180),
                Size = new Size(320, 20),
                TabIndex = tabIndex++,
                AccessibleName = "Download URL, select to copy",
                AccessibleDescription = "Download the latest release from this URL"
            };

            form.Controls.AddRange([updateLabel, updateCmd]);
            buttonY = 210;
        }

        var okButton = new Button
        {
            Text = "&OK",
            DialogResult = System.Windows.Forms.DialogResult.OK,
            Size = new Size(80, 28),
            Location = new Point(160, buttonY),
            TabIndex = tabIndex,
            AccessibleName = "OK, close dialog"
        };
        form.AcceptButton = okButton;
        form.Controls.Add(okButton);

        form.ShowDialog();
    }

    private static int CompareVersions(string current, string latest)
    {
        if (Version.TryParse(current, out var currentVer) && Version.TryParse(latest, out var latestVer))
            return currentVer.CompareTo(latestVer);
        return string.Compare(current, latest, StringComparison.Ordinal);
    }

    private static string GetCurrentVersion()
    {
        var assembly = Assembly.GetExecutingAssembly();
        var informational = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
        return informational?.Split('+')[0] ?? assembly.GetName().Version?.ToString() ?? "0.0.0";
    }

    private void ExitService()
    {
        var sessions = _sessionManager.GetActiveSessions();
        if (sessions.Count > 0)
        {
            var result = MessageBox.Show(
                $"There are {sessions.Count} active session(s).\n\n" +
                "Do you want to save all sessions before exiting?",
                "Exit ExcelMCP CLI",
                MessageBoxButtons.YesNoCancel,
                MessageBoxIcon.Question);

            if (result == System.Windows.Forms.DialogResult.Cancel)
                return;

            if (result == System.Windows.Forms.DialogResult.Yes)
            {
                try
                {
                    foreach (var session in sessions)
                    {
                        _sessionManager.CloseSession(session.SessionId, save: true);
                    }
                    ShowBalloon("Sessions Saved", $"Saved and closed {sessions.Count} session(s).");
                }
                catch (Exception ex)
                {
                    var continueResult = MessageBox.Show(
                        $"Error saving sessions: {ex.Message}\n\nExit anyway?",
                        "Error",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Error);

                    if (continueResult != System.Windows.Forms.DialogResult.Yes)
                        return;
                }
            }
            else
            {
                try
                {
                    foreach (var session in sessions)
                    {
                        _sessionManager.CloseSession(session.SessionId, save: false);
                    }
                }
                catch (Exception ex)
                {
                    ShowBalloon("Warning", $"Error closing sessions: {ex.Message}", ToolTipIcon.Warning);
                }
            }
        }

        _requestShutdown();
    }

    /// <summary>
    /// Hidden window that listens for the TaskbarCreated message broadcast by explorer.exe
    /// after it restarts, so the tray icon can be re-registered.
    /// </summary>
    private sealed class TaskbarNotificationWindow : NativeWindow
    {
        private readonly NotifyIcon _notifyIcon;
        private readonly uint _wmTaskbarCreated;

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern uint RegisterWindowMessage(string lpString);

        public TaskbarNotificationWindow(NotifyIcon notifyIcon)
        {
            _notifyIcon = notifyIcon;
            _wmTaskbarCreated = RegisterWindowMessage("TaskbarCreated");

            // Create a message-only window to receive broadcast messages
            CreateHandle(new CreateParams());
        }

        protected override void WndProc(ref Message m)
        {
            if (_wmTaskbarCreated != 0 && m.Msg == (int)_wmTaskbarCreated)
            {
                // Explorer restarted — re-register the tray icon
                _notifyIcon.Visible = false;
                _notifyIcon.Visible = true;
            }

            base.WndProc(ref m);
        }
    }

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;

        _refreshTimer.Stop();
        _refreshTimer.Dispose();
        _taskbarWindow.DestroyHandle();
        _notifyIcon.Visible = false;
        _notifyIcon.Dispose();
        _contextMenu.Dispose();
    }
}
