using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Timers;
using Decal.Adapter;
using Decal.Adapter.Wrappers;
using MosswartMassacre.Views;
using Mag.Shared.Constants;

namespace MosswartMassacre
{
    [FriendlyName("Mosswart Massacre")]
    public class PluginCore : PluginBase, IPluginLogger, IGameStats
    {
        // Hot Reload Support Properties
        private static string _assemblyDirectory = null;
        public static string AssemblyDirectory 
        {
            get 
            {
                if (_assemblyDirectory == null) 
                {
                    try 
                    {
                        _assemblyDirectory = System.IO.Path.GetDirectoryName(typeof(PluginCore).Assembly.Location);
                    }
                    catch 
                    {
                        _assemblyDirectory = Environment.CurrentDirectory;
                    }
                }
                return _assemblyDirectory;
            }
            set 
            {
                _assemblyDirectory = value;
            }
        }
        public static bool IsHotReload { get; set; }
        
        internal static PluginHost MyHost;
        // Static bridge properties for VVSTabbedMainView (reads from manager instances)
        private static InventoryMonitor _staticInventoryMonitor;
        internal static int cachedPrismaticCount => _staticInventoryMonitor?.CachedPrismaticCount ?? 0;
        private static KillTracker _staticKillTracker;
        internal static int totalKills => _staticKillTracker?.TotalKills ?? 0;
        internal static double killsPerHour => _staticKillTracker?.KillsPerHour ?? 0;
        internal static int sessionDeaths => _staticKillTracker?.SessionDeaths ?? 0;
        internal static int totalDeaths => _staticKillTracker?.TotalDeaths ?? 0;
        internal static DateTime statsStartTime => _staticKillTracker?.StatsStartTime ?? DateTime.Now;
        internal static DateTime lastKillTime => _staticKillTracker?.LastKillTime ?? DateTime.Now;

        // IGameStats explicit implementation (for WebSocket telemetry)
        int IGameStats.TotalKills => _staticKillTracker?.TotalKills ?? 0;
        double IGameStats.KillsPerHour => _staticKillTracker?.KillsPerHour ?? 0;
        int IGameStats.SessionDeaths => _staticKillTracker?.SessionDeaths ?? 0;
        int IGameStats.TotalDeaths => _staticKillTracker?.TotalDeaths ?? 0;
        int IGameStats.CachedPrismaticCount => _staticInventoryMonitor?.CachedPrismaticCount ?? 0;
        string IGameStats.CharTag => CharTag;
        DateTime IGameStats.StatsStartTime => _staticKillTracker?.StatsStartTime ?? DateTime.Now;

        private static Timer vitalsTimer;
        private static System.Windows.Forms.Timer commandTimer;
        private static Timer characterStatsTimer;
        private static Timer _updateCheckTimer;
        private static readonly Queue<string> pendingCommands = new Queue<string>();
        private static RareTracker _staticRareTracker;
        public static bool RareMetaEnabled
        {
            get => _staticRareTracker?.RareMetaEnabled ?? true;
            set { if (_staticRareTracker != null) _staticRareTracker.RareMetaEnabled = value; }
        }

        // VVS View Management
        private static class ViewManager
        {
            public static void ViewInit()
            {
                Views.VVSTabbedMainView.ViewInit();
            }

            public static void ViewDestroy()
            {
                Views.VVSTabbedMainView.ViewDestroy();
            }

            public static void UpdateKillStats(int totalKills, double killsPer5Min, double killsPerHour)
            {
                Views.VVSTabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
            }

            public static void UpdateElapsedTime(TimeSpan elapsed)
            {
                Views.VVSTabbedMainView.UpdateElapsedTime(elapsed);
            }

            public static void UpdateRareCount(int rareCount)
            {
                Views.VVSTabbedMainView.UpdateRareCount(rareCount);
            }

            public static void SetRareMetaToggleState(bool enabled)
            {
                Views.VVSTabbedMainView.SetRareMetaToggleState(enabled);
            }

            public static void RefreshSettingsFromConfig()
            {
                Views.VVSTabbedMainView.RefreshSettingsFromConfig();
            }

            public static void RestoreWindowPosition()
            {
                Views.VVSTabbedMainView.RestorePosition();
            }

            public static void RefreshUpdateStatus()
            {
                Views.VVSTabbedMainView.RefreshUpdateStatus();
            }
        }
        public static string CharTag { get; set; } = "";
        public static bool WebSocketEnabled { get; set; } = false;
        public bool InventoryLogEnabled { get; set; } = false;
        public static bool AggressiveChatStreamingEnabled { get; set; } = true;
        private MossyInventory _inventoryLogger;
        public static NavVisualization navVisualization;
        public static ChestLooter chestLooter;

        // Quest Management for always-on quest streaming
        public static QuestManager questManager;

        private static readonly Queue<string> _chatQueue = new Queue<string>();

        // Managers
        private KillTracker _killTracker;
        private RareTracker _rareTracker;
        private InventoryMonitor _inventoryMonitor;
        private ChatEventRouter _chatEventRouter;
        private GameEventRouter _gameEventRouter;
        private QuestStreamingService _questStreamingService;
        private CommandRouter _commandRouter;
        private LiveInventoryTracker _liveInventoryTracker;
        private EquipmentCantripStateTracker _equipmentCantripStateTracker;
        private NearbyObjectsTracker _nearbyObjectsTracker;
        private VitalSharingTracker _vitalSharingTracker;
        private CombatStatsTracker _combatStatsTracker;
        private static PluginCore _instance;
        public static VitalSharingTracker VitalSharingTracker => _instance?._vitalSharingTracker;

        public static void SetVitalSharingEnabled(bool enabled)
        {
            try
            {
                PluginSettings.Instance.VitalSharingEnabled = enabled;
                var tracker = _instance?._vitalSharingTracker;
                if (tracker != null)
                {
                    if (enabled)
                    {
                        tracker.Start();
                        WriteToChat("[VitalShare] ENABLED");
                    }
                    else
                    {
                        tracker.Stop();
                        WriteToChat("[VitalShare] DISABLED");
                    }
                }
                // Sync the checkbox on the Settings tab so chat commands
                // and the UI stay in lockstep.
                Views.VVSTabbedMainView.RefreshSettingsFromConfig();
            }
            catch (Exception ex)
            {
                WriteToChat($"[VitalShare] Toggle error: {ex.Message}");
            }
        }

        protected override void Startup()
        {
            try
            {
                _instance = this;
                // Set MyHost - for hot reload scenarios, Host might be null
                if (Host != null)
                {
                    MyHost = Host;
                }
                else if (MyHost == null)
                {
                    // Hot reload fallback - this is okay, WriteToChat will handle it
                    MyHost = null;
                }
                
                // Check if this is a hot reload (flag for post-init handling)
                var isCharacterLoaded = CoreManager.Current.CharacterFilter.LoginStatus == 3;
                var needsHotReload = IsHotReload || isCharacterLoaded;

                // Clean up old event subscriptions to prevent duplicates on hot reload.
                // C# -= with a non-subscribed handler is a no-op, so safe on first load.
                if (_chatEventRouter != null)
                    CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(_chatEventRouter.OnChatText);
                CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(ChatEventRouter.AllChatText);
                CoreManager.Current.CommandLineText -= OnChatCommand;
                CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
                CoreManager.Current.CharacterFilter.Death -= OnCharacterDeath;
                CoreManager.Current.WorldFilter.CreateObject -= OnSpawn;
                CoreManager.Current.WorldFilter.CreateObject -= OnPortalDetected;
                CoreManager.Current.WorldFilter.ReleaseObject -= OnDespawn;
                if (_inventoryMonitor != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _inventoryMonitor.OnInventoryCreate;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _inventoryMonitor.OnInventoryRelease;
                    CoreManager.Current.WorldFilter.ChangeObject -= _inventoryMonitor.OnInventoryChange;
                }
                if (_liveInventoryTracker != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _liveInventoryTracker.OnCreateObject;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _liveInventoryTracker.OnReleaseObject;
                    CoreManager.Current.WorldFilter.ChangeObject -= _liveInventoryTracker.OnChangeObject;
                }
                if (_equipmentCantripStateTracker != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _equipmentCantripStateTracker.OnCreateObject;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _equipmentCantripStateTracker.OnReleaseObject;
                    CoreManager.Current.WorldFilter.ChangeObject -= _equipmentCantripStateTracker.OnChangeObject;
                    CoreManager.Current.CharacterFilter.ChangeEnchantments -= _equipmentCantripStateTracker.OnChangeEnchantments;
                }
                if (_gameEventRouter != null)
                    CoreManager.Current.EchoFilter.ServerDispatch -= _gameEventRouter.OnServerDispatch;
                WebSocket.OnServerCommand -= HandleServerCommand;
                WebSocket.OnServerMessage -= HandleServerMessage;

                // Stop old timers before recreating (prevents timer leaks on hot reload)
                _killTracker?.Stop();
                if (vitalsTimer != null)
                {
                    vitalsTimer.Stop();
                    vitalsTimer.Dispose();
                    vitalsTimer = null;
                }
                if (commandTimer != null)
                {
                    commandTimer.Stop();
                    commandTimer.Dispose();
                    commandTimer = null;
                }
                if (_updateCheckTimer != null)
                {
                    _updateCheckTimer.Stop();
                    _updateCheckTimer.Dispose();
                    _updateCheckTimer = null;
                }

                // Initialize kill tracker (owns the 1-sec stats timer)
                _killTracker = new KillTracker(
                    this,
                    (kills, per5, perHr) => ViewManager.UpdateKillStats(kills, per5, perHr),
                    elapsed => ViewManager.UpdateElapsedTime(elapsed));
                _staticKillTracker = _killTracker;
                _killTracker.Start();

                // Initialize inventory monitor (taper tracking)
                _inventoryMonitor = new InventoryMonitor(this);
                _staticInventoryMonitor = _inventoryMonitor;

                // Initialize live inventory tracker (delta WebSocket messages)
                _liveInventoryTracker = new LiveInventoryTracker(this);
                _equipmentCantripStateTracker = new EquipmentCantripStateTracker(this);

                // Initialize chat event router (rareTracker set later in LoginComplete)
                _chatEventRouter = new ChatEventRouter(
                    this, _killTracker, null,
                    count => ViewManager.UpdateRareCount(count),
                    msg => MyHost?.Actions.InvokeChatParser($"/a {msg}"));

                // Initialize game event router
                _gameEventRouter = new GameEventRouter(this);

                // Note: Startup messages will appear after character login
                // Subscribe to events
                CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(_chatEventRouter.OnChatText);
                CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(ChatEventRouter.AllChatText);
                CoreManager.Current.CommandLineText += OnChatCommand;
                CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
                CoreManager.Current.CharacterFilter.Death += OnCharacterDeath;
                CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
                CoreManager.Current.WorldFilter.CreateObject += OnPortalDetected;
                CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
                CoreManager.Current.WorldFilter.CreateObject += _inventoryMonitor.OnInventoryCreate;
                CoreManager.Current.WorldFilter.ReleaseObject += _inventoryMonitor.OnInventoryRelease;
                CoreManager.Current.WorldFilter.ChangeObject += _inventoryMonitor.OnInventoryChange;
                CoreManager.Current.WorldFilter.CreateObject += _liveInventoryTracker.OnCreateObject;
                CoreManager.Current.WorldFilter.ReleaseObject += _liveInventoryTracker.OnReleaseObject;
                CoreManager.Current.WorldFilter.ChangeObject += _liveInventoryTracker.OnChangeObject;
                CoreManager.Current.WorldFilter.CreateObject += _equipmentCantripStateTracker.OnCreateObject;
                CoreManager.Current.WorldFilter.ReleaseObject += _equipmentCantripStateTracker.OnReleaseObject;
                CoreManager.Current.WorldFilter.ChangeObject += _equipmentCantripStateTracker.OnChangeObject;
                CoreManager.Current.CharacterFilter.ChangeEnchantments += _equipmentCantripStateTracker.OnChangeEnchantments;

                // Initialize VVS view after character login
                ViewManager.ViewInit();

                // Initialize vitals streaming timer
                vitalsTimer = new Timer(Constants.VitalsUpdateIntervalMs);
                vitalsTimer.Elapsed += SendVitalsUpdate;
                vitalsTimer.Start();

                // Initialize command processing timer (Windows Forms timer for main thread)
                commandTimer = new System.Windows.Forms.Timer();
                commandTimer.Interval = Constants.CommandProcessIntervalMs;
                commandTimer.Tick += ProcessPendingCommands;
                commandTimer.Start();

                // Note: View initialization moved to LoginComplete for VVS compatibility

                // Initialize character stats and hook ServerDispatch early
                // 0x0013 (character properties with luminance) fires DURING login,
                // BEFORE LoginComplete — must hook here to catch it
                CharacterStats.Init(this);
                CoreManager.Current.EchoFilter.ServerDispatch += _gameEventRouter.OnServerDispatch;

                // Enable TLS1.2
                ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
                //Enable vTank interface
                vTank.Enable();
                // Set logger for WebSocket
                WebSocket.SetLogger(this);
                WebSocket.SetGameStats(this);
                //lyssna på commands
                WebSocket.OnServerCommand += HandleServerCommand;
                WebSocket.OnServerMessage += HandleServerMessage;
                //starta inventory. Hanterar subscriptions  i den med

                _inventoryLogger = new MossyInventory();

                // Initialize navigation visualization system
                navVisualization = new NavVisualization();

                // Initialize nearby objects tracker (radar)
                _nearbyObjectsTracker = new NearbyObjectsTracker(this);

                // Initialize vital sharing tracker (cross-machine vital/debuff coordination)
                _vitalSharingTracker = new VitalSharingTracker(this);

                // Initialize combat stats tracker (Mag-Tools style combat parsing)
                try
                {
                    _combatStatsTracker = new CombatStatsTracker(this);
                }
                catch (Exception combatEx)
                {
                    WriteToChat($"[CombatStats] FATAL: Failed to create tracker: {combatEx.GetType().Name}: {combatEx.Message}");
                    if (combatEx.InnerException != null)
                        WriteToChat($"[CombatStats] Inner: {combatEx.InnerException.GetType().Name}: {combatEx.InnerException.Message}");
                }

                // Initialize command router
                _commandRouter = new CommandRouter();
                RegisterCommands();

                // Note: ChestLooter is initialized in LoginComplete after PluginSettings.Initialize()

                // Note: DECAL Harmony patches will be initialized in LoginComplete event
                // where the chat system is available for error messages

                // Hot reload: run after all core objects are initialized
                if (needsHotReload && isCharacterLoaded)
                {
                    try
                    {
                        WriteToChat("[INFO] Hot reload detected - reinitializing plugin");
                        InitializeForHotReload();
                        WriteToChat("[INFO] Hot reload initialization complete");
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"[ERROR] Hot reload initialization failed: {ex.Message}");
                    }
                }

                // Auto-update check is initialized after PluginSettings.Initialize() in LoginComplete

            }
            catch (Exception ex)
            {
                WriteToChat("Error during startup: " + ex.Message);
            }
        }

