using System;
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Quarry Computer", "Marte6", "1.0.0")]
    [Description("Manages quarry operations and configurations with in-game UI")]
    public class QuarryComputer : CovalencePlugin
    {
        [PluginReference]
        private Plugin ImageLibrary = null,
            LangAPI = null,
            Notify = null;

        #region Constants
        private const ulong QuarrySkinId = 3341965622;

        private const string BaseItemName = "arcade.machine.chippy";
        private const string BaseDeployedItemName = "chippyarcademachine";
        private const string QuarryPrefab = "assets/content/structures/excavator/prefabs/excavatorsignalcomputer.prefab";
        private const string QuarryOutputStoragePrefab = "assets/prefabs/deployable/hot air balloon/subents/hab_storage.prefab";
        private const string QuarryFuelStoragePrefab = "assets/content/vehicles/boats/rowboat/subents/rowboat_storage.prefab";
        private const string StorageAdaptorPrefab = "assets/prefabs/deployable/playerioents/industrialadaptors/storageadaptor.deployed.prefab";
        private const string ButtonPrefab = "assets/prefabs/io/electric/switches/pressbutton/pressbutton.prefab";
        private const string GroundLossFx = "assets/prefabs/deployable/quarry/effects/mining-quarry-deploy.prefab";
        private const string QuarryTurnedOnFx = "assets/prefabs/npc/autoturret/effects/targetacquired.prefab";
        private const string QuarryUpgradedFx = "assets/bundled/prefabs/fx/build/promote_toptier.prefab";

        private const string UiBackground = "6iYcl0UppZxwaotNu6kghxTF7s8a4r4d";
        private const string UiMainPanel = "VsUNAYUweeIfW5qvG1zHcQ09hy06GGFi";
        private const string UiLeftPanel = "6k3PPUUOyd8A6RgqGBan9cMLn59GTzRS";
        private const string UiRightPanel = "o9C5OcIYWCoOsRpcULoyfKPGGxb8LAoJ";
        private const string UiPopupOverlayBlock = "Mu8AqzfN17Djw3TstzIsjnJqKi4W5n5j";
        private const string UiPopupShadow = "JVQZBnIR9vmF844dkfYsuOymwWBTCeJV";
        private const string UiPopupBorder = "tmZZ1WqIBuBlqvNeabQH6D6wvK5Gfz7u";
        private const string UiPopupBackground = "3kNfPEASr9RkcBTB9vtMLoxkpKfqc4FU";
        private const string UiButtonStart = "TB9vtMLoxk3kNfPEASr9RkcBpKfqc4FU";

        private const string CommandUiClose = "3bizteteO5ic8z82GJHeQx77hlDMLcI9";
        private const string CommandUiStart = "n4DHuNvEGFpIW2gCuZ4ZftJzQBLHpZUx";
        private const string CommandUiRemove = "fl6n4DHugCuZKCrLHpZUF4ZNvEGIW2px";
        private const string CommandUiConfirmRemove = "PTZdQzOalxnZFiwDkiKgMdk1wbuG6T64";
        private const string CommandUiCancelRemove = "09stuljgGWtJzQBvFevq2chgWvZNvodr";
        private const string CommandUiUpgrade = "mSjiZxaTD4hdq9hUHcLGdQviIPzfksgK";
        private const string CommandUiConfirmUpgrade = "gTpJiMwJn7JSbSWFr8fSjY4nHg72Fryl";
        private const string CommandUiCancelUpgrade = "B3xei8Zq8VVr4zptxJxYxEfW3sh40x82";

        private const Item.Flag ExtractedItemFlag = (Item.Flag)(1 << 78);
        #endregion

        #region Fields
        private static QuarryComputer Instance;
        private int MaxLevel;
        private readonly List<string> Permissions = new() { "quarrycomputer.give", "quarrycomputer.industrial", "quarrycomputer.vip", "quarrycomputer.norequirements", "quarrycomputer.admin" };
        #endregion

        #region Cache
        private readonly HashSet<ulong> PlayersWithPopup = new();
        private readonly HashSet<ulong> ItemContainersFuel = new();
        private readonly HashSet<ulong> ItemContainersOutput = new();
        private readonly HashSet<ulong> StorageContainersFuel = new();
        private readonly HashSet<ulong> StorageContainersOutput = new();
        private readonly HashSet<ulong> Buttons = new();
        private readonly HashSet<ulong> Adaptors = new();
        private readonly Dictionary<ulong, QuarryComputerBehaviour> OpenedQuarryInterfaces = new();
        #endregion

        #region Game Hooks
        private void OnServerInitialized()
        {
            if (ImageLibrary == null)
            {
                PrintError("ImageLibrary is not installed! Plugin has been disabled!");
                Interface.Oxide.UnloadPlugin(Title);
                return;
            }

            Instance = this;
            LoadImages();
            RegisterPermissions();
            RegisterCommands();
            SetMaxLevel();
            LoadData();
            InitializeExistingQuarries();
        }

        private void OnServerSave() => SaveData();

        private void Unload()
        {
            DestroyAllUis();
            UnloadAllQuarryBehaviours();
            SaveData();
        }

        private void OnEntityBuilt(Planner planner, GameObject builtObject)
        {
            var player = planner?.GetOwnerPlayer();
            var entity = builtObject.GetComponent<BaseEntity>();

            if (player == null || entity == null || entity.ShortPrefabName != BaseDeployedItemName || entity.skinID != QuarrySkinId)
                return;

            var playerQuarryLimit = Configs.Profiles.Default.QuarryLimit;

            if (permission.UserHasPermission(player.UserIDString, "quarrycomputer.vip"))
                playerQuarryLimit = Configs.Profiles.VIP.QuarryLimit;

            var level = RemoveItemData(player.GetActiveItem().uid.Value);

            if (!player.IsBuildingAuthed() || GetPlayerQuarryCount(player.userID) >= playerQuarryLimit)
            {
                if (entity)
                    entity.Kill();

                NotifyPlayer(player.IPlayer, !player.IsBuildingAuthed() ? "NoBuildingAuth" : "QuarryLimitReached", 1);
                timer.Once(1, () => GiveQuarryItem(player, level));
                return;
            }

            DeployQuarry(player, entity, level);
            NextTick(() =>
            {
                entity?.Kill();
            });
        }

        object OnEntityKill(ExcavatorSignalComputer quarry)
        {
            if (quarry.OwnerID != 0)
                RemoveQuarryData(quarry.OwnerID, quarry.net.ID.Value);

            return null;
        }

        ItemContainer.CanAcceptResult? CanAcceptItem(ItemContainer container, Item item)
        {
            if (ItemContainersFuel.Contains(container.uid.Value) && item.info.shortname != Configs.FuelConfiguration.Shortname)
                return ItemContainer.CanAcceptResult.CannotAccept;

            if (ItemContainersOutput.Contains(container.uid.Value) && !item.flags.HasFlag(ExtractedItemFlag))
                return ItemContainer.CanAcceptResult.CannotAccept;

            return null;
        }

        private object CanPickupEntity(BasePlayer player, IndustrialStorageAdaptor entity)
        {
            if (Adaptors.Contains(entity.net.ID.Value))
                return false;

            return null;
        }

        private object OnExcavatorSuppliesRequest(ExcavatorSignalComputer computer, BasePlayer player)
        {
            if (PluginData.QuarriesData.ContainsKey(computer.net.ID.Value))
                return false;

            return null;
        }

        private object OnButtonPress(PressButton button, BasePlayer player)
        {
            if (Buttons.Contains(button.net.ID.Value))
            {
                var outputContainer = button.GetParentEntity();
                var quarry = outputContainer.GetComponentInParent<QuarryComputerBehaviour>();

                if (quarry.OwnerID != player.userID)
                {
                    NotifyPlayer(player, "NotYours", 1);
                    return null;
                }

                OpenedQuarryInterfaces.Add(player.userID, quarry);
                CreateUi(player, quarry);
            }

            return null;
        }
        #endregion

        #region Data
        private StoredData PluginData;

        private class StoredData
        {
            [JsonProperty("Players")]
            public Dictionary<ulong, PlayersData> PlayersData = new Dictionary<ulong, PlayersData>();

            [JsonProperty("Quarries")]
            public Dictionary<ulong, QuarriesData> QuarriesData = new Dictionary<ulong, QuarriesData>();

            [JsonProperty("Items")]
            public Dictionary<ulong, ItemsData> ItemsData = new Dictionary<ulong, ItemsData>();
        }

        private class PlayersData
        {
            [JsonProperty("Quarries")]
            public HashSet<ulong> ComputerId { get; set; }
        }

        private class QuarriesData
        {
            [JsonProperty("OwnerId")]
            public ulong OwnerId { get; set; }

            [JsonProperty("Level")]
            public int Level { get; set; }
        }

        private class ItemsData
        {
            [JsonProperty("OwnerId")]
            public ulong OwnerId { get; set; }

            [JsonProperty("Level")]
            public int Level { get; set; }
        }

        private void SaveData()
        {
            if (PluginData != null)
            {
                Interface.Oxide.DataFileSystem.WriteObject($"{Name}_Data", PluginData, true);
            }
        }

        private void LoadData()
        {
            PluginData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>($"{Name}_Data");
            if (PluginData == null)
            {
                PluginData = new StoredData();
                SaveData();
            }
        }

        private void AddQuarryData(ulong ownerId, ulong netId, int level = 1)
        {
            if (!PluginData.QuarriesData.ContainsKey(netId))
                PluginData.QuarriesData[netId] = new QuarriesData { OwnerId = ownerId, Level = level };
            else
            {
                var quarryData = PluginData.QuarriesData[netId];
                quarryData.OwnerId = ownerId;
                quarryData.Level = level;
            }

            if (PluginData.PlayersData.ContainsKey(ownerId))
            {
                var playerData = PluginData.PlayersData[ownerId];

                if (!playerData.ComputerId.Contains(netId))
                    playerData.ComputerId.Add(netId);
            }
            else
                PluginData.PlayersData[ownerId] = new PlayersData { ComputerId = new HashSet<ulong> { netId } };
        }

        private void UpdateQuarryLevelData(ulong netId, int newLevel)
        {
            if (PluginData.QuarriesData.ContainsKey(netId))
            {
                var quarryData = PluginData.QuarriesData[netId];
                quarryData.Level = newLevel;
            }
        }

        private int GetQuarryLevelData(ulong netId)
        {
            if (PluginData.QuarriesData.ContainsKey(netId))
                return PluginData.QuarriesData[netId].Level;

            return 1;
        }

        private int GetPlayerQuarryCount(ulong ownerId)
        {
            if (PluginData.PlayersData.ContainsKey(ownerId))
            {
                var playerData = PluginData.PlayersData[ownerId];
                return playerData.ComputerId.Count;
            }

            return 0;
        }

        private void RemoveQuarryData(ulong ownerId, ulong netId)
        {
            if (PluginData.QuarriesData.ContainsKey(netId))
                PluginData.QuarriesData.Remove(netId);

            if (PluginData.PlayersData.ContainsKey(ownerId))
            {
                var playerData = PluginData.PlayersData[ownerId];

                if (playerData.ComputerId.Contains(netId))
                    playerData.ComputerId.Remove(netId);
            }
        }

        private void AddItemData(ulong itemId, ulong userId, int level)
        {
            if (level > 1)
                PluginData.ItemsData[itemId] = new ItemsData { OwnerId = userId, Level = level };
        }

        private int RemoveItemData(ulong itemId)
        {
            var level = 1;
            if (PluginData.ItemsData.ContainsKey(itemId))
            {
                level = PluginData.ItemsData[itemId].Level;
                PluginData.ItemsData.Remove(itemId);
            }

            return level;
        }
        #endregion

        #region Configuration
        private Configuration Configs;

        private class Configuration
        {
            [JsonProperty("Use LangAPI Plugin")]
            public bool UseLangAPI { get; set; }

            [JsonProperty("Use Notify Plugin")]
            public bool UseNotify { get; set; }

            [JsonProperty("Give Quarry Command")]
            public string[] GiveQuarryCommand { get; set; }

            [JsonProperty("Fuel Configuration")]
            public FuelConfiguration FuelConfiguration { get; set; }

            [JsonProperty("No Upgrades (Level 1)")]
            public NoUpgrades NoUpgrades { get; set; }

            [JsonProperty("Upgrades (Level 2+)")]
            public List<UpgradeOption> Upgrades { get; set; }

            [JsonProperty("Keep Upgrades On Remove Quarry")]
            public bool KeepUpgradesOnRemoval { get; set; }

            [JsonProperty("Profiles")]
            public UserProfiles Profiles { get; set; }

            [JsonProperty("Version")]
            public VersionNumber Version { get; set; }
        }

        private class FuelConfiguration
        {
            [JsonProperty("Shortname")]
            public string Shortname { get; set; }

            [JsonProperty("Resource Extraction Interval In Seconds")]
            public int ResourceExtractionInterval { get; set; }

            [JsonProperty("Fuel Required Per Extraction")]
            public int FuelRequiredPerExtraction { get; set; }
        }

        private class NoUpgrades
        {
            [JsonProperty("Storage Slots")]
            public StorageSlots StorageSlots { get; set; }

            [JsonProperty("Resources Output Per Extraction")]
            public List<ResourceItem> ResourcesOutput { get; set; }
        }

        private class StorageSlots
        {
            [JsonProperty("Fuel")]
            public int FuelSlots { get; set; }

            [JsonProperty("Resources")]
            public int OutputSlots { get; set; }
        }

        private class ResourceItem
        {
            [JsonProperty("Shortname")]
            public string Shortname { get; set; }

            [JsonProperty("Amount")]
            public int Amount { get; set; }
        }

        private class UpgradeOption
        {
            [JsonProperty("Storage Slots")]
            public StorageSlots StorageSlots { get; set; }

            [JsonProperty("Resources Output Per Extraction")]
            public List<ResourceItem> ResourcesOutput { get; set; }

            [JsonProperty("Required Items To Upgrade")]
            public List<ResourceItem> UpgradeItems { get; set; }
        }

        private class UserProfiles
        {
            [JsonProperty("Default (No Permission)")]
            public UserProfile Default { get; set; }

            [JsonProperty("VIP (quarrycomputer.vip Permission)")]
            public UserProfile VIP { get; set; }
        }

        private class UserProfile
        {
            [JsonProperty("Quarry Limit")]
            public int QuarryLimit { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                Configs = Config.ReadObject<Configuration>();
                if (Configs != null)
                {
                    if (Configs.Version < Version)
                        UpdateConfigValues();

                    SaveConfig();
                }
                else
                    throw new Exception();
            }
            catch
            {
                PrintError("There is an error in your configuration file. Default values will be used.");
                LoadDefaultConfig();
            }
        }

        protected override void LoadDefaultConfig() => Configs = CreateDefaultConfig();

        protected override void SaveConfig() => Config.WriteObject(Configs);

        private void UpdateConfigValues()
        {
            PrintWarning("Configuration change detected! Updating settings.");

            Configs.Version = Version;

            PrintWarning("Configuration update completed!");
        }

        private Configuration CreateDefaultConfig()
        {
            return new Configuration
            {
                GiveQuarryCommand = new[] { "gq", "givequarry" },
                UseLangAPI = false,
                UseNotify = true,
                FuelConfiguration = new FuelConfiguration
                {
                    Shortname = "charcoal",
                    ResourceExtractionInterval = 6,
                    FuelRequiredPerExtraction = 1,
                },
                NoUpgrades = new NoUpgrades
                {
                    StorageSlots = new StorageSlots { FuelSlots = 3, OutputSlots = 6 },
                    ResourcesOutput = new List<ResourceItem>
                    {
                        new ResourceItem { Shortname = "crude.oil", Amount = 1 },
                        new ResourceItem { Shortname = "stones", Amount = 1 },
                    },
                },
                Upgrades = new List<UpgradeOption>
                {
                    new UpgradeOption
                    {
                        StorageSlots = new StorageSlots { FuelSlots = 6, OutputSlots = 12 },
                        ResourcesOutput = new List<ResourceItem>
                        {
                            new ResourceItem { Shortname = "crude.oil", Amount = 2 },
                            new ResourceItem { Shortname = "metal.ore", Amount = 1 },
                            new ResourceItem { Shortname = "stones", Amount = 2 },
                        },
                        UpgradeItems = new List<ResourceItem>
                        {
                            new ResourceItem { Shortname = "scrap", Amount = 1000 },
                            new ResourceItem { Shortname = "pumpkin", Amount = 1000 },
                            new ResourceItem { Shortname = "bone.fragments", Amount = 1000 },
                        },
                    },
                    new UpgradeOption
                    {
                        StorageSlots = new StorageSlots { FuelSlots = 12, OutputSlots = 24 },
                        ResourcesOutput = new List<ResourceItem>
                        {
                            new ResourceItem { Shortname = "crude.oil", Amount = 3 },
                            new ResourceItem { Shortname = "hq.metal.ore", Amount = 3 },
                            new ResourceItem { Shortname = "metal.ore", Amount = 3 },
                            new ResourceItem { Shortname = "stones", Amount = 3 },
                            new ResourceItem { Shortname = "sulfur.ore", Amount = 3 },
                        },
                        UpgradeItems = new List<ResourceItem>
                        {
                            new ResourceItem { Shortname = "scrap", Amount = 5000 },
                            new ResourceItem { Shortname = "pumpkin", Amount = 1000 },
                            new ResourceItem { Shortname = "cloth", Amount = 10000 },
                            new ResourceItem { Shortname = "red.berry", Amount = 1000 },
                            new ResourceItem { Shortname = "bone.fragments", Amount = 1000 },
                        },
                    },
                },
                KeepUpgradesOnRemoval = true,
                Profiles = new UserProfiles
                {
                    Default = new UserProfile { QuarryLimit = 1 },
                    VIP = new UserProfile { QuarryLimit = 3 },
                },
            };
        }
        #endregion

        #region Localization
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["NoPermission"] = "You don't have permissions to use this command",
                    ["NoBuildingAuth"] = "You must have building auth to use this",
                    ["NotMiningQuarry"] = "This object is not a Mining Quarry",
                    ["NotOwner"] = "This object belongs to another owner",
                    ["QuarryRemoved"] = "Mining Quarry Removed",
                    ["NoQuarriesFound"] = "No quarries found",
                },
                this
            );
        }

        public string GetItemDisplayName(BasePlayer player, string itemShortName)
        {
            ItemDefinition itemDefinition = ItemManager.FindItemDefinition(itemShortName);
            var displayName = itemDefinition.displayName.translated;

            if (Configs.UseLangAPI && LangAPI != null && LangAPI.Call<bool>("IsDefaultDisplayName", displayName))
            {
                var displayNameTranslated = LangAPI.Call<string>("GetItemDisplayName", itemShortName, displayName, player.UserIDString);
                if (!string.IsNullOrEmpty(displayNameTranslated))
                {
                    return displayNameTranslated;
                }
            }

            return displayName;
        }
        #endregion

        #region UiCommands
        [Command(CommandUiStart)]
        private void UiStartCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            var quarry = OpenedQuarryInterfaces[basePlayer.userID];

            if (!quarry.HasFuel())
            {
                DestroyUi(basePlayer);
                NotifyPlayer(player, "NoFuel", 1);
            }
            else
            {
                if (!quarry.HasSpace())
                {
                    DestroyUi(basePlayer);
                    NotifyPlayer(player, "NoSpace", 1);
                }
                else
                {
                    if (quarry.TurnOn())
                    {
                        NotifyPlayer(player, "TurnedOn", 0);
                        Effect.server.Run(QuarryTurnedOnFx, basePlayer.GetNetworkPosition());
                    }
                    else
                    {
                        DestroyUi(basePlayer);
                        NotifyPlayer(player, "AlreadyOn", 0);
                    }
                }
            }

            CuiHelper.DestroyUi(basePlayer, UiButtonStart);
        }

        [Command(CommandUiClose)]
        private void UiCloseCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            DestroyUi(basePlayer);
        }

        [Command(CommandUiRemove)]
        private void UiRemoveCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            ToggleConfirmationPopup(basePlayer, "Remove the Quarry?", CommandUiConfirmRemove, CommandUiCancelRemove);
        }

        [Command(CommandUiConfirmRemove)]
        private void UiConfirmRemoveCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            var quarry = OpenedQuarryInterfaces[basePlayer.userID];

            if (!quarry.IsEmpty())
            {
                DestroyUi(basePlayer);
                NotifyPlayer(basePlayer, "NotEmpty", 1);
                return;
            }

            quarry.SignalComputer.Kill();

            if (Configs.KeepUpgradesOnRemoval)
                GiveQuarryItem(basePlayer, quarry.Level);
            else
                GiveQuarryItem(basePlayer, 1);

            NotifyPlayer(basePlayer, "QuarryRemoved", 0);

            DestroyUi(basePlayer);
        }

        [Command(CommandUiCancelRemove)]
        private void UiCancelRemoveCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            ClosePopup(basePlayer);
        }

        [Command(CommandUiUpgrade)]
        private void UiUpgradeCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            ToggleConfirmationPopup(basePlayer, "Upgrade the Quarry?", CommandUiConfirmUpgrade, CommandUiCancelUpgrade);
        }

        [Command(CommandUiConfirmUpgrade)]
        private void UiConfirmUpgradeCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            var quarry = OpenedQuarryInterfaces[basePlayer.userID];

            if (!permission.UserHasPermission(basePlayer.UserIDString, "quarrycomputer.norequirements"))
            {
                if (quarry.UpgradeRequirements != null)
                {
                    if (!CheckAndRemoveUpgradeItems(basePlayer, quarry.UpgradeRequirements.UpgradeItems))
                    {
                        NotifyPlayer(player, "UpgradeRequirements", 1);
                        ClosePopup(basePlayer);
                        return;
                    }
                }
            }

            quarry.Upgrade();
            Effect.server.Run(QuarryUpgradedFx, basePlayer.GetNetworkPosition());

            DestroyUi(basePlayer);
        }

        private bool CheckAndRemoveUpgradeItems(BasePlayer player, List<ResourceItem> requiredItems)
        {
            // Create a dictionary of required items and their quantities
            var requiredItemMap = requiredItems.ToDictionary(item => item.Shortname, item => item.Amount);

            // Use a pooled list to gather player's items
            List<Item> allItems = Pool.Get<List<Item>>();
            player.inventory.GetAllItems(allItems);

            // Check if the player has all required items
            if (!HasAllRequiredItems(allItems, requiredItemMap))
            {
                Pool.FreeList(ref allItems);
                return false;
            }

            // Remove required items from player's inventory
            RemoveItems(allItems, player, requiredItemMap);
            Pool.FreeList(ref allItems);
            return true;
        }

        private bool HasAllRequiredItems(List<Item> allItems, Dictionary<string, int> requiredItemMap)
        {
            var playerItemMap = allItems.GroupBy(item => item.info.shortname).ToDictionary(g => g.Key, g => g.Sum(item => item.amount));

            foreach (var requirement in requiredItemMap)
            {
                if (!playerItemMap.TryGetValue(requirement.Key, out int count) || count < requirement.Value)
                {
                    return false;
                }
            }

            return true;
        }

        private void RemoveItems(List<Item> allItems, BasePlayer player, Dictionary<string, int> requiredItemMap)
        {
            foreach (var requirement in requiredItemMap)
            {
                int amountToRemove = requirement.Value;

                foreach (var item in allItems.Where(item => item.info.shortname == requirement.Key))
                {
                    if (amountToRemove <= 0)
                        break;

                    if (item.amount <= amountToRemove)
                    {
                        amountToRemove -= item.amount;
                        player.inventory.Take(null, item.info.itemid, item.amount);
                        item.Remove();
                    }
                    else
                    {
                        item.amount -= amountToRemove;
                        item.MarkDirty();
                        amountToRemove = 0;
                    }
                }
            }
        }

        [Command(CommandUiCancelUpgrade)]
        private void UiCancelUpgradeCommand(IPlayer player)
        {
            var basePlayer = player.Object as BasePlayer;
            ClosePopup(basePlayer);
        }
        #endregion

        #region Commands
        private void GiveQuarryCommand(IPlayer iplayer, string command, string[] args)
        {
            var player = iplayer?.Object as BasePlayer;
            if (player == null || args == null || args.Length < 1)
                return;

            if (!iplayer.HasPermission("quarrycomputer.give"))
            {
                NotifyPlayer(iplayer, "NoPermission", 1);
                return;
            }

            var targetPlayer = BasePlayer.Find(args[0]);
            if (targetPlayer == null)
            {
                NotifyPlayer(iplayer, "PlayerNotFound", 1);
                return;
            }

            if (GiveQuarryItem(targetPlayer, 1))
            {
                NotifyPlayer(iplayer, "QuarrySend", 0);
                NotifyPlayer(targetPlayer, "QuarryReceived", 0);
            }
            else
            {
                NotifyPlayer(iplayer, "QuarrySend", 0);
                NotifyPlayer(targetPlayer, "QuarryReceived", 0);
                NotifyPlayer(targetPlayer, "QuarryDropped", 1);
            }
        }
        #endregion

        #region UI Handling
        private void CreateUi(BasePlayer player, QuarryComputerBehaviour quarry)
        {
            var fuelName = GetItemDisplayName(player, Configs.FuelConfiguration.Shortname);
            var container = new CuiElementContainer();
            AddBackground(container);
            AddMainPanel(container, fuelName);
            AddLeftPanel(container, quarry);

            if (quarry.UpgradeRequirements != null)
                AddRightPanel(container, quarry);

            CuiHelper.AddUi(player, container);
        }

        private void AddBackground(CuiElementContainer container)
        {
            AddPanel(container, "0 0", "1 1", UiBackground, "Overlay", "0 0 0 0.9", true);
        }

        private void AddMainPanel(CuiElementContainer container, string fuelName)
        {
            AddPanel(container, "0.235 0.18", "0.765 0.784", UiMainPanel, UiBackground, "0.6 0.6 0.6 0.95", true);
            AddInnerSquare(container, UiMainPanel, "0 0.9570", "1 1", "0.39 0.39 0.39 1");
            AddText(
                container,
                UiMainPanel,
                "0.02 0.96",
                "1 1",
                $"Extraction Interval: {Configs.FuelConfiguration.ResourceExtractionInterval} sec | Fuel per extraction: {Configs.FuelConfiguration.FuelRequiredPerExtraction} {fuelName}",
                13,
                TextAnchor.MiddleLeft
            );
            AddCloseButton(container, UiMainPanel);
        }

        private void AddLeftPanel(CuiElementContainer container, QuarryComputerBehaviour quarry)
        {
            var outputSlots = quarry.OutputContainer.inventory.capacity;
            var fuelSlots = quarry.FuelContainer.inventory.capacity;

            var parent = UiLeftPanel;
            AddPanel(container, "0 0", "0.5 0.94", parent, UiMainPanel, "0 0 0 0");
            AddPanelStructure(container, parent, $"Quarry Level {quarry.Level}", "0 0 1 1");

            AddText(container, parent, "0.38 0.679", "1 1", "Storage", 13, TextAnchor.MiddleLeft);
            AddText(container, parent, "0.09 0.590", "1 1", $"Fuel: {fuelSlots} slots", 13, TextAnchor.MiddleLeft);
            AddText(container, parent, "0.45 0.590", "1 1", $"Resource: {outputSlots} slots", 13, TextAnchor.MiddleLeft);

            AddHorizontalLine(container, parent, "0.06 0.769", "0.89 0.77");

            AddText(container, parent, "0.34 0.490", "1 1", "Extraction", 13, TextAnchor.MiddleLeft);

            AddDictionaryWithSpacing(container, parent, quarry.ResourcesOutput, 0.18f, 0.725f, 0.065f);

            AddButton(container, parent, "Start", "", "0.15", "0.11", "0.45", "0.16", "0.6 0.6 0.6 0.95", "0.4 0.4 0.4 1");
            if (!quarry.IsOn)
                AddStartButton(container, parent);

            AddRemoveButton(container, parent);
        }

        private void AddRightPanel(CuiElementContainer container, QuarryComputerBehaviour quarry)
        {
            var parent = UiRightPanel;
            AddPanel(container, "0.5 0", "1 0.94", parent, UiMainPanel, "0 0 0 0");
            AddPanelStructure(container, parent, $"Upgrade to Level {quarry.Level + 1}", "0.5 0 0 1");

            AddText(container, parent, "0.38 0.679", "1 1", "Storage", 13, TextAnchor.MiddleLeft);
            AddText(container, parent, "0.09 0.590", "1 1", $"Fuel: {quarry.UpgradeRequirements.StorageSlots.FuelSlots} slots", 13, TextAnchor.MiddleLeft);
            AddText(container, parent, "0.45 0.590", "1 1", $"Resource: {quarry.UpgradeRequirements.StorageSlots.OutputSlots} slots", 13, TextAnchor.MiddleLeft);

            AddHorizontalLine(container, parent, "0.06 0.769", "0.89 0.77");

            AddText(container, parent, "0.34 0.490", "1 1", "Extraction", 13, TextAnchor.MiddleLeft);

            AddDictionaryWithSpacing(container, parent, quarry.UpgradeRequirements.ResourcesOutput, 0.18f, 0.725f, 0.065f);

            AddHorizontalLine(container, parent, "0.06 0.464", "0.89 0.465");

            AddText(container, parent, "0.34 0.0", "1 0.88", "Requirements", 13, TextAnchor.MiddleLeft);

            AddDictionaryWithSpacing(container, parent, quarry.UpgradeRequirements.UpgradeItems, 0.18f, 0.419f, 0.065f);

            AddUpgradeButton(container, parent);
        }

        private void AddPanelStructure(CuiElementContainer container, string parent, string title, string titleColor)
        {
            AddInnerSquare(container, parent, "0.10 0.05", "0.95 0.90", "0 0 0 0.97");
            AddInnerSquare(container, parent, "0.054 0.1033", "0.895 0.945", "0.8 0.8 0.8 1");
            AddInnerSquare(container, parent, "0.06 0.108", "0.89 0.94", titleColor);
            AddHorizontalLine(container, parent, "0.06 0.865", "0.89 0.865");

            AddText(container, parent, "0.1 0.84", "0.84 0.98", title, 15, TextAnchor.MiddleCenter);
        }

        private void AddDictionaryWithSpacing(CuiElementContainer container, string parent, List<ResourceItem> texts, float initialAnchorX, float initialAnchorY, float spacing)
        {
            float anchorX = initialAnchorX;

            int i = 0;
            foreach (var item in texts)
            {
                i++;

                float anchorY = initialAnchorY - (spacing * i);

                if (i >= 5 && i < 9)
                {
                    anchorY = initialAnchorY - (spacing * (i - 4));
                    anchorX = initialAnchorX + 0.23F;
                }

                if (i >= 9)
                {
                    anchorY = initialAnchorY - (spacing * (i - 8));
                    anchorX = initialAnchorX + 0.47F;
                }

                float imageSize = 0.07f;

                float imageAnchorMinX = anchorX - (imageSize / 2);
                float imageAnchorMaxX = anchorX + (imageSize / 2);
                float imageAnchorMinY = anchorY + 0.04f - (imageSize / 2);
                float imageAnchorMaxY = anchorY + 0.02f + (imageSize / 2);

                AddImage(container, parent, item.Shortname, $"{imageAnchorMinX} {imageAnchorMinY}", $"{imageAnchorMaxX} {imageAnchorMaxY}", "ResourceImage");

                AddText(container, parent, $"{anchorX + 0.05f} {anchorY}", $"{anchorX + 0.05f + 0.8f} {anchorY + 0.06}", item.Amount.ToString(), 12, TextAnchor.MiddleLeft);
            }
        }

        private void AddTextsWithSpacing(CuiElementContainer container, string parent, string[] texts, float anchorX, float initialAnchorY, float spacing)
        {
            for (int i = 0; i < texts.Length; i++)
            {
                float anchorY = initialAnchorY - (spacing * i);
                AddText(container, parent, $"{anchorX} {anchorY}", $"{anchorX + 0.8f} {anchorY + 0.06}", texts[i], 12, TextAnchor.MiddleLeft);
            }
        }

        private void AddHorizontalLine(CuiElementContainer container, string parent, string anchorMin, string anchorMax)
        {
            AddInnerSquare(container, parent, anchorMin, anchorMax, "0.8 0.8 0.8 1");
        }

        private void AddImage(CuiElementContainer container, string parent, string resourceName, string anchorMin, string anchorMax, string imageName)
        {
            string imageUrl = ImageLibrary?.Call("GetImage", resourceName) as string;
            if (string.IsNullOrEmpty(imageUrl))
            {
                PrintWarning($"Failed to get image for item: {resourceName}");
                return;
            }

            container.Add(
                new CuiElement
                {
                    Name = imageName,
                    Parent = parent,
                    Components =
                    {
                        new CuiRawImageComponent { Png = imageUrl },
                        new CuiRectTransformComponent { AnchorMin = anchorMin, AnchorMax = anchorMax },
                    },
                }
            );
        }

        private void AddCloseButton(CuiElementContainer container, string parent)
        {
            container.Add(
                new CuiButton
                {
                    Button =
                    {
                        Color = "1 0 0 1",
                        Command = CommandUiClose,
                        Close = UiBackground,
                    },
                    RectTransform = { AnchorMin = "0.965 0.957", AnchorMax = "1 1" },
                    Text =
                    {
                        Text = "✖",
                        FontSize = 13,
                        Align = TextAnchor.MiddleCenter,
                        Color = "1 1 1 0.5",
                    },
                },
                parent
            );
        }

        private void AddStartButton(CuiElementContainer container, string parent)
        {
            AddButton(container, parent, "Start", CommandUiStart, "0.15", "0.11", "0.45", "0.16", "0.6 0.6 0.6 0.95", "0 1 0 1", buttonName: UiButtonStart);
        }

        private void AddRemoveButton(CuiElementContainer container, string parent)
        {
            AddButton(container, parent, "Remove", CommandUiRemove, "0.50", "0.11", "0.80", "0.16", "0.6 0.6 0.6 0.95", "1 0 0 1");
        }

        private void AddUpgradeButton(CuiElementContainer container, string parent)
        {
            AddButton(container, parent, "Upgrade", CommandUiUpgrade, "0.35", "0.11", "0.65", "0.16", "0.6 0.6 0.6 0.95", "0 1 0 1");
        }

        private void DestroyUi(BasePlayer player)
        {
            if (player == null)
                return;

            CuiHelper.DestroyUi(player, UiBackground);
            CuiHelper.DestroyUi(player, UiMainPanel);
            CuiHelper.DestroyUi(player, UiLeftPanel);
            CuiHelper.DestroyUi(player, UiRightPanel);
            CuiHelper.DestroyUi(player, UiPopupOverlayBlock);
            CuiHelper.DestroyUi(player, UiPopupShadow);
            CuiHelper.DestroyUi(player, UiPopupBorder);
            CuiHelper.DestroyUi(player, UiPopupBackground);

            OpenedQuarryInterfaces.Remove(player.userID);
            PlayersWithPopup.Remove(player.userID);
        }

        private void DestroyAllUis()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                DestroyUi(player);
            }
        }

        private void ToggleConfirmationPopup(BasePlayer player, string message, string confirmCommand, string cancelCommand)
        {
            if (player != null && !PlayersWithPopup.Contains(player.userID))
            {
                PlayersWithPopup.Add(player.userID);
                ShowConfirmationPopup(player, message, confirmCommand, cancelCommand);
            }
        }

        private void ShowConfirmationPopup(BasePlayer player, string message, string confirmCommand, string cancelCommand)
        {
            var container = new CuiElementContainer();
            AddPanel(container, "0 0", "1 1", UiPopupOverlayBlock, "Overlay", "0 0 0 0.9", true);
            AddPanel(container, "0.4 0.4", "0.6 0.6", UiPopupShadow, UiPopupOverlayBlock, "0 0 0 0.85");
            AddPanel(container, "0.0 0.09", "0.95 1.0", UiPopupBorder, UiPopupShadow, "1 1 1 1");
            AddPanel(container, "0.01 0.01", "0.991 0.985", UiPopupBackground, UiPopupBorder, "0 0 0 1");
            AddText(container, UiPopupBackground, "0 0", "1 1", message, 14, TextAnchor.MiddleCenter);
            AddButton(container, UiPopupBackground, "Yes", confirmCommand, "0.25", "0.05", "0.45", "0.20", "0.6 0.6 0.6 0.95", "0 1 0 1");
            AddButton(container, UiPopupBackground, "No", cancelCommand, "0.55", "0.05", "0.75", "0.20", "0.6 0.6 0.6 0.95", "1 0 0 1");
            CuiHelper.AddUi(player, container);
        }

        private void ClosePopup(BasePlayer player)
        {
            if (player != null)
            {
                CuiHelper.DestroyUi(player, UiPopupOverlayBlock);
                CuiHelper.DestroyUi(player, UiPopupShadow);
                CuiHelper.DestroyUi(player, UiPopupBorder);
                CuiHelper.DestroyUi(player, UiPopupBackground);

                PlayersWithPopup.Remove(player.userID);
            }
        }
        #endregion

        #region UI Helpers
        private void AddPanel(CuiElementContainer container, string anchorMin, string anchorMax, string panelName, string parent, string color, bool isCursorEnabled = false)
        {
            var panel = new CuiPanel
            {
                Image = { Color = color },
                RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
                CursorEnabled = isCursorEnabled,
            };

            container.Add(panel, parent, panelName);
        }

        private void AddInnerSquare(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string color)
        {
            container.Add(new CuiPanel { Image = { Color = color }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax } }, parent);
        }

        private void AddText(CuiElementContainer container, string parent, string anchorMin, string anchorMax, string text, int fontSize, TextAnchor alignment)
        {
            container.Add(
                new CuiLabel
                {
                    Text =
                    {
                        Text = text,
                        FontSize = fontSize,
                        Font = "DroidSansMono.ttf",
                        Align = alignment,
                        Color = "1 1 1 1",
                    },
                    RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax },
                },
                parent
            );
        }

        private void AddButton(
            CuiElementContainer container,
            string parent,
            string text,
            string command,
            string anchorMinX,
            string anchorMinY,
            string anchorMaxX,
            string anchorMaxY,
            string bgColor,
            string textColor,
            string buttonName = ""
        )
        {
            container.Add(
                new CuiButton
                {
                    Button =
                    {
                        Color = bgColor,
                        Command = command,
                        Close = "",
                    },
                    RectTransform = { AnchorMin = $"{anchorMinX} {anchorMinY}", AnchorMax = $"{anchorMaxX} {anchorMaxY}" },
                    Text =
                    {
                        Text = text,
                        FontSize = 14,
                        Font = "DroidSansMono.ttf",
                        Align = TextAnchor.MiddleCenter,
                        Color = textColor,
                    },
                },
                parent,
                buttonName
            );
        }
        #endregion

        #region Quarry Management Methods
        private bool GiveQuarryItem(BasePlayer player, int level)
        {
            var item = CreateQuarryItem(player.userID, level);

            if (player.inventory.GiveItem(item))
                return true;

            item.Drop(player.inventory.containerMain.dropPosition, player.inventory.containerMain.dropVelocity);
            return false;
        }

        private Item CreateQuarryItem(ulong userID, int level)
        {
            var item = ItemManager.CreateByName(BaseItemName, 1, QuarrySkinId);
            item.name = "Quarry Computer";

            AddItemData(item.uid.Value, userID, level);

            return item;
        }

        private void DeployQuarry(BasePlayer player, BaseEntity entity, int level)
        {
            var position = entity.transform.position;
            var rotation = Quaternion.Euler(0, entity.transform.rotation.eulerAngles.y + 90, 0);
            var quarry = GameManager.server.CreateEntity(QuarryPrefab, position, rotation);
            quarry.OwnerID = player.userID;
            quarry.Spawn();
            quarry.SendNetworkUpdateImmediate();

            AttachContainer(quarry, QuarryOutputStoragePrefab, new Vector3(0.05f, 0.75f, -0.21f), new Vector3(0, 180, 0), false, player);
            AttachContainer(quarry, QuarryFuelStoragePrefab, new Vector3(0.05f, 0.75f, 0.32f), new Vector3(90, 0, 0), true, player);

            AddQuarryData(player.userID, quarry.net.ID.Value, level);

            quarry.gameObject.AddComponent<QuarryComputerBehaviour>();
        }

        private void AttachContainer(BaseEntity entity, string prefab, Vector3 localPosition, Vector3 localEulerAngles, bool isFuelContainer, BasePlayer player)
        {
            var container = GameManager.server.CreateEntity(prefab) as StorageContainer;
            container.SetParent(entity);
            container.OwnerID = player.userID;
            container.transform.localPosition = localPosition;
            container.transform.localRotation = Quaternion.Euler(localEulerAngles);
            container.Spawn();
            container.SendNetworkUpdateImmediate();

            if (permission.UserHasPermission(player.UserIDString, "quarrycomputer.industrial"))
            {
                var adaptor = GameManager.server.CreateEntity(StorageAdaptorPrefab, container.transform.position, container.transform.rotation) as IndustrialStorageAdaptor;
                adaptor.SetParent(container, true, true);
                adaptor.OwnerID = player.userID;
                adaptor.transform.localPosition = isFuelContainer ? new Vector3(0.00f, -0.06f, -0.26f) : new Vector3(0.00f, 0.26f, 0.05f);
                adaptor.transform.rotation = container.transform.rotation * Quaternion.Euler(isFuelContainer ? new Vector3(0, 270, 90) : new Vector3(0, 90, 0));
                adaptor.Spawn();
                adaptor.SendNetworkUpdateImmediate();
            }

            if (!isFuelContainer)
            {
                var button = GameManager.server.CreateEntity(ButtonPrefab) as PressButton;
                button.SetParent(container, true, true);
                button.OwnerID = player.userID;
                button.transform.localPosition = new Vector3(0.40f, 1.60f, -0.13f);
                button.transform.rotation = container.transform.rotation * Quaternion.Euler(new Vector3(10, 90, 180));
                button.Spawn();
                button.SendNetworkUpdateImmediate();
            }
        }

        private void HandleFuelConsumption(Item fuel, int requiredFuelAmount)
        {
            if (fuel.amount > requiredFuelAmount)
                SafelyRemoveItem(fuel.SplitItem(requiredFuelAmount));
            else
                SafelyRemoveItem(fuel);
        }

        public static class DictionaryPool<TKey, TValue>
        {
            private static readonly Stack<Dictionary<TKey, TValue>> pool = new Stack<Dictionary<TKey, TValue>>();

            public static Dictionary<TKey, TValue> Get()
            {
                lock (pool)
                {
                    return pool.Count > 0 ? pool.Pop() : new Dictionary<TKey, TValue>();
                }
            }

            public static void Free(Dictionary<TKey, TValue> dictionary)
            {
                dictionary.Clear();
                lock (pool)
                {
                    pool.Push(dictionary);
                }
            }
        }

        private bool CanTransferAllResources(StorageContainer outputContainer, List<ResourceItem> resources)
        {
            // Use a pooled dictionary to track available space for stackable items
            Dictionary<string, int> stackableItems = DictionaryPool<string, int>.Get();
            int emptySlots = outputContainer.inventory.capacity - outputContainer.inventory.itemList.Count;

            // Calculate available stackable space for each item type
            foreach (var item in outputContainer.inventory.itemList)
            {
                if (!stackableItems.ContainsKey(item.info.shortname))
                {
                    stackableItems[item.info.shortname] = 0;
                }
                stackableItems[item.info.shortname] += item.MaxStackable() - item.amount;
            }

            // Check space for each resource
            foreach (var resource in resources)
            {
                // Retrieve the item definition
                ItemDefinition itemDef = ItemManager.FindItemDefinition(resource.Shortname);
                if (itemDef == null)
                {
                    DictionaryPool<string, int>.Free(stackableItems);
                    return false; // Undefined item, cannot proceed
                }

                int remainingAmount = resource.Amount;
                // Use existing stack space first
                if (stackableItems.TryGetValue(resource.Shortname, out int currentStackableSpace))
                {
                    remainingAmount -= currentStackableSpace;
                }

                // Use empty slots if more space is needed
                while (remainingAmount > 0 && emptySlots > 0)
                {
                    remainingAmount -= itemDef.stackable;
                    emptySlots--;
                }

                if (remainingAmount > 0) // Not enough space
                {
                    DictionaryPool<string, int>.Free(stackableItems);
                    return false;
                }
            }

            DictionaryPool<string, int>.Free(stackableItems); // Free the pooled dictionary
            return true; // All items can fit
        }

        private bool TransferResourcesIfPossible(StorageContainer outputContainer, List<ResourceItem> resources)
        {
            if (!CanTransferAllResources(outputContainer, resources))
            {
                return false; // Cannot transfer all resources, abort
            }

            // Proceed with resource transfer
            foreach (var resource in resources)
            {
                Item item = ItemManager.CreateByName(resource.Shortname, resource.Amount, 0uL);
                item.SetFlag(ExtractedItemFlag, true);

                if (!item.MoveToContainer(outputContainer.inventory))
                {
                    item.Remove(); // Clean up if transfer fails
                    return false; // Unsuccessful transfer, abort
                }
            }

            return true; // Successful transfer of all resources
        }

        private void SafelyRemoveItem(Item item)
        {
            item?.RemoveFromWorld();
            item?.RemoveFromContainer();
            item?.Remove();
        }
        #endregion

        #region Utility Methods
        private void LoadImages()
        {
            var itemIcons = new List<KeyValuePair<string, ulong>>();

            foreach (var item in Configs.NoUpgrades.ResourcesOutput)
                itemIcons.Add(new KeyValuePair<string, ulong>(item.Shortname, 0));

            foreach (var upgrade in Configs.Upgrades)
            {
                foreach (var item in upgrade.ResourcesOutput)
                    itemIcons.Add(new KeyValuePair<string, ulong>(item.Shortname, 0));

                foreach (var item in upgrade.UpgradeItems)
                    itemIcons.Add(new KeyValuePair<string, ulong>(item.Shortname, 0));
            }

            if (itemIcons.Count > 0)
                ImageLibrary?.Call("LoadImageList", Title, itemIcons, null);
        }

        private string Msg(string userid, string key, params object[] obj)
        {
            return string.Format(lang.GetMessage(key, this, userid), obj);
        }

        private void NotifyPlayer(IPlayer player, string key, int type, params object[] obj)
        {
            var UserIDString = player.Id;
            if (Configs.UseNotify && Notify != null)
                Interface.Oxide.CallHook("SendNotify", UserIDString, type, Msg(UserIDString, key, obj));
            else
                player.Message(Msg(player.Id, key, obj));
        }

        private void NotifyPlayer(BasePlayer player, string key, int type, params object[] obj)
        {
            var UserIDString = player.UserIDString;
            if (Configs.UseNotify && Notify != null)
                Interface.Oxide.CallHook("SendNotify", UserIDString, type, Msg(UserIDString, key, obj));
            else
                player.ChatMessage(Msg(UserIDString, key, obj));
        }

        private void RegisterPermissions()
        {
            foreach (string permissionName in Permissions)
                permission.RegisterPermission(permissionName, this);
        }

        private void RegisterCommands()
        {
            AddCovalenceCommand(Configs.GiveQuarryCommand, nameof(GiveQuarryCommand));
        }

        private void InitializeExistingQuarries()
        {
            var existingNetIds = new HashSet<ulong>();

            foreach (var signalComputer in UnityEngine.Object.FindObjectsOfType<ExcavatorSignalComputer>())
            {
                if (signalComputer.OwnerID != 0)
                {
                    var netId = signalComputer.net.ID.Value;
                    var ownerId = signalComputer.OwnerID;
                    var level = GetQuarryLevelData(netId);

                    AddQuarryData(ownerId, netId, level);
                    existingNetIds.Add(netId);

                    signalComputer.gameObject.AddComponent<QuarryComputerBehaviour>();
                }
            }

            RemoveMissingQuarries(existingNetIds);
        }

        private void RemoveMissingQuarries(HashSet<ulong> existingNetIds)
        {
            var quarriesToRemove = new List<ulong>();

            foreach (var quarry in PluginData.QuarriesData)
            {
                if (!existingNetIds.Contains(quarry.Key))
                {
                    quarriesToRemove.Add(quarry.Key);
                }
            }

            foreach (var quarryId in quarriesToRemove)
            {
                var ownerId = PluginData.QuarriesData[quarryId].OwnerId;
                RemoveQuarryData(ownerId, quarryId);
            }
        }

        private void UnloadAllQuarryBehaviours()
        {
            var behaviours = UnityEngine.Object.FindObjectsOfType<QuarryComputerBehaviour>();
            foreach (var behaviour in behaviours)
            {
                behaviour.Unload();
                UnityEngine.Object.Destroy(behaviour);
            }
        }

        private void SetMaxLevel()
        {
            MaxLevel = Configs.Upgrades.Count() + 1;
        }

        #endregion

        #region Quarry Behaviour Class
        private class QuarryComputerBehaviour : MonoBehaviour
        {
            public ExcavatorSignalComputer SignalComputer;
            public StorageContainer OutputContainer;
            public StorageContainer FuelContainer;
            public List<ResourceItem> ResourcesOutput;
            public UpgradeOption UpgradeRequirements;
            public int Level { get; private set; }
            public ulong OwnerID { get; private set; }
            public bool IsOn = true;

            //private Item FuelItem;

            private void Awake()
            {
                InitializeSignalComputer();
                UpdateQuarryOwnerID();
                InitializeContainers();
                UpdateQuarryLevel();
                UpdateQuarryResourcesOutput();
                UpdateQuarryCapacity();
                UpdateUpgradeRequirements();
                ScheduleRoutineTasks();
            }

            public bool TurnOn()
            {
                if (!IsInvoking(nameof(PerformResourceExtraction)))
                {
                    IsOn = true;
                    InvokeRepeating(nameof(PerformResourceExtraction), 0f, Instance.Configs.FuelConfiguration.ResourceExtractionInterval);
                    return true;
                }

                return false;
            }

            private void InitializeSignalComputer()
            {
                SignalComputer = GetComponent<ExcavatorSignalComputer>();
                SignalComputer.SetFlag(BaseEntity.Flags.Reserved8, false);
                SignalComputer.chargePower = 0f;
            }

            private void UpdateQuarryOwnerID()
            {
                OwnerID = SignalComputer.OwnerID;
            }

            private void InitializeContainers()
            {
                foreach (var container in SignalComputer.children.Cast<StorageContainer>())
                {
                    if (container.name == QuarryOutputStoragePrefab || container.name == QuarryFuelStoragePrefab)
                    {
                        if (container.name == QuarryOutputStoragePrefab)
                        {
                            OutputContainer = container;
                            Instance.StorageContainersFuel.Add(container.net.ID.Value);
                            Instance.ItemContainersOutput.Add(container.inventory.uid.Value);
                        }
                        else
                        {
                            FuelContainer = container;
                            Instance.StorageContainersOutput.Add(container.net.ID.Value);
                            Instance.ItemContainersFuel.Add(container.inventory.uid.Value);
                        }

                        foreach (var entity in container.children)
                        {
                            if (entity is PressButton)
                                Instance.Buttons.Add(entity.net.ID.Value);

                            if (entity is IndustrialStorageAdaptor)
                                Instance.Adaptors.Add(entity.net.ID.Value);
                        }
                    }
                }
            }

            public void Upgrade()
            {
                var newLevel = Level + 1;
                if (newLevel <= Instance.MaxLevel)
                    Level = newLevel;

                Instance.UpdateQuarryLevelData(SignalComputer.net.ID.Value, newLevel);
                UpdateQuarryResourcesOutput();
                UpdateQuarryCapacity();
                UpdateUpgradeRequirements();
            }

            private void UpdateQuarryLevel()
            {
                Level = Instance.GetQuarryLevelData(SignalComputer.net.ID.Value);
            }

            private void UpdateQuarryResourcesOutput()
            {
                if (Level == 1)
                    ResourcesOutput = Instance.Configs.NoUpgrades.ResourcesOutput;
                else
                    ResourcesOutput = Instance.Configs.Upgrades[Level - 2].ResourcesOutput;
            }

            private void UpdateQuarryCapacity()
            {
                if (Level == 1)
                {
                    OutputContainer.inventory.capacity = Instance.Configs.NoUpgrades.StorageSlots.OutputSlots;
                    FuelContainer.inventory.capacity = Instance.Configs.NoUpgrades.StorageSlots.FuelSlots;
                }
                else
                {
                    OutputContainer.inventory.capacity = Instance.Configs.Upgrades[Level - 2].StorageSlots.OutputSlots;
                    FuelContainer.inventory.capacity = Instance.Configs.Upgrades[Level - 2].StorageSlots.FuelSlots;
                }
            }

            private void UpdateUpgradeRequirements()
            {
                UpgradeRequirements = Instance.Configs.Upgrades.ElementAtOrDefault(Level - 1);
            }

            private void ScheduleRoutineTasks()
            {
                InvokeRepeating(nameof(CheckGroundIntegrity), 30f, 30f);
                InvokeRepeating(nameof(PerformResourceExtraction), 0f, Instance.Configs.FuelConfiguration.ResourceExtractionInterval);
            }

            public bool HasFuel()
            {
                var fuelItem = FuelContainer.inventory.itemList.Find(item => item.info.shortname == Instance.Configs.FuelConfiguration.Shortname);
                int fuelRequired = Instance.Configs.FuelConfiguration.FuelRequiredPerExtraction;

                if (fuelItem == null || fuelItem.amount < fuelRequired)
                {
                    return false;
                }

                return true;
            }

            public bool HasSpace()
            {
                if (!Instance.CanTransferAllResources(OutputContainer, ResourcesOutput))
                {
                    return false;
                }

                return true;
            }

            public bool IsEmpty()
            {
                if (FuelContainer.inventory.itemList.Count == 0 && OutputContainer.inventory.itemList.Count == 0)
                {
                    return true;
                }

                return false;
            }

            private void PerformResourceExtraction()
            {
                var fuelItem = FuelContainer.inventory.itemList.Find(item => item.info.shortname == Instance.Configs.FuelConfiguration.Shortname);
                int fuelRequired = Instance.Configs.FuelConfiguration.FuelRequiredPerExtraction;

                if (!HasSufficientFuel(fuelItem, fuelRequired))
                {
                    DeactivateQuarry();
                    return;
                }

                if (!Instance.TransferResourcesIfPossible(OutputContainer, ResourcesOutput))
                {
                    DeactivateQuarry();
                    return;
                }

                Instance.HandleFuelConsumption(fuelItem, fuelRequired);
                ActivateQuarry();
            }

            private bool HasSufficientFuel(Item fuelItem, int fuelRequired)
            {
                return fuelItem != null && fuelItem.amount >= fuelRequired;
            }

            private void ActivateQuarry()
            {
                SetSignalComputerState(true);
                IsOn = true;
            }

            private void DeactivateQuarry()
            {
                SetSignalComputerState(false);
                IsOn = false;
                CancelInvoke(nameof(PerformResourceExtraction));
            }

            private void SetSignalComputerState(bool isActive)
            {
                SignalComputer.SetFlag(BaseEntity.Flags.Reserved8, isActive);
                SignalComputer.chargePower = isActive ? 1 : 0;
            }

            private void CheckGroundIntegrity()
            {
                if (SignalComputer.HasParent() && SignalComputer.GetParentEntity() is Tugboat)
                {
                    CancelInvoke(nameof(CheckGroundIntegrity));
                    return;
                }

                if (!IsGroundStable())
                {
                    HandleGroundLoss();
                }
            }

            private bool IsGroundStable()
            {
                Vector3 origin = SignalComputer.transform.position + Vector3.up * 0.1f;
                bool isGrounded = Physics.Raycast(origin, Vector3.down, out RaycastHit hitInfo, 4f, LayerMask.GetMask("Terrain", "Construction"));
                return isGrounded && hitInfo.distance <= 0.2f;
            }

            private void HandleGroundLoss()
            {
                CancelInvoke(nameof(PerformResourceExtraction));
                CancelInvoke(nameof(CheckGroundIntegrity));

                Instance.RemoveQuarryData(OwnerID, SignalComputer.net.ID.Value);

                SignalComputer.Kill();
                Instance.CreateQuarryItem(OwnerID, Level)?.Drop(SignalComputer.transform.position, Vector3.zero);
                Effect.server.Run(GroundLossFx, SignalComputer.transform.position);
            }

            private void OnDestroy()
            {
                CancelInvoke(nameof(PerformResourceExtraction));
                CancelInvoke(nameof(CheckGroundIntegrity));
            }

            public void Unload()
            {
                SignalComputer.SetFlag(BaseEntity.Flags.Reserved8, false);
                SignalComputer.chargePower = 0f;
            }
        }
        #endregion
    }
}