using System;
using System.Collections.Generic;
using System.Linq;
using Decal.Adapter;
using Decal.Adapter.Wrappers;
using Decal.Interop.Input;
using Mag.Shared.Constants;
using Timer = Decal.Interop.Input.Timer;

namespace MosswartMassacre
{
    /// <summary>
    /// Chest Looter - Automated chest looting with VTank loot profile integration
    /// Ported from Utility Belt with enhancements for name-based chest/key selection
    /// </summary>
    public class ChestLooter : IDisposable
    {
        #region Enums

        private enum ItemState
        {
            None = 0x0001,
            InContainer = 0x0002,
            RequestingInfo = 0x0004,
            NeedsToBeLooted = 0x0008,
            Ignore = 0x0016,
            Looted = 0x0032,
            Blacklisted = 0x0064
        }

        private enum LooterState
        {
            Closed = 0x0001,
            Unlocking = 0x0002,
            Locked = 0x0004,
            Unlocked = 0x0008,
            Opening = 0x00016,
            Open = 0x0032,
            Looting = 0x0064,
            Closing = 0x0128,
            Salvaging = 0x0256,
            Done = 0x0512
        }

        public enum RunType
        {
            None = 0x0001,
            CommandLine = 0x0002,  // Started via /mm commands
            UI = 0x0004            // Started via UI button
        }

        private enum ContainerType
        {
            Chest = 0x0001,
            MyCorpse = 0x0002,
            MonsterCorpse = 0x0004,
            Unknown = 0x0008
        }

        #endregion

        #region Fields

        private readonly CoreManager core;
        private readonly ChestLooterSettings settings;

        private Dictionary<int, ItemState> containerItems = new Dictionary<int, ItemState>();
        private List<int> containerItemsList = new List<int>();

        private bool dispatchEnabled = false;
        private bool ubOwnedContainer = false;
        private bool first = true;

        private int targetContainerID = 0;
        private int targetKeyID = 0;
        private string targetKeyName = "";
        private string targetChestName = "";

        private int lootAttemptCount = 0;
        private int unlockAttempt = 0;
        private int openAttempt = 0;
        private int lastOpenContainer = 0;

        private LooterState looterState = LooterState.Done;
        private LooterState lastLooterState = LooterState.Done;
        private RunType runType = RunType.None;
        private ContainerType containerType = ContainerType.Unknown;

        private DateTime startTime = DateTime.MinValue;
        private DateTime lastAttempt = DateTime.UtcNow;

        private TimerClass baseTimer;
        private bool disposed = false;

        #endregion

        #region Events

        public event EventHandler LooterStarted;
        public event EventHandler LooterFinished;
        public event EventHandler LooterFinishedForceStop;
        public event EventHandler<string> StatusChanged;

        #endregion

        #region Properties

        public bool IsRunning => looterState != LooterState.Done;
        public RunType CurrentRunType => runType;
        public string CurrentStatus => GetStatusString();

        #endregion

        #region Constructor & Initialization

        public ChestLooter(CoreManager coreManager, ChestLooterSettings chestLooterSettings)
        {
            core = coreManager ?? throw new ArgumentNullException(nameof(coreManager));
            settings = chestLooterSettings ?? throw new ArgumentNullException(nameof(chestLooterSettings));

            settings.Validate();
        }

        public void Initialize()
        {
            try
            {
                if (settings.Enabled)
                {
                    EnableDispatch();
                }
            }
            catch (Exception ex)
            {
                LogError($"ChestLooter initialization error: {ex.Message}");
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Start looting using configured chest and key names
        /// </summary>
        public bool StartByName()
        {
            return StartByName(settings.ChestName, settings.KeyName);
        }

        /// <summary>
        /// Start looting with specified chest and key names
        /// </summary>
        public bool StartByName(string chestName, string keyName)
        {
            try
            {
                if (IsRunning)
                {
                    LogError("Looter is already running. Stop it first.");
                    return false;
                }

                if (string.IsNullOrEmpty(chestName))
                {
                    LogError("Chest name not set. Use /mm setchest <name>");
                    return false;
                }

                if (string.IsNullOrEmpty(keyName))
                {
                    LogError("Key name not set. Use /mm setkey <name>");
                    return false;
                }

                // Find the closest chest by name
                var chest = Utils.FindClosestChestByName(chestName);
                if (chest == null)
                {
                    LogError($"Could not find chest named '{chestName}'");
                    return false;
                }

                // Find the key in inventory
                var key = Utils.FindKeyInInventory(keyName);
                if (key == null)
                {
                    LogError($"Could not find key named '{keyName}' in inventory");
                    return false;
                }

                targetChestName = chestName;
                targetKeyName = keyName;
                targetContainerID = chest.Id;
                targetKeyID = key.Id;

                Log($"Starting chest looter: chest='{chest.Name}' key='{key.Name}'");

                return StartInternal(RunType.CommandLine);
            }
            catch (Exception ex)
            {
                LogError($"Error starting looter by name: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Start looting with manually selected chest and key
        /// </summary>
        public bool StartWithSelection(int chestId, string keyName)
        {
            try
            {
                if (IsRunning)
                {
                    LogError("Looter is already running. Stop it first.");
                    return false;
                }

                if (!core.Actions.IsValidObject(chestId))
                {
                    LogError("Invalid chest selection");
                    return false;
                }

                var key = Utils.FindKeyInInventory(keyName);
                if (key == null)
                {
                    LogError($"Could not find key named '{keyName}' in inventory");
                    return false;
                }

                targetContainerID = chestId;
                targetKeyID = key.Id;
                targetKeyName = keyName;

                return StartInternal(RunType.UI);
            }
            catch (Exception ex)
            {
                LogError($"Error starting looter with selection: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// Stop the looter
        /// </summary>
        public void Stop()
        {
            try
            {
                if (!IsRunning)
                    return;

                Log("Stopping looter...");
                DoneLooting(false, true);
            }
            catch (Exception ex)
            {
                LogError($"Error stopping looter: {ex.Message}");
            }
        }

        /// <summary>
        /// Set the chest name for next run
        /// </summary>
        public void SetChestName(string name)
        {
            settings.ChestName = name;
            Log($"Chest name set to: {name}");
        }

        /// <summary>
        /// Set the key name for next run
        /// </summary>
        public void SetKeyName(string name)
        {
            settings.KeyName = name;
            Log($"Key name set to: {name}");
        }

        #endregion

        #region Private Core Methods

        private bool StartInternal(RunType type)
        {
            try
            {
                looterState = LooterState.Unlocking;
                lastAttempt = DateTime.UtcNow;
                runType = type;
                ubOwnedContainer = true;
                startTime = DateTime.UtcNow;

                EnableDispatch();
                EnableBaseTimer();
                LockVtankSettings();  // Lock VTank to prevent interference

                LooterStarted?.Invoke(this, EventArgs.Empty);
                UpdateStatus("Starting looter...");

                return true;
            }
            catch (Exception ex)
            {
                LogError($"Error in StartInternal: {ex.Message}");
                return false;
            }
        }

        private void EnableDispatch()
        {
            try
            {
                if (!dispatchEnabled)
                {
                    core.EchoFilter.ServerDispatch += EchoFilter_ServerDispatch;
                    core.EchoFilter.ClientDispatch += EchoFilter_ClientDispatch;
                    dispatchEnabled = true;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error enabling dispatch: {ex.Message}");
            }
        }

        private void DisableDispatch()
        {
            try
            {
                if (dispatchEnabled && !settings.Enabled)
                {
                    core.EchoFilter.ServerDispatch -= EchoFilter_ServerDispatch;
                    core.EchoFilter.ClientDispatch -= EchoFilter_ClientDispatch;
                    dispatchEnabled = false;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error disabling dispatch: {ex.Message}");
            }
        }

        private void EnableBaseTimer()
        {
            try
            {
                if (baseTimer == null)
                {
                    baseTimer = new TimerClass();
                    baseTimer.Timeout += BaseTimer_Timeout;
                    baseTimer.Start(settings.OverallSpeed);
                }
            }
            catch (Exception ex)
            {
                LogError($"Error enabling base timer: {ex.Message}");
            }
        }

        private void DisableBaseTimer()
        {
            try
            {
                if (baseTimer != null)
                {
                    baseTimer.Timeout -= BaseTimer_Timeout;
                    baseTimer.Stop();
                    baseTimer = null;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error disabling base timer: {ex.Message}");
            }
        }

        private void DoneLooting(bool writeChat = true, bool force = false)
        {
            try
            {
                var itemsInContainer = containerItems.Where(x => x.Value == ItemState.InContainer);
                var needsToBeLootedItems = containerItems.Where(x => x.Value == ItemState.NeedsToBeLooted);
                var lootedItems = containerItems.Where(x => x.Value == ItemState.Looted);
                var blacklistedItems = containerItems.Where(x => x.Value == ItemState.Blacklisted);
                var ignoredItems = containerItems.Where(x => x.Value == ItemState.Ignore);
                var requestingInfoItems = containerItems.Where(x => x.Value == ItemState.RequestingInfo);

                if (writeChat)
                {
                    var elapsed = DateTime.UtcNow - startTime;
                    var testMode = settings.TestMode ? " [TEST MODE] " : "";

                    if (needsToBeLootedItems.Count() + blacklistedItems.Count() <= 0)
                    {
                        Log($"Looter{testMode} completed in {elapsed.TotalSeconds:F1}s - Scanned {containerItems.Count} items, looted {lootedItems.Count()} items");
                    }
                    else
                    {
                        Log($"Looter{testMode} completed in {elapsed.TotalSeconds:F1}s - Scanned {containerItems.Count} items, looted {lootedItems.Count()} items - Failed to loot {needsToBeLootedItems.Count() + blacklistedItems.Count()} items");
                    }
                }

                // Unlock VTank FIRST (like UB does)
                UnlockVtankSettings();

                // Reset state (matching UB exactly)
                containerItems.Clear();
                containerItemsList.Clear();
                looterState = LooterState.Done;
                lastLooterState = LooterState.Done;
                ubOwnedContainer = false;
                lootAttemptCount = 0;
                unlockAttempt = 0;
                openAttempt = 0;
                targetKeyID = 0;
                containerType = ContainerType.Unknown;
                startTime = DateTime.MinValue;
                lastAttempt = DateTime.UtcNow;

                // FIXED: Check force FIRST - skip restart entirely if force stopping
                if (force)
                {
                    // Force stop - don't restart, just cleanup
                    LooterFinishedForceStop?.Invoke(this, EventArgs.Empty);
                    targetKeyName = "";
                    targetChestName = "";
                    targetContainerID = 0;
                    runType = RunType.None;
                    DisableBaseTimer();
                    if (!settings.Enabled && dispatchEnabled)
                    {
                        DisableDispatch();
                    }
                    UpdateStatus("Ready");
                    return;  // Exit early - don't check for more keys
                }

                // Not force - check if we should restart with more keys
                if ((runType == RunType.UI || runType == RunType.CommandLine) && HasMoreKeys(targetKeyName))
                {
                    // Restart the whole process like UB does with StartUI
                    StartUI(targetContainerID, targetKeyName);
                }
                else
                {
                    // No more keys - cleanup
                    targetKeyName = "";
                    targetChestName = "";
                    targetContainerID = 0;
                    runType = RunType.None;
                    DisableBaseTimer();
                    if (!settings.Enabled && dispatchEnabled)
                    {
                        DisableDispatch();
                    }
                    UpdateStatus("Ready");
                    LooterFinished?.Invoke(this, EventArgs.Empty);
                }
            }
            catch (Exception ex)
            {
                LogError($"Error in DoneLooting: {ex.Message}");
            }
        }

        /// <summary>
        /// Start looting via UI/CommandLine - matches UB's StartUI method
        /// </summary>
        private void StartUI(int container, string key)
        {
            try
            {
                targetContainerID = container;
                targetKeyName = key;

                // Find key by EXACT name match (like UB)
                var keyObj = FindKeyByExactName(key);
                if (keyObj == null)
                {
                    Log("Looter: Out of keys to use");
                    LooterFinished?.Invoke(this, EventArgs.Empty);
                    DoneLooting(false, true);
                    return;
                }

                targetKeyID = keyObj.Id;

                looterState = LooterState.Unlocking;
                lastAttempt = DateTime.UtcNow;
                // Keep runType as is (UI or CommandLine)
                ubOwnedContainer = true;
                first = true;
                unlockAttempt = 0;
                openAttempt = 0;

                EnableDispatch();
                EnableBaseTimer();
            }
            catch (Exception ex)
            {
                LogError($"Error in StartUI: {ex.Message}");
            }
        }

        /// <summary>
        /// Find a key by EXACT name match (like UB's Util.FindInventoryObjectByName)
        /// </summary>
        private WorldObject FindKeyByExactName(string name)
        {
            try
            {
                using (var inv = core.WorldFilter.GetInventory())
                {
                    foreach (var wo in inv)
                    {
                        if (wo.Name == name)
                        {
                            return wo;
                        }
                    }
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        private void StopAndCleanup()
        {
            targetKeyName = "";
            targetChestName = "";
            targetContainerID = 0;
            runType = RunType.None;

            DisableBaseTimer();
            DisableDispatch();

            LooterFinished?.Invoke(this, EventArgs.Empty);
            UpdateStatus("Ready");
        }

        /// <summary>
        /// Lock VTank to prevent it from interfering with looting (matches UB's LockVtankSettings)
        /// </summary>
        private void LockVtankSettings()
        {
            try
            {
                if (vTank.Instance == null) return;

                // Lock navigation to prevent moving away from chest
                vTank.Decision_Lock(uTank2.ActionLockType.Navigation, TimeSpan.FromMilliseconds(30000));
                // Lock void spells (debuffs)
                vTank.Decision_Lock(uTank2.ActionLockType.VoidSpellLockedOut, TimeSpan.FromMilliseconds(30000));
                // Lock war spells (attacks)
                vTank.Decision_Lock(uTank2.ActionLockType.WarSpellLockedOut, TimeSpan.FromMilliseconds(30000));
                // Lock melee attacks
                vTank.Decision_Lock(uTank2.ActionLockType.MeleeAttackShot, TimeSpan.FromMilliseconds(30000));
                // Lock item use to prevent VTank from using items
                vTank.Decision_Lock(uTank2.ActionLockType.ItemUse, TimeSpan.FromMilliseconds(30000));
            }
            catch (Exception ex)
            {
                LogError($"Error locking VTank: {ex.Message}");
            }
        }

        /// <summary>
        /// Unlock VTank after looting is complete (matches UB's UnLockVtankSettings)
        /// </summary>
        private void UnlockVtankSettings()
        {
            try
            {
                if (vTank.Instance == null) return;

                vTank.Decision_UnLock(uTank2.ActionLockType.Navigation);
                vTank.Decision_UnLock(uTank2.ActionLockType.VoidSpellLockedOut);
                vTank.Decision_UnLock(uTank2.ActionLockType.WarSpellLockedOut);
                vTank.Decision_UnLock(uTank2.ActionLockType.MeleeAttackShot);
                vTank.Decision_UnLock(uTank2.ActionLockType.ItemUse);
            }
            catch (Exception ex)
            {
                LogError($"Error unlocking VTank: {ex.Message}");
            }
        }

        #endregion

        #region Network Event Handlers

        private void EchoFilter_ServerDispatch(object sender, NetworkMessageEventArgs e)
        {
            try
            {
                switch (e.Message.Type)
                {
                    case 0xF7B0:
                        switch (e.Message.Value<int>("event"))
                        {
                            case 0x0196: // Item_OnViewContents - chest opened
                                HandleChestOpened(e);
                                break;

                            case 0x01C7: // Item_UseDone (Failure Type)
                                if (e.Message.Value<int>("unknown") == 1201) // Already open
                                {
                                    if (core.Actions.OpenedContainer == targetContainerID)
                                    {
                                        looterState = LooterState.Open;
                                    }
                                }
                                break;

                            case 0x0022: // Item_ServerSaysContainID (moved object)
                                int movedObjectId = e.Message.Value<int>("item");
                                int movedContainerId = e.Message.Value<int>("container");
                                if (containerItems.ContainsKey(movedObjectId) && IsContainerPlayer(movedContainerId))
                                {
                                    LootedItem(movedObjectId);
                                }
                                break;
                        }
                        break;

                    case 0x0024: // Object deleted (stacked item looted)
                        int removedObjectId = e.Message.Value<int>("object");
                        LootedItem(removedObjectId);
                        break;

                    case 0xF750: // Chest unlocked/locked
                        HandleChestLockState(e);
                        break;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error in ServerDispatch: {ex.Message}");
            }
        }

        private void EchoFilter_ClientDispatch(object sender, NetworkMessageEventArgs e)
        {
            try
            {
                switch (e.Message.Type)
                {
                    case 0xF7B1:
                        switch (e.Message.Value<int>("action"))
                        {
                            case 0x0019: // Item moved into container
                                int itemIntoContainer = e.Message.Value<int>("item");
                                if (containerItems.ContainsKey(itemIntoContainer))
                                {
                                    lootAttemptCount++;
                                }
                                break;
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error in ClientDispatch: {ex.Message}");
            }
        }

        private void HandleChestOpened(NetworkMessageEventArgs e)
        {
            try
            {
                // Match UB's condition exactly: if (Enabled || runType == runtype.UI)
                // For us: settings.Enabled OR runType is UI/CommandLine
                if (settings.Enabled || runType == RunType.UI || runType == RunType.CommandLine)
                {
                    int containerID = e.Message.Value<int>("container");
                    var itemCount = e.Message.Value<int>("itemCount");
                    var items = e.Message.Struct("items");

                    if (itemCount <= 0) return;
                    if (containerID == 0) return;
                    if (!IsValidContainer(containerID)) return;

                    containerType = GetContainerType(containerID);

                    // Return if container isn't enabled in settings
                    if (containerType == ContainerType.Unknown) return;
                    // EnableChests check only when NOT running via UI/CommandLine
                    if (containerType == ContainerType.Chest && !settings.EnableChests && runType != RunType.UI && runType != RunType.CommandLine) return;

                    // Past returns, now we do things
                    startTime = DateTime.UtcNow;
                    targetContainerID = containerID;

                    // When we receive chest contents, the chest is open - transition to Open state
                    // UB relies on HandleStateOpening checking OpenedContainer, but we can be more direct
                    if (looterState != LooterState.Looting)
                    {
                        looterState = LooterState.Open;
                    }

                    // Add all items in chest to tracking
                    for (int i = 0; i < itemCount; i++)
                    {
                        int woid = items.Struct(i).Value<int>("item");
                        ItemState state = ItemState.InContainer;
                        UpdateContainerItems(woid, state);
                    }

                    Log($"HandleChestOpened: Added {itemCount} items, containerItems.Count={containerItems.Count}, state={looterState}");

                    EnableBaseTimer();
                    UpdateStatus($"Chest opened, scanning {itemCount} items...");
                }
                else
                {
                    // UB's else branch
                    looterState = LooterState.Open;
                    DoneLooting(false, false);
                }
            }
            catch (Exception ex)
            {
                LogError($"Error handling chest opened: {ex.Message}");
            }
        }

        private void HandleChestLockState(NetworkMessageEventArgs e)
        {
            try
            {
                int lockedObjID = e.Message.Value<int>("object");
                int lockedObjIDEffect = e.Message.Value<int>("effect");

                if (ubOwnedContainer && lockedObjID == targetContainerID)
                {
                    if (lockedObjIDEffect == 148) // Locked
                    {
                        if (looterState == LooterState.Closing)
                        {
                            looterState = LooterState.Closed;
                        }
                        else
                        {
                            looterState = LooterState.Locked;
                        }
                    }
                    else if (lockedObjIDEffect == 147) // Unlocked
                    {
                        looterState = LooterState.Unlocked;
                        UpdateStatus("Chest unlocked");
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"Error handling chest lock state: {ex.Message}");
            }
        }

        #endregion

        #region Timer Handler

        private void BaseTimer_Timeout(Timer Source)
        {
            try
            {
                if (lastOpenContainer != core.Actions.OpenedContainer)
                {
                    lastOpenContainer = core.Actions.OpenedContainer;
                }

                if (lastLooterState != looterState)
                {
                    first = true;
                    lastLooterState = looterState;
                }

                switch (looterState)
                {
                    case LooterState.Done:
                        // Nothing to do - timer will be stopped when appropriate
                        break;
                    case LooterState.Closed:
                        HandleStateClosed();
                        break;
                    case LooterState.Locked:
                        HandleStateLocked();
                        break;
                    case LooterState.Unlocked:
                        HandleStateUnlocked();
                        break;
                    case LooterState.Unlocking:
                        HandleStateUnlocking();
                        break;
                    case LooterState.Open:
                        HandleStateOpen();
                        break;
                    case LooterState.Opening:
                        HandleStateOpening();
                        break;
                    case LooterState.Looting:
                        HandleStateLooting();
                        break;
                    case LooterState.Closing:
                        HandleStateClosing();
                        break;
                }
            }
            catch (Exception ex)
            {
                LogError($"Error in BaseTimer_Timeout: {ex.Message}");
            }
        }

        #endregion

        #region State Handlers

        private void HandleStateClosed()
        {
            if (ubOwnedContainer)
            {
                looterState = LooterState.Done;
                DoneLooting();
            }
            else
            {
                looterState = LooterState.Done;
            }
        }

        private void HandleStateLocked()
        {
            if (runType == RunType.UI || runType == RunType.CommandLine)
            {
                looterState = LooterState.Unlocking;
            }
            else
            {
                DoneLooting(false, false);
            }
        }

        private void HandleStateUnlocked()
        {
            looterState = LooterState.Opening;
            lastAttempt = DateTime.UtcNow;
        }

        private void HandleStateUnlocking()
        {
            if (first || DateTime.UtcNow - lastAttempt >= TimeSpan.FromMilliseconds(settings.DelaySpeed))
            {
                if (unlockAttempt >= settings.MaxUnlockAttempts)
                {
                    DoneLooting(false, false);
                    return;
                }

                lastAttempt = DateTime.UtcNow;
                core.Actions.SelectItem(targetKeyID);
                core.Actions.ApplyItem(targetKeyID, targetContainerID);
                unlockAttempt++;
                first = false;
                UpdateStatus($"Unlocking... (attempt {unlockAttempt}/{settings.MaxUnlockAttempts})");
            }
        }

        private void HandleStateOpen()
        {
            looterState = LooterState.Looting;
            UpdateStatus("Looting items...");
        }

        private void HandleStateOpening()
        {
            if (core.Actions.OpenedContainer == targetContainerID)
            {
                looterState = LooterState.Open;
                return;
            }

            if (first || DateTime.UtcNow - lastAttempt >= TimeSpan.FromMilliseconds(settings.DelaySpeed))
            {
                if (openAttempt >= settings.MaxOpenAttempts)
                {
                    Log("Reached max open attempts");
                    DoneLooting(false, false);
                    return;
                }

                lastAttempt = DateTime.UtcNow;
                core.Actions.UseItem(targetContainerID, 0);
                openAttempt++;
                first = false;
                UpdateStatus($"Opening... (attempt {openAttempt}/{settings.MaxOpenAttempts})");
            }
        }

        private void HandleStateLooting()
        {
            var itemsInContainer = containerItems.Where(x => x.Value == ItemState.InContainer);
            var needsToBeLootedItems = containerItems.Where(x => x.Value == ItemState.NeedsToBeLooted);
            var lootedItems = containerItems.Where(x => x.Value == ItemState.Looted);
            var blacklistedItems = containerItems.Where(x => x.Value == ItemState.Blacklisted);
            var ignoredItems = containerItems.Where(x => x.Value == ItemState.Ignore);
            var requestingInfoItems = containerItems.Where(x => x.Value == ItemState.RequestingInfo);

            // Process items that need classification (InContainer first, then RequestingInfo)
            if (itemsInContainer.Count() > 0)
            {
                GetLootDecision(itemsInContainer.First().Key);
            }
            else if (requestingInfoItems.Count() > 0)
            {
                // Re-check items that were waiting for ID data
                GetLootDecision(requestingInfoItems.First().Key);
            }

            // Loot items
            foreach (int item in needsToBeLootedItems.Select(w => w.Key).ToList())
            {
                if (!settings.TestMode)
                {
                    core.Actions.MoveItem(item, core.CharacterFilter.Id);
                    if (needsToBeLootedItems.First().Key == item)
                    {
                        lootAttemptCount++;
                    }
                }
                else
                {
                    containerItems[item] = ItemState.Looted;
                }

                if (lootAttemptCount >= settings.AttemptsBeforeBlacklisting)
                {
                    int blackListedItem = needsToBeLootedItems.First().Key;
                    containerItems[blackListedItem] = ItemState.Blacklisted;
                }
            }

            // Check if done looting (all items are either looted, blacklisted, or ignored)
            if (containerItems.Count() > 0 &&
                lootedItems.Count() + blacklistedItems.Count() + ignoredItems.Count() == containerItemsList.Count())
            {
                if (ubOwnedContainer)
                {
                    lastAttempt = DateTime.UtcNow;
                    looterState = LooterState.Closing;
                }
                else
                {
                    DoneLooting();
                }
            }

            UpdateStatus($"Looting... ({lootedItems.Count()}/{containerItems.Count} items)");
        }

        private void HandleStateClosing()
        {
            // Check if container is closed (match UB exactly - only check OpenedContainer == 0)
            if (core.Actions.OpenedContainer == 0)
            {
                looterState = LooterState.Closed;
                return;
            }

            // Try to close the chest
            if (first || DateTime.UtcNow - lastAttempt >= TimeSpan.FromMilliseconds(settings.DelaySpeed))
            {
                lastAttempt = DateTime.UtcNow;
                core.Actions.UseItem(targetContainerID, 0);
                first = false;
                UpdateStatus("Closing chest...");
            }
        }

        #endregion

        #region Loot Decision Logic

        /// <summary>
        /// Determine if an item should be looted based on VTank loot profile
        /// Ported directly from Utility Belt's Looter.cs GetLootDecision method
        /// </summary>
        private void GetLootDecision(int item)
        {
            try
            {
                if (!core.Actions.IsValidObject(item))
                {
                    containerItems.Remove(item);
                    containerItemsList.Remove(item);
                    return;
                }

                var wo = core.WorldFilter[item];

                // Check if item needs ID first (red rules in VTank profile)
                bool needsId = false;
                try
                {
                    needsId = uTank2.PluginCore.PC.FLootPluginQueryNeedsID(item);
                }
                catch
                {
                    containerItems[item] = ItemState.Ignore;
                    return;
                }

                if (!needsId)
                {
                    // Item doesn't need ID, classify immediately
                    dynamic result = null;
                    try
                    {
                        result = uTank2.PluginCore.PC.FLootPluginClassifyImmediate(item);
                    }
                    catch
                    {
                        containerItems[item] = ItemState.Ignore;
                        return;
                    }

                    if (result.IsKeep)
                    {
                        containerItems[item] = ItemState.NeedsToBeLooted;
                    }

                    if (result.IsKeepUpTo)
                    {
                        // IsKeepUpTo - check inventory count for this rule
                        int itemCount = 0;
                        bool waitingForInvItems = false;

                        using (var inv = core.WorldFilter.GetInventory())
                        {
                            foreach (WorldObject invItem in inv)
                            {
                                waitingForInvItems = false;

                                if (uTank2.PluginCore.PC.FLootPluginQueryNeedsID(invItem.Id))
                                {
                                    core.Actions.RequestId(invItem.Id);
                                    waitingForInvItems = true;
                                    break;
                                }
                                else
                                {
                                    var invItemResult = uTank2.PluginCore.PC.FLootPluginClassifyImmediate(invItem.Id);
                                    if (result.RuleName == invItemResult.RuleName)
                                    {
                                        if (invItem.Values(LongValueKey.StackMax, 0) > 0)
                                        {
                                            itemCount += invItem.Values(LongValueKey.StackCount, 1);
                                        }
                                        else
                                        {
                                            itemCount++;
                                        }
                                    }
                                }
                            }
                        }

                        if (!waitingForInvItems)
                        {
                            if (itemCount >= result.Data1)
                            {
                                containerItems[item] = ItemState.Ignore;
                            }
                            else
                            {
                                containerItems[item] = ItemState.NeedsToBeLooted;
                            }
                        }
                    }

                    if (result.IsSalvage)
                    {
                        // Treat salvage items as loot (salvage feature not implemented)
                        containerItems[item] = ItemState.NeedsToBeLooted;
                    }

                    if (result.IsNoLoot)
                    {
                        containerItems[item] = ItemState.Ignore;
                    }

                    // If none of the above matched, item stays in current state (InContainer)
                    // This means it doesn't match any rule - we ignore it
                    if (!result.IsKeep && !result.IsKeepUpTo && !result.IsSalvage && !result.IsNoLoot)
                    {
                        containerItems[item] = ItemState.Ignore;
                    }

                    if (settings.TestMode)
                    {
                        containerItems[item] = ItemState.Looted;
                    }
                }
                else
                {
                    // Item needs ID - request it and mark as RequestingInfo
                    if (containerItems[item] != ItemState.RequestingInfo)
                    {
                        core.Actions.RequestId(item);
                        containerItems[item] = ItemState.RequestingInfo;
                    }
                }
            }
            catch
            {
                containerItems[item] = ItemState.Ignore;
            }
        }

        #endregion

        #region Helper Methods

        private bool IsValidContainer(int containerId)
        {
            try
            {
                WorldObject containerWO = core.WorldFilter[containerId];
                if (containerWO.ObjectClass != ObjectClass.Container &&
                    containerWO.ObjectClass != ObjectClass.Corpse)
                {
                    return false;
                }

                // Check if it has item slots (is a real container)
                try
                {
                    if (containerWO.Values(LongValueKey.ItemSlots) < 48)
                        return false;
                }
                catch
                {
                    return false;
                }

                return true;
            }
            catch
            {
                return false;
            }
        }

        private ContainerType GetContainerType(int container)
        {
            try
            {
                var wo = core.WorldFilter[container];
                switch (wo.ObjectClass)
                {
                    case ObjectClass.Container:
                        return ContainerType.Chest;
                    case ObjectClass.Corpse:
                        if (wo.Name == $"Corpse of {core.CharacterFilter.Name}")
                            return ContainerType.MyCorpse;
                        else
                            return ContainerType.MonsterCorpse;
                }
                return ContainerType.Unknown;
            }
            catch
            {
                return ContainerType.Unknown;
            }
        }

        private bool IsContainerPlayer(int container)
        {
            try
            {
                if (container == 0)
                    return false;

                // Main pack
                if (core.WorldFilter[container].Id == core.CharacterFilter.Id)
                    return true;

                // Side pack
                if (core.WorldFilter[container].Container == core.CharacterFilter.Id)
                    return true;

                return false;
            }
            catch
            {
                return false;
            }
        }

        private void UpdateContainerItems(int item, ItemState state)
        {
            try
            {
                if (!containerItems.ContainsKey(item))
                {
                    containerItems.Add(item, state);
                }
                if (!containerItemsList.Contains(item))
                {
                    containerItemsList.Add(item);
                }
            }
            catch (Exception ex)
            {
                LogError($"Error updating container items: {ex.Message}");
            }
        }

        private void LootedItem(int item)
        {
            try
            {
                if (containerItems.ContainsKey(item))
                {
                    if (containerItems[item] != ItemState.Looted)
                    {
                        lootAttemptCount = 0;
                        containerItems[item] = ItemState.Looted;
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"Error marking item as looted: {ex.Message}");
            }
        }

        private bool HasMoreKeys(string keyName)
        {
            try
            {
                // Match UB's exact implementation - EXACT name match
                int count = 0;
                using (var inv = core.WorldFilter.GetInventory())
                {
                    foreach (var wo in inv)
                    {
                        if (wo.Name == keyName)  // EXACT match like UB
                        {
                            if (wo.Values(LongValueKey.StackCount, 0) > 0)
                            {
                                count += wo.Values(LongValueKey.StackCount);
                            }
                            else
                            {
                                count++;
                            }
                        }
                    }
                }
                return count > 0;
            }
            catch (Exception ex)
            {
                LogError($"HasMoreKeys exception: {ex.Message}");
                return false;
            }
        }

        private string GetStatusString()
        {
            if (looterState == LooterState.Done)
                return "Ready";

            return $"{looterState} - {containerItems.Count} items";
        }

        private void UpdateStatus(string status)
        {
            StatusChanged?.Invoke(this, status);
        }

        private void Log(string message)
        {
            PluginCore.WriteToChat($"[ChestLooter] {message}");
        }

        private void LogError(string message)
        {
            PluginCore.WriteToChat($"[ChestLooter] ERROR: {message}");
        }

        #endregion

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    try
                    {
                        if (baseTimer != null)
                        {
                            baseTimer.Timeout -= BaseTimer_Timeout;
                            baseTimer.Stop();
                            baseTimer = null;
                        }
                        DisableDispatch();
                    }
                    catch (Exception ex)
                    {
                        LogError($"Error during disposal: {ex.Message}");
                    }
                }

                disposed = true;
            }
        }

        ~ChestLooter()
        {
            Dispose(false);
        }

        #endregion
    }
}