        protected override void Shutdown()
        {
            try
            {
                PluginSettings.Save();
                WriteToChat("Mosswart Massacre is shutting down. Bye!");



                // Unsubscribe from chat message event
                CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(_chatEventRouter.OnChatText);
                CoreManager.Current.CommandLineText -= OnChatCommand;
                CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(ChatEventRouter.AllChatText);
                CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
                CoreManager.Current.CharacterFilter.Death -= OnCharacterDeath;
                CoreManager.Current.WorldFilter.CreateObject -= OnSpawn;
                CoreManager.Current.WorldFilter.CreateObject -= OnPortalDetected;
                CoreManager.Current.WorldFilter.ReleaseObject -= OnDespawn;
                // Unsubscribe inventory monitor
                if (_inventoryMonitor != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _inventoryMonitor.OnInventoryCreate;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _inventoryMonitor.OnInventoryRelease;
                    CoreManager.Current.WorldFilter.ChangeObject -= _inventoryMonitor.OnInventoryChange;
                }
                // Unsubscribe from server dispatch
                CoreManager.Current.EchoFilter.ServerDispatch -= _gameEventRouter.OnServerDispatch;

                // Stop kill tracker
                _killTracker?.Stop();

                if (vitalsTimer != null)
                {
                    vitalsTimer.Stop();
                    vitalsTimer.Dispose();
                    vitalsTimer = null;
                }

                if (commandTimer != null)
                {
                    commandTimer.Stop();
                    commandTimer.Dispose();
                    commandTimer = null;
                }

                // Stop quest streaming service
                _questStreamingService?.Stop();
                _questStreamingService = null;

                // Stop and dispose character stats timer
                if (characterStatsTimer != null)
                {
                    characterStatsTimer.Stop();
                    characterStatsTimer.Elapsed -= OnCharacterStatsUpdate;
                    characterStatsTimer.Dispose();
                    characterStatsTimer = null;
                }

                if (_updateCheckTimer != null)
                {
                    _updateCheckTimer.Stop();
                    _updateCheckTimer.Dispose();
                    _updateCheckTimer = null;
                }

                // Dispose quest manager
                if (questManager != null)
                {
                    questManager.Dispose();
                    questManager = null;
                }

                // Stop radar tracker
                _nearbyObjectsTracker?.Dispose();
                _nearbyObjectsTracker = null;

                // Stop vital sharing tracker
                _vitalSharingTracker?.Dispose();
                _vitalSharingTracker = null;

                // Stop combat stats tracker
                _combatStatsTracker?.Dispose();
                _combatStatsTracker = null;

                // Clean up the view
                ViewManager.ViewDestroy();
                //Disable vtank interface
                vTank.Disable();
                // sluta lyssna på commands
                WebSocket.OnServerCommand -= HandleServerCommand;
                WebSocket.OnServerMessage -= HandleServerMessage;
                WebSocket.Stop();
                //shutdown inv
                _inventoryLogger.Dispose();

                // Clean up navigation visualization
                if (navVisualization != null)
                {
                    navVisualization.Dispose();
                    navVisualization = null;
                }

                // Clean up taper tracking
                _inventoryMonitor?.Cleanup();

                // Clean up live inventory tracker
                if (_liveInventoryTracker != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _liveInventoryTracker.OnCreateObject;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _liveInventoryTracker.OnReleaseObject;
                    CoreManager.Current.WorldFilter.ChangeObject -= _liveInventoryTracker.OnChangeObject;
                    _liveInventoryTracker.Cleanup();
                }
                if (_equipmentCantripStateTracker != null)
                {
                    CoreManager.Current.WorldFilter.CreateObject -= _equipmentCantripStateTracker.OnCreateObject;
                    CoreManager.Current.WorldFilter.ReleaseObject -= _equipmentCantripStateTracker.OnReleaseObject;
                    CoreManager.Current.WorldFilter.ChangeObject -= _equipmentCantripStateTracker.OnChangeObject;
                    CoreManager.Current.CharacterFilter.ChangeEnchantments -= _equipmentCantripStateTracker.OnChangeEnchantments;
                    _equipmentCantripStateTracker.Dispose();
                    _equipmentCantripStateTracker = null;
                }

                // Clean up Harmony patches
                DecalHarmonyClean.Cleanup();

                MyHost = null;
            }
            catch (Exception ex)
            {
                WriteToChat("Error during shutdown: " + ex.Message);
            }
        }
        private void CharacterFilter_LoginComplete(object sender, EventArgs e)
        {
            CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;

            WriteToChat("Mosswart Massacre has started!");



            PluginSettings.Initialize(); // Safe to call now

            // Initialize chest looter system (needs PluginSettings to be ready)
            try
            {
                chestLooter = new ChestLooter(CoreManager.Current, PluginSettings.Instance.ChestLooterSettings);
                chestLooter.Initialize();
                chestLooter.StatusChanged += (sender, status) =>
                {
                    VVSTabbedMainView.UpdateChestLooterStatus(status);
                };
            }
            catch (Exception ex)
            {
                WriteToChat($"[ChestLooter] Initialization failed: {ex.Message}");
            }

            // Initialize rare tracker and wire to chat router
            _rareTracker = new RareTracker(this);
            _staticRareTracker = _rareTracker;
            _chatEventRouter.SetRareTracker(_rareTracker);
            _chatEventRouter.SetCombatTracker(_combatStatsTracker);
            WriteToChat($"[CombatStats] tracker={(_combatStatsTracker != null ? "OK" : "NULL")}");

            // Apply the values
            _rareTracker.RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
            WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
            CharTag = PluginSettings.Instance.CharTag;
            ViewManager.SetRareMetaToggleState(RareMetaEnabled);
            ViewManager.RefreshSettingsFromConfig(); // Refresh all UI settings after loading
            if (WebSocketEnabled)
                WebSocket.Start();

            // Auto-start vital sharing if enabled in settings
            if (PluginSettings.Instance.VitalSharingEnabled && _vitalSharingTracker != null)
            {
                _vitalSharingTracker.Start();
                WriteToChat("[VitalShare] Enabled at login");
            }

            // Start combat stats tracker (always on — mirrors Mag-Tools behavior)
            _combatStatsTracker?.RestartSession();
            _combatStatsTracker?.Start();

            if (PluginSettings.Instance.AutoUpdateEnabled)
            {
                _updateCheckTimer = new Timer(30000);
                _updateCheckTimer.AutoReset = false;
                _updateCheckTimer.Elapsed += (s, ev) =>
                {
                    Task.Run(() => UpdateManager.CheckAndInstallAsync());
                    _updateCheckTimer?.Dispose();
                    _updateCheckTimer = null;
                };
                _updateCheckTimer.Start();
            }
            else
            {
                WriteToChat("[Update] Auto updates disabled in settings");
            }

            // Initialize Harmony patches using UtilityBelt's loaded DLL
            try
            {
                bool success = DecalHarmonyClean.Initialize();
                if (success)
                    WriteToChat("[OK] Plugin message interception active");
                else
                    WriteToChat("[FAIL] Could not initialize message interception");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Harmony initialization failed: {ex.Message}");
            }

            // Initialize death tracking
            _killTracker.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));

            // Initialize cached Prismatic Taper count
            _inventoryMonitor.Initialize();

            // Initialize live inventory tracking (after full inventory dump)
            _liveInventoryTracker?.Initialize();
            _equipmentCantripStateTracker?.Initialize();

            // Initialize quest manager for always-on quest streaming
            try
            {
                questManager = new QuestManager();
                
                // Trigger full quest data refresh (same as clicking refresh button)
                Views.FlagTrackerView.RefreshQuestData();
                
                // Initialize quest streaming service (30 seconds)
                _questStreamingService = new QuestStreamingService(this);
                _questStreamingService.Start();

                WriteToChat("[OK] Quest streaming initialized with full data refresh");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Quest streaming initialization failed: {ex.Message}");
            }

            // Start character stats streaming
            // Note: Init() and ServerDispatch hook are in Startup() so we catch
            // 0x0013 (luminance/properties) which fires BEFORE LoginComplete
            try
            {
                // Start 10-minute character stats timer
                characterStatsTimer = new Timer(Constants.CharacterStatsIntervalMs);
                characterStatsTimer.Elapsed += OnCharacterStatsUpdate;
                characterStatsTimer.AutoReset = true;
                characterStatsTimer.Start();

                // Send initial stats after 5-second delay (let CharacterFilter populate)
                var initialDelay = new Timer(Constants.LoginDelayMs);
                initialDelay.AutoReset = false;
                initialDelay.Elapsed += (s, args) =>
                {
                    CharacterStats.CollectAndSend();
                    ((Timer)s).Dispose();
                };
                initialDelay.Start();

                WriteToChat("[OK] Character stats streaming initialized (10-min interval)");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Character stats initialization failed: {ex.Message}");
            }

        }


        private void InitializeForHotReload()
        {
            // This method handles initialization that depends on character being logged in
            // Similar to LoginComplete but designed for hot reload scenarios
            
            WriteToChat("Mosswart Massacre hot reload initialization started!");

            // 1. Initialize settings - CRITICAL first step
            PluginSettings.Initialize();

            // 1b. Initialize chest looter system (needs PluginSettings to be ready)
            try
            {
                chestLooter = new ChestLooter(CoreManager.Current, PluginSettings.Instance.ChestLooterSettings);
                chestLooter.Initialize();
                chestLooter.StatusChanged += (sender, status) =>
                {
                    VVSTabbedMainView.UpdateChestLooterStatus(status);
                };
            }
            catch (Exception ex)
            {
                WriteToChat($"[ChestLooter] Initialization failed: {ex.Message}");
            }

            // 2. Initialize rare tracker if not already set (missed when LoginComplete doesn't fire)
            if (_rareTracker == null)
            {
                _rareTracker = new RareTracker(this);
                _staticRareTracker = _rareTracker;
                _chatEventRouter.SetRareTracker(_rareTracker);
            }

            // Apply the values from settings
            _rareTracker.RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
            WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
            CharTag = PluginSettings.Instance.CharTag;

            // 3. Update UI with current settings and restore window position
            ViewManager.SetRareMetaToggleState(RareMetaEnabled);
            ViewManager.RefreshSettingsFromConfig();
            ViewManager.RestoreWindowPosition();

            // 4. Restart services if they were enabled (stop first, then start)
            if (WebSocketEnabled)
            {
                WebSocket.Stop();   // Stop existing
                WebSocket.Start();  // Restart
            }

            // 5. Initialize Harmony patches (only if not already done)
            // Note: Harmony patches are global and don't need reinitialization
            if (!DecalHarmonyClean.IsActive())
            {
                try
                {
                    bool success = DecalHarmonyClean.Initialize();
                    if (success)
                        WriteToChat("[OK] Plugin message interception active");
                    else
                        WriteToChat("[FAIL] Could not initialize message interception");
                }
                catch (Exception ex)
                {
                    WriteToChat($"[ERROR] Harmony initialization failed: {ex.Message}");
                }
            }

            // 6. Reinitialize death tracking
            _killTracker?.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));

            // 7. Reinitialize cached Prismatic Taper count
            _inventoryMonitor?.Initialize();

            // 7b. Reinitialize live inventory tracking
            _liveInventoryTracker?.Initialize();
            _equipmentCantripStateTracker?.Initialize();

            // 8. Reinitialize quest manager for hot reload
            try
            {
                if (questManager == null)
                {
                    questManager = new QuestManager();
                    WriteToChat("[OK] Quest manager reinitialized");
                }
                else
                {
                    WriteToChat("[INFO] Quest manager already active");
                }
                
                // Trigger full quest data refresh (same as clicking refresh button)
                Views.FlagTrackerView.RefreshQuestData();
                WriteToChat("[INFO] Quest data refresh triggered for hot reload");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Quest manager hot reload failed: {ex.Message}");
            }

            // 9. Reinitialize quest streaming service for hot reload
            try
            {
                _questStreamingService?.Stop();
                _questStreamingService = new QuestStreamingService(this);
                _questStreamingService.Start();

                WriteToChat("[OK] Quest streaming service reinitialized (30s interval)");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Quest streaming service hot reload failed: {ex.Message}");
            }

            // 10. Reinitialize combat stats tracker
            try
            {
                _chatEventRouter.SetCombatTracker(_combatStatsTracker);
                _combatStatsTracker?.RestartSession();
                _combatStatsTracker?.Start();
                WriteToChat($"[OK] Combat stats tracker initialized (tracker={(_combatStatsTracker != null ? "OK" : "NULL")})");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Combat stats hot reload failed: {ex.Message}");
            }

            // 10b. Reinitialize vital sharing if enabled
            if (PluginSettings.Instance.VitalSharingEnabled && _vitalSharingTracker != null)
            {
                _vitalSharingTracker.Start();
                WriteToChat("[OK] Vital sharing re-enabled after hot reload");
            }

            // 11. Reinitialize character stats streaming
            try
            {
                if (characterStatsTimer == null)
                {
                    characterStatsTimer = new Timer(Constants.CharacterStatsIntervalMs);
                    characterStatsTimer.Elapsed += OnCharacterStatsUpdate;
                    characterStatsTimer.AutoReset = true;
                    characterStatsTimer.Start();
                }

                // Send initial stats after short delay
                var initialDelay = new Timer(Constants.LoginDelayMs);
                initialDelay.AutoReset = false;
                initialDelay.Elapsed += (s, args) =>
                {
                    CharacterStats.CollectAndSend();
                    ((Timer)s).Dispose();
                };
                initialDelay.Start();

                WriteToChat("[OK] Character stats streaming initialized");
            }
            catch (Exception ex)
            {
                WriteToChat($"[ERROR] Character stats hot reload failed: {ex.Message}");
            }

            WriteToChat("Hot reload initialization completed!");
        }


        private async void OnSpawn(object sender, CreateObjectEventArgs e)
        {
            var mob = e.New;
            if (mob.ObjectClass != ObjectClass.Monster) return;

            try
            {
                // Get DECAL coordinates
                var decalCoords = mob.Coordinates();
                if (decalCoords == null) return;
                
                const string fmt = "F7";
                string ns = decalCoords.NorthSouth.ToString(fmt, CultureInfo.InvariantCulture);
                string ew = decalCoords.EastWest.ToString(fmt, CultureInfo.InvariantCulture);

                // Get Z coordinate using RawCoordinates() for accurate world Z position
                string zCoord = "0";
                try
                {
                    var rawCoords = mob.RawCoordinates();
                    if (rawCoords != null)
                    {
                        zCoord = rawCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                    }
                    else
                    {
                        // Fallback to player Z approximation if RawCoordinates fails
                        var playerCoords = Coordinates.Me;
                        if (Math.Abs(playerCoords.Z) > 0.1)
                        {
                            zCoord = playerCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                        }
                    }
                }
                catch
                {
                    // Fallback to player Z approximation on error
                    try
                    {
                        var playerCoords = Coordinates.Me;
                        if (Math.Abs(playerCoords.Z) > 0.1)
                        {
                            zCoord = playerCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                        }
                    }
                    catch
                    {
                        zCoord = "0";
                    }
                }

                await WebSocket.SendSpawnAsync(ns, ew, zCoord, mob.Name);
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[WS] Spawn send failed: {ex}");
            }
        }

        private async void OnPortalDetected(object sender, CreateObjectEventArgs e)
        {
            var portal = e.New;
            if (portal.ObjectClass != ObjectClass.Portal) return;

            try
            {
                // Get portal coordinates from DECAL
                var decalCoords = portal.Coordinates();
                if (decalCoords == null) return;

                // Only report portals within 240 meters of the player
                var portalPos = new Coordinates(decalCoords.EastWest, decalCoords.NorthSouth);
                var playerPos = Coordinates.Me;
                double dist = playerPos.DistanceToFlat(portalPos);
                if (dist > 240.0) return;

                const string fmt = "F7";
                string ns = decalCoords.NorthSouth.ToString(fmt, CultureInfo.InvariantCulture);
                string ew = decalCoords.EastWest.ToString(fmt, CultureInfo.InvariantCulture);

                // Get Z coordinate using RawCoordinates() for accurate world Z position
                string zCoord = "0";
                try
                {
                    var rawCoords = portal.RawCoordinates();
                    if (rawCoords != null)
                    {
                        zCoord = rawCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                    }
                    else
                    {
                        // Fallback to player Z approximation if RawCoordinates fails
                        var playerCoords = Coordinates.Me;
                        if (Math.Abs(playerCoords.Z) > 0.1)
                        {
                            zCoord = playerCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                        }
                    }
                }
                catch
                {
                    // Fallback to player Z approximation on error
                    try
                    {
                        var playerCoords = Coordinates.Me;
                        if (Math.Abs(playerCoords.Z) > 0.1)
                        {
                            zCoord = playerCoords.Z.ToString("F2", CultureInfo.InvariantCulture);
                        }
                    }
                    catch
                    {
                        zCoord = "0";
                    }
                }

                await WebSocket.SendPortalAsync(ns, ew, zCoord, portal.Name);
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[PORTAL ERROR] {ex.Message}");
                PluginCore.WriteToChat($"[WS] Portal send failed: {ex}");
            }
        }

 
        private void OnDespawn(object sender, ReleaseObjectEventArgs e)
        {
            var mob = e.Released;
            if (mob.ObjectClass != ObjectClass.Monster) return;

        
            // var c = mob.Coordinates();
            // PluginCore.WriteToChat(
            //     $"[Despawn] {mob.Name} @ (NS={c.NorthSouth:F1}, EW={c.EastWest:F1})");
        }


        private void OnCharacterDeath(object sender, Decal.Adapter.Wrappers.DeathEventArgs e)
        {
            _killTracker.OnDeath();
            _killTracker.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
        }

        private void HandleServerCommand(CommandEnvelope env)
        {
            // This is called from WebSocket thread - queue for main thread execution
            lock (pendingCommands)
            {
                pendingCommands.Enqueue(env.Command);
            }
        }

        private void HandleServerMessage(string rawJson)
        {
            // This is called from WebSocket thread.
            // VitalSharingTracker.HandleIncomingShareMessage uses its own lock and
            // defers actual VTank API calls to the RenderFrame drain.
            try
            {
                _vitalSharingTracker?.HandleIncomingShareMessage(rawJson);
            }
            catch (Exception ex)
            {
                WriteToChat($"[VitalShare] HandleServerMessage error: {ex.Message}");
            }
        }

        private void ProcessPendingCommands(object sender, EventArgs e)
        {
            // This runs on the main UI thread via Windows Forms timer
            string command = null;
            
            lock (pendingCommands)
            {
                if (pendingCommands.Count > 0)
                    command = pendingCommands.Dequeue();
            }
            
            if (command != null)
            {
                try
                {
                    // Handle radar commands directly (don't send to chat box)
                    if (command == "start_radar")
                    {
                        _nearbyObjectsTracker?.Start();
                        return;
                    }
                    if (command == "stop_radar")
                    {
                        _nearbyObjectsTracker?.Stop();
                        return;
                    }

                    // Execute ALL WebSocket commands on main thread - fast and reliable
                    DispatchChatToBoxWithPluginIntercept(command);
                }
                catch (Exception ex)
                {
                    WriteToChat($"[WS] Command execution error: {ex.Message}");
                }
            }
        }



        private void OnChatCommand(object sender, ChatParserInterceptEventArgs e)
        {
            try
            {
                if (e.Text.StartsWith("/mm", StringComparison.OrdinalIgnoreCase))
                {
                    e.Eat = true; // Prevent the message from showing in chat
                    HandleMmCommand(e.Text);
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[Error] Failed to process /mm command: {ex.Message}");
            }
        }


        private static void SendVitalsUpdate(object sender, ElapsedEventArgs e)
        {
            try
            {
                // Only send if WebSocket is enabled
                if (!WebSocketEnabled)
                    return;

                // Collect vitals data
                int currentHealth = CoreManager.Current.Actions.Vital[VitalType.CurrentHealth];
                int currentStamina = CoreManager.Current.Actions.Vital[VitalType.CurrentStamina];
                int currentMana = CoreManager.Current.Actions.Vital[VitalType.CurrentMana];
                
                int maxHealth = CoreManager.Current.Actions.Vital[VitalType.MaximumHealth];
                int maxStamina = CoreManager.Current.Actions.Vital[VitalType.MaximumStamina];
                int maxMana = CoreManager.Current.Actions.Vital[VitalType.MaximumMana];
                
                int vitae = CoreManager.Current.CharacterFilter.Vitae;

                // Create vitals data structure
                var vitalsData = new
                {
                    type = "vitals",
                    timestamp = DateTime.UtcNow.ToString("o"),
                    character_name = CoreManager.Current.CharacterFilter.Name,
                    health_current = currentHealth,
                    health_max = maxHealth,
                    health_percentage = maxHealth > 0 ? Math.Round((double)currentHealth / maxHealth * 100, 1) : 0,
                    stamina_current = currentStamina,
                    stamina_max = maxStamina,
                    stamina_percentage = maxStamina > 0 ? Math.Round((double)currentStamina / maxStamina * 100, 1) : 0,
                    mana_current = currentMana,
                    mana_max = maxMana,
                    mana_percentage = maxMana > 0 ? Math.Round((double)currentMana / maxMana * 100, 1) : 0,
                    vitae = vitae
                };

                // Send via WebSocket
                _ = WebSocket.SendVitalsAsync(vitalsData);
            }
            catch (Exception ex)
            {
                WriteToChat($"Error sending vitals: {ex.Message}");
            }
        }

        private static void OnCharacterStatsUpdate(object sender, ElapsedEventArgs e)
        {
            try
            {
                CharacterStats.CollectAndSend();
            }
            catch (Exception ex)
            {
                WriteToChat($"[CharStats] Timer error: {ex.Message}");
            }
        }


        public static void WriteToChat(string message)
        {
            try
            {
                // For hot reload scenarios where MyHost might be null, use CoreManager directly
                if (MyHost != null)
                {
                    MyHost.Actions.AddChatText("[Mosswart Massacre] " + message, 0, 1);
                }
                else
                {
                    // Hot reload fallback1 - use CoreManager directly like the original template
                    CoreManager.Current.Actions.AddChatText("[Mosswart Massacre] " + message, 1);
                }
            }
            catch (Exception ex)
            {
                // Last resort fallback - try CoreManager even if MyHost was supposed to work
                try
                {
                    CoreManager.Current.Actions.AddChatText($"[Mosswart Massacre] {message} (WriteToChat error: {ex.Message})", 1);
                }
                catch
                {
                    // Give up - can't write to chat at all
                }
            }
        }

        void IPluginLogger.Log(string message) => WriteToChat(message);

        public static void RestartStats()
        {
            _staticKillTracker?.RestartStats();
            if (_staticRareTracker != null)
                _staticRareTracker.RareCount = 0;
            ViewManager.UpdateRareCount(0);
        }
        public static void ToggleRareMeta()
        {
            _staticRareTracker?.ToggleRareMeta();
            ViewManager.SetRareMetaToggleState(RareMetaEnabled);
        }

        [DllImport("Decal.dll")]
        private static extern int DispatchOnChatCommand(ref IntPtr str, [MarshalAs(UnmanagedType.U4)] int target);

        public static bool Decal_DispatchOnChatCommand(string cmd)
        {
            IntPtr bstr = Marshal.StringToBSTR(cmd);

            try
            {
                bool eaten = (DispatchOnChatCommand(ref bstr, 1) & 0x1) > 0;
                return eaten;
            }
            finally
            {
                Marshal.FreeBSTR(bstr);
            }
        }
        public static void DispatchChatToBoxWithPluginIntercept(string cmd)
        {
            if (!Decal_DispatchOnChatCommand(cmd))
                CoreManager.Current.Actions.InvokeChatParser(cmd);
        }
        private void HandleMmCommand(string text)
        {
            _commandRouter.Dispatch(text);
        }

        private void RegisterCommands()
        {
            _commandRouter.Register("ws", args =>
            {
                if (args.Length > 1)
                {
                    if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
                    {
                        WebSocketEnabled = true;
                        WebSocket.Start();
                        PluginSettings.Instance.WebSocketEnabled = true;
                        WriteToChat("WS streaming ENABLED.");
                    }
                    else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
                    {
                        WebSocketEnabled = false;
                        WebSocket.Stop();
                        PluginSettings.Instance.WebSocketEnabled = false;
                        WriteToChat("WS streaming DISABLED.");
                    }
                    else
                    {
                        WriteToChat("Usage: /mm ws <enable|disable>");
                    }
                }
                else
                {
                    WriteToChat("Usage: /mm ws <enable|disable>");
                }
            }, "Enable/disable WebSocket streaming");

            _commandRouter.Register("vitalsharing", args =>
            {
                if (args.Length < 2)
                {
                    WriteToChat("Usage: /mm vitalsharing <on|off|status|overlay>");
                    return;
                }

                var sub = args[1].ToLowerInvariant();

                if (sub == "on")
                {
                    if (!WebSocketEnabled)
                    {
                        WriteToChat("[VitalShare] WebSocket must be enabled first. Run /mm ws enable");
                        return;
                    }
                    SetVitalSharingEnabled(true);
                }
                else if (sub == "off")
                {
                    SetVitalSharingEnabled(false);
                }
                else if (sub == "status")
                {
                    var enabled = PluginSettings.Instance.VitalSharingEnabled;
                    var active = _vitalSharingTracker?.IsActive == true;
                    var tag = PluginSettings.Instance.CharTag ?? "";
                    WriteToChat($"[VitalShare] enabled={enabled} active={active} tag='{tag}'");
                }
                else if (sub == "overlay")
                {
                    Views.VitalSharingOverlayView.ToggleWindow();
                }
                else
                {
                    WriteToChat("Usage: /mm vitalsharing <on|off|status|overlay>");
                }
            }, "Enable/disable vital sharing with other MosswartOverlord clients");

            _commandRouter.Register("report", args =>
            {
                TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime;
                string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}, Session Deaths: {_killTracker.SessionDeaths}, Total Deaths: {_killTracker.TotalDeaths}";
                WriteToChat(reportMessage);
            }, "Show kill/death/rare stats");

            _commandRouter.Register("getmetastate", args =>
            {
                string metaState = VtankControl.VtGetMetaState();
                WriteToChat(metaState);
            }, "Show current VTank meta state");

            _commandRouter.Register("loc", args =>
            {
                Coordinates here = Coordinates.Me;
                var pos = Utils.GetPlayerPosition();
                WriteToChat($"Location: {here}  (X={pos.X:F1}, Y={pos.Y:F1}, Z={pos.Z:F1})");
            }, "Show current location");

            _commandRouter.Register("reset", args =>
            {
                RestartStats();
            }, "Reset kill/rare counters");

            _commandRouter.Register("meta", args =>
            {
                RareMetaEnabled = !RareMetaEnabled;
                WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}");
                ViewManager.SetRareMetaToggleState(RareMetaEnabled);
            }, "Toggle rare meta state");

            _commandRouter.Register("nextwp", args =>
            {
                double result = VtankControl.VtAdvanceWaypoint();
                if (result == 1)
                    WriteToChat("Advanced VTank to next waypoint.");
                else
                    WriteToChat("Failed to advance VTank waypoint. Is VTank running?");
            }, "Advance VTank to next waypoint");

            _commandRouter.Register("setchest", args =>
            {
                if (args.Length < 2)
                {
                    WriteToChat("[ChestLooter] Usage: /mm setchest <chest name>");
                    return;
                }
                string chestName = string.Join(" ", args.Skip(1));
                if (chestLooter != null)
                {
                    chestLooter.SetChestName(chestName);
                    if (PluginSettings.Instance?.ChestLooterSettings != null)
                    {
                        PluginSettings.Instance.ChestLooterSettings.ChestName = chestName;
                        PluginSettings.Save();
                    }
                    Views.VVSTabbedMainView.RefreshChestLooterUI();
                }
            }, "Set chest name for looter");

            _commandRouter.Register("setkey", args =>
            {
                if (args.Length < 2)
                {
                    WriteToChat("[ChestLooter] Usage: /mm setkey <key name>");
                    return;
                }
                string keyName = string.Join(" ", args.Skip(1));
                if (chestLooter != null)
                {
                    chestLooter.SetKeyName(keyName);
                    if (PluginSettings.Instance?.ChestLooterSettings != null)
                    {
                        PluginSettings.Instance.ChestLooterSettings.KeyName = keyName;
                        PluginSettings.Save();
                    }
                    Views.VVSTabbedMainView.RefreshChestLooterUI();
                }
            }, "Set key name for looter");

            _commandRouter.Register("lootchest", args =>
            {
                if (chestLooter != null)
                {
                    if (!chestLooter.StartByName())
                        WriteToChat("[ChestLooter] Failed to start. Check chest/key names are set.");
                }
                else
                {
                    WriteToChat("[ChestLooter] Chest looter not initialized");
                }
            }, "Start chest looting");

            _commandRouter.Register("stoploot", args =>
            {
                if (chestLooter != null)
                    chestLooter.Stop();
                else
                    WriteToChat("[ChestLooter] Chest looter not initialized");
            }, "Stop chest looting");

            _commandRouter.Register("vtanktest", args =>
            {
                try
                {
                    WriteToChat("Testing VTank interface...");
                    WriteToChat($"VTank Instance: {(vTank.Instance != null ? "Found" : "NULL")}");
                    WriteToChat($"VTank Type: {vTank.Instance?.GetType()?.Name ?? "NULL"}");
                    WriteToChat($"NavCurrent: {vTank.Instance?.NavCurrent ?? -1}");
                    WriteToChat($"NavNumPoints: {vTank.Instance?.NavNumPoints ?? -1}");
                    WriteToChat($"NavType: {vTank.Instance?.NavType}");
                    WriteToChat($"MacroEnabled: {vTank.Instance?.MacroEnabled}");
                }
                catch (Exception ex)
                {
                    WriteToChat($"VTank test error: {ex.Message}");
                }
            }, "");

            _commandRouter.Register("decalstatus", args =>
            {
                try
                {
                    WriteToChat("=== Harmony Patch Status (UtilityBelt Pattern) ===");
                    WriteToChat($"Patches Active: {DecalHarmonyClean.IsActive()}");
                    WriteToChat($"Messages Intercepted: {DecalHarmonyClean.GetMessagesIntercepted()}");
                    WriteToChat($"WebSocket Streaming: {(AggressiveChatStreamingEnabled && WebSocketEnabled ? "ACTIVE" : "INACTIVE")}");

                    WriteToChat("=== Harmony Version Status ===");
                    try
                    {
                        var harmonyTest = Harmony.HarmonyInstance.Create("test.version.check");
                        WriteToChat($"[OK] Harmony Available (ID: {harmonyTest.Id})");
                        var harmonyAssembly = typeof(Harmony.HarmonyInstance).Assembly;
                        WriteToChat($"[OK] Harmony Version: {harmonyAssembly.GetName().Version}");
                        WriteToChat($"[OK] Harmony Location: {harmonyAssembly.Location}");
                    }
                    catch (Exception harmonyEx)
                    {
                        WriteToChat($"[FAIL] Harmony Test Failed: {harmonyEx.Message}");
                    }
                }
                catch (Exception ex)
                {
                    WriteToChat($"Status check error: {ex.Message}");
                }
            }, "");

            _commandRouter.Register("decaldebug", args =>
            {
                if (args.Length > 1)
                {
                    if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
                    {
                        AggressiveChatStreamingEnabled = true;
                        WriteToChat("[OK] DECAL debug streaming ENABLED - will show captured messages + stream via WebSocket");
                    }
                    else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
                    {
                        AggressiveChatStreamingEnabled = false;
                        WriteToChat("[FAIL] DECAL debug streaming DISABLED - WebSocket streaming also disabled");
                    }
                    else
                    {
                        WriteToChat("Usage: /mm decaldebug <enable|disable>");
                    }
                }
                else
                {
                    WriteToChat("Usage: /mm decaldebug <enable|disable>");
                }
            }, "");

            _commandRouter.Register("gui", args =>
            {
                try
                {
                    WriteToChat("Attempting to manually initialize GUI...");
                    ViewManager.ViewDestroy();
                    ViewManager.ViewInit();
                    WriteToChat("GUI initialization attempt completed.");
                }
                catch (Exception ex)
                {
                    WriteToChat($"GUI initialization error: {ex.Message}");
                }
            }, "Reinitialize GUI");

            _commandRouter.Register("testprismatic", args =>
            {
                try
                {
                    WriteToChat("=== FULL INVENTORY DUMP ===");
                    var worldFilter = CoreManager.Current.WorldFilter;
                    var playerInv = CoreManager.Current.CharacterFilter.Id;

                    WriteToChat("Listing ALL items in your main inventory:");
                    int itemNum = 1;

                    foreach (WorldObject item in worldFilter.GetByContainer(playerInv))
                    {
                        if (!string.IsNullOrEmpty(item.Name))
                        {
                            int stackCount = item.Values(LongValueKey.StackCount, 0);
                            WriteToChat($"{itemNum:D2}: '{item.Name}' (count: {stackCount}, icon: 0x{item.Icon:X}, class: {item.ObjectClass})");
                            itemNum++;

                            string nameLower = item.Name.ToLower();
                            if (nameLower.Contains("taper") || nameLower.Contains("prismatic") ||
                                nameLower.Contains("prism") || nameLower.Contains("component"))
                            {
                                WriteToChat($"    *** POSSIBLE MATCH: '{item.Name}' ***");
                            }
                        }
                    }

                    WriteToChat($"=== Total items listed: {itemNum - 1} ===");

                    WriteToChat("=== Testing Utility Functions on Prismatic Taper ===");
                    var foundItem = Utils.FindItemByName("Prismatic Taper");
                    if (foundItem != null)
                    {
                        WriteToChat($"SUCCESS! Found: '{foundItem.Name}'");
                        WriteToChat($"Utils.GetItemStackSize: {Utils.GetItemStackSize("Prismatic Taper")}");
                        WriteToChat($"Utils.GetItemIcon: 0x{Utils.GetItemIcon("Prismatic Taper"):X}");
                        WriteToChat($"Utils.GetItemDisplayIcon: 0x{Utils.GetItemDisplayIcon("Prismatic Taper"):X}");
                        WriteToChat("=== TELEMETRY WILL NOW WORK! ===");
                    }
                    else
                    {
                        WriteToChat("ERROR: Still can't find Prismatic Taper with utility functions!");
                    }
                }
                catch (Exception ex)
                {
                    WriteToChat($"Search error: {ex.Message}");
                }
            }, "");

            _commandRouter.Register("deathstats", args =>
            {
                try
                {
                    WriteToChat("=== Death Tracking Statistics ===");
                    WriteToChat($"Session Deaths: {_killTracker.SessionDeaths}");
                    WriteToChat($"Total Deaths: {_killTracker.TotalDeaths}");

                    int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
                    WriteToChat($"Character Property NumDeaths: {currentCharDeaths}");

                    if (currentCharDeaths != _killTracker.TotalDeaths)
                    {
                        WriteToChat($"[WARNING] Death count sync issue detected!");
                        WriteToChat($"Updating totalDeaths from {_killTracker.TotalDeaths} to {currentCharDeaths}");
                        _killTracker.SetTotalDeaths(currentCharDeaths);
                    }

                    WriteToChat("Death tracking is active and will increment on character death.");
                }
                catch (Exception ex)
                {
                    WriteToChat($"Death stats error: {ex.Message}");
                }
            }, "Show death tracking stats");

            _commandRouter.Register("testdeath", args =>
            {
                try
                {
                    WriteToChat("=== Manual Death Test ===");
                    WriteToChat($"Current sessionDeaths variable: {_killTracker.SessionDeaths}");
                    WriteToChat($"Current totalDeaths variable: {_killTracker.TotalDeaths}");

                    int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths);
                    WriteToChat($"Character Property NumDeaths (43): {currentCharDeaths}");

                    _killTracker.OnDeath();
                    WriteToChat($"Manually incremented sessionDeaths to: {_killTracker.SessionDeaths}");
                    WriteToChat("Note: This doesn't simulate a real death, just tests the tracking variables.");

                    WriteToChat($"Death event subscription check:");
                    var deathEvent = typeof(Decal.Adapter.Wrappers.CharacterFilter).GetEvent("Death");
                    WriteToChat($"Death event exists: {deathEvent != null}");
                }
                catch (Exception ex)
                {
                    WriteToChat($"Test death error: {ex.Message}");
                }
            }, "");

            _commandRouter.Register("testtaper", args =>
            {
                try
                {
                    WriteToChat("=== Cached Taper Tracking Test ===");
                    WriteToChat($"Cached Count: {_inventoryMonitor.CachedPrismaticCount}");
                    WriteToChat($"Last Count: {_inventoryMonitor.LastPrismaticCount}");

                    int utilsCount = Utils.GetItemStackSize("Prismatic Taper");
                    WriteToChat($"Utils Count: {utilsCount}");

                    if (_inventoryMonitor.CachedPrismaticCount == utilsCount)
                    {
                        WriteToChat("[OK] Cached count matches Utils count");
                    }
                    else
                    {
                        WriteToChat($"[WARNING] Count mismatch! Cached: {_inventoryMonitor.CachedPrismaticCount}, Utils: {utilsCount}");
                        WriteToChat("Refreshing cached count...");
                        _inventoryMonitor.Initialize();
                    }

                    WriteToChat("=== Container Analysis ===");
                    int mainPackCount = 0;
                    int sidePackCount = 0;
                    int playerId = CoreManager.Current.CharacterFilter.Id;

                    foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
                    {
                        if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
                        {
                            int stackCount = wo.Values(LongValueKey.StackCount, 1);
                            if (wo.Container == playerId)
                                mainPackCount += stackCount;
                            else
                                sidePackCount += stackCount;
                        }
                    }

                    WriteToChat($"Main Pack Tapers: {mainPackCount}");
                    WriteToChat($"Side Pack Tapers: {sidePackCount}");
                    WriteToChat($"Total: {mainPackCount + sidePackCount}");

                    WriteToChat("=== Event System Status ===");
                    WriteToChat($"Tracking {_inventoryMonitor.TrackedTaperCount} taper stacks for delta detection");
                    WriteToChat($"Known stack sizes: {_inventoryMonitor.KnownStackSizesCount} items");
                    WriteToChat("Pure delta tracking - NO expensive inventory scans during events!");
                    WriteToChat("Now tracks: consumption, drops, trades, container moves");
                    WriteToChat("Try moving tapers between containers and casting spells!");
                }
                catch (Exception ex)
                {
                    WriteToChat($"Taper test error: {ex.Message}");
                }
            }, "");

            _commandRouter.Register("finditem", args =>
            {
                if (args.Length > 1)
                {
                    string itemName = string.Join(" ", args, 1, args.Length - 1).Trim('"');
                    WriteToChat($"=== Searching for: '{itemName}' ===");

                    var foundItem = Utils.FindItemByName(itemName);
                    if (foundItem != null)
                    {
                        WriteToChat($"FOUND: '{foundItem.Name}'");
                        WriteToChat($"Count: {foundItem.Values(LongValueKey.StackCount, 0)}");
                        WriteToChat($"Icon: 0x{foundItem.Icon:X}");
                        WriteToChat($"Display Icon: 0x{(foundItem.Icon + 0x6000000):X}");
                        WriteToChat($"Object Class: {foundItem.ObjectClass}");
                    }
                    else
                    {
                        WriteToChat($"NOT FOUND: '{itemName}'");
                        WriteToChat("Make sure the name is exactly as it appears in-game.");
                    }
                }
                else
                {
                    WriteToChat("Usage: /mm finditem \"Item Name\"");
                    WriteToChat("Example: /mm finditem \"Prismatic Taper\"");
                }
            }, "Find item in inventory");

            _commandRouter.Register("checkforupdate", args =>
            {
                Task.Run(async () =>
                {
                    await UpdateManager.CheckForUpdateAsync();
                    try
                    {
                        ViewManager.RefreshUpdateStatus();
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"Error refreshing UI: {ex.Message}");
                    }
                });
            }, "Check for plugin updates");

            _commandRouter.Register("update", args =>
            {
                Task.Run(async () =>
                {
                    await UpdateManager.DownloadAndInstallUpdateAsync();
                });
            }, "Download and install latest update");

            _commandRouter.Register("debugupdate", args =>
            {
                Views.VVSTabbedMainView.DebugUpdateControls();
            }, "");

            _commandRouter.Register("sendinventory", args =>
            {
                if (_inventoryLogger != null)
                    _inventoryLogger.ForceInventoryUpload();
                else
                    WriteToChat("[INV] Inventory system not initialized");
            }, "Force full inventory upload");

            _commandRouter.Register("refreshquests", args =>
            {
                try
                {
                    WriteToChat("[QUEST] Refreshing quest data...");
                    Views.FlagTrackerView.RefreshQuestData();
                }
                catch (Exception ex)
                {
                    WriteToChat($"[QUEST] Refresh failed: {ex.Message}");
                }
            }, "Refresh quest data");

            _commandRouter.Register("queststatus", args =>
            {
                try
                {
                    WriteToChat("=== Quest Streaming Status ===");
                    WriteToChat($"Timer Active: {_questStreamingService?.IsRunning ?? false}");
                    WriteToChat($"WebSocket Enabled: {WebSocketEnabled}");
                    WriteToChat($"Quest Manager: {(questManager != null ? "Active" : "Not Active")}");
                    WriteToChat($"Quest Count: {questManager?.QuestList?.Count ?? 0}");

                    if (questManager?.QuestList != null)
                    {
                        var priorityQuests = questManager.QuestList
                            .Where(q => QuestStreamingService.IsHighPriorityQuest(q.Id))
                            .GroupBy(q => q.Id)
                            .Select(g => g.First())
                            .ToList();
                        WriteToChat($"Priority Quests Found: {priorityQuests.Count}");
                        foreach (var quest in priorityQuests)
                        {
                            string questName = questManager.GetFriendlyQuestName(quest.Id);
                            WriteToChat($"  - {questName} ({quest.Id})");
                        }
                    }

                    WriteToChat($"Verbose Logging: {PluginSettings.Instance?.VerboseLogging ?? false}");
                    WriteToChat("Use '/mm verbose' to toggle debug logging");
                }
                catch (Exception ex)
                {
                    WriteToChat($"[QUEST] Status check failed: {ex.Message}");
                }
            }, "Show quest streaming status");

            _commandRouter.Register("verbose", args =>
            {
                if (PluginSettings.Instance != null)
                {
                    PluginSettings.Instance.VerboseLogging = !PluginSettings.Instance.VerboseLogging;
                    WriteToChat($"Verbose logging: {(PluginSettings.Instance.VerboseLogging ? "ENABLED" : "DISABLED")}");
                }
                else
                {
                    WriteToChat("Settings not initialized");
                }
            }, "Toggle verbose debug logging");

            _commandRouter.Register("metasrefresh", args =>
            {
                Task.Run(async () =>
                {
                    try
                    {
                        var count = await MetaSyncManager.RefreshCatalogAsync();
                        WriteToChat($"[Metas] Loaded {count} remote .met/.nav files");
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"[Metas] Refresh failed: {ex.Message}");
                    }
                });
            }, "Refresh remote metas list");

            _commandRouter.Register("metascheck", args =>
            {
                Task.Run(async () =>
                {
                    try
                    {
                        var summary = await MetaSyncManager.CheckStatusesAsync();
                        WriteToChat($"[Metas] Up-to-date: {summary.upToDate}, Updates: {summary.needsUpdate}, Missing: {summary.notDownloaded}, Errors: {summary.errors}");
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"[Metas] Check failed: {ex.Message}");
                    }
                });
            }, "Check local metas against remote");

            _commandRouter.Register("metaspull", args =>
            {
                Task.Run(async () =>
                {
                    try
                    {
                        var result = await MetaSyncManager.DownloadOutdatedAsync();
                        WriteToChat($"[Metas] Downloaded: {result.downloaded}, Skipped: {result.skipped}, Failed: {result.failed}, Backups: {result.backups}");
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"[Metas] Pull failed: {ex.Message}");
                    }
                });
            }, "Download outdated metas/nav files");

            _commandRouter.Register("metaspullfile", args =>
            {
                if (args.Length < 2)
                {
                    WriteToChat("Usage: /mm metaspullfile <relative/path/file.met>");
                    return;
                }

                var relativePath = string.Join(" ", args.Skip(1));
                Task.Run(async () =>
                {
                    try
                    {
                        var result = await MetaSyncManager.DownloadFileAsync(relativePath);
                        WriteToChat($"[Metas] {relativePath}: {result.Message}");
                    }
                    catch (Exception ex)
                    {
                        WriteToChat($"[Metas] Pull file failed: {ex.Message}");
                    }
                });
            }, "Download one metas/nav file by relative path");
        }


    }
}

    
