using EFT.InventoryLogic;
using SPT.Reflection.Utils;
using System.Text;
using KeycardTemplate = GClass2623;
using ArmorTemplate = GClass2550;
using ArmoredRigTemplate = GClass2602;
using MagazineTemplate = GClass2586;

namespace LootValueEX.Extensions
{
    internal static class ItemExtensions
    {
        /// <summary>
        /// Checks if an item has been examined by the player.
        /// </summary>
        /// <param name="item">The item to check.</param>
        /// <returns>True if the item has been examined, false otherwise.</returns>
        internal static bool IsExamined(this Item? item) => item != null &&
                                                            ClientAppUtils.GetMainApp().GetClientBackEndSession()
                                                                .Profile.Examined(item);

        internal static bool IsStacked(this Item? item) =>
            item != null && (item.StackObjectsCount > 1 || item.UnlimitedCount);

        internal static string PrehashTemplate(this Item? item) =>
            string.Format("{0}|{1}|{2}", item?.TemplateId, item.GetDurability(), item.GetUses());

        internal static Item? UnstackItem(this Item? item)
        {
            if (item == null)
                return null;

            if (!item.IsStacked())
                return item;

            Item? itemClone = item.CloneItem();
            itemClone.StackObjectsCount = 1;
            itemClone.UnlimitedCount = false;
            return itemClone;
        }

        /// <summary>
        /// Retrieves the value of a specific attribute from an item.
        /// </summary>
        /// <param name="item">The item to retrieve the attribute from.</param>
        /// <param name="attributeId">The ID of the attribute to retrieve.</param>
        /// <returns>The value of the attribute, or -1f if the item is null or the attribute is not found.</returns>
        internal static float GetItemAttribute(this Item? item, EItemAttributeId attributeId)
        {
            if (item == null)
                return -1f;
            try
            {
                return item.Attributes.Single(att => att.Id.Equals(attributeId)).Base();
            }
            catch (InvalidOperationException)
            {
                return -1f;
            }
        }

        /// <summary>
        /// Generates a custom hash for the given item.
        ///  
        /// This function takes an optional Item as a parameter and returns its custom hash.
        /// If the item is null, it returns an empty string. The custom hash is determined by the item's template, durability, and uses.
        /// </summary>
        /// <param name="item">The item to retrieve the custom hash for.</param>
        /// <returns>The custom hash of the item, or an empty string if the item is null.</returns>
        internal static async Task<string> GetCustomHashAsync(this Item? item)
        {
            if (item == null)
                return string.Empty;
            StringBuilder prehashString = new StringBuilder();

            item.GetAllItems().Where(i => !i.Equals(item)).DoMap(i => prehashString.Append(i.PrehashTemplate()));
            if (item.Template.Equals(typeof(MagazineTemplate)))
            {
                MagazineTemplate magTemplate = (MagazineTemplate)item.Template;
                magTemplate.Cartridges.DoMap(s => s.Items.DoMap(i => prehashString.Append(i.PrehashTemplate())));
            }

            prehashString.Append(item.PrehashTemplate());
            return await Task.Run(() => Utils.HashingUtils.ConvertToSha256(prehashString.ToString()));
        }

        internal static string GetCustomHash(this Item? item) =>
            Task.Run(() => item?.GetCustomHashAsync()).Result ?? string.Empty;
#if DEBUG
        internal static string AttributesToString(this Item? item)
        {
            if (item == null)
                return String.Empty;

            if (item.Attributes.IsNullOrEmpty())
                return String.Empty;

            StringBuilder sb = new StringBuilder();
            item.Attributes.ForEach(attr =>
                sb.Append(
                    $"\n{attr.Id}\n\tName: {attr.Name}\n\tBase Value: {attr.Base()}\n\tString value: {attr.StringValue()}\n\tDisplay Type: {attr.DisplayType()}"));
            return sb.ToString();
        }
#endif


        /// <summary>
        /// Retrieves the durability of a given item.
        /// 
        /// This function takes an optional Item as a parameter and returns its durability.
        /// If the item is null, it returns -1f. The durability is determined by the item's template.
        /// </summary>
        /// <param name="item">The item to retrieve the durability for.</param>
        /// <returns>The durability of the item, or -1f if the item is null.</returns>
        internal static float GetDurability(this Item? item)
        {
            if (item == null)
                return -1f;

            switch (item.Template)
            {
                case ArmoredRigTemplate armoredRig:
                case ArmorTemplate armor:
                    return Utils.ItemUtils.GetArmorDurability(
                        item.GetItemComponentsInChildren<RepairableComponent>(true));
                default:
                    return item.GetItemAttribute(EItemAttributeId.Durability);
            }
        }

        /// <summary>
        /// Retrieves the number of uses remaining for a given item.
        /// 
        /// This function takes an optional Item as a parameter and returns the number of uses remaining.
        /// If the item is null, it returns -1f. The number of uses is determined by the item's template.
        /// </summary>
        /// <param name="item">The item to retrieve the number of uses for.</param>
        /// <returns>The number of uses remaining for the item, or -1f if the item is null.</returns>
        internal static float GetUses(this Item? item)
        {
            if (item == null)
                return -1f;

            switch (item.Template)
            {
                case KeycardTemplate:
                    KeyComponent keyComponent = item.GetItemComponent<KeyComponent>();
                    return keyComponent.Template.MaximumNumberOfUsage - keyComponent.NumberOfUsages;
                case Meds2Class:
                    if (item.TryGetItemComponent(out MedKitComponent medKitComponent))
                        return medKitComponent.HpResource;
                    return -1f;
                case MagazineTemplate:
                    MagazineClass? magazineClass = item as MagazineClass;
                    return magazineClass?.Count ?? -1f;
                case AmmoBoxTemplate:
                    AmmoBox? ammoBox = item as AmmoBox;
                    return ammoBox?.Count ?? -1f;
                default:
                    return -1f;
            }
        }
    }
}