#pragma once

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace ta::features::items::contracts
{
    enum class ItemCategory : std::uint32_t
    {
        Other      = 1u << 0,
        Weapon     = 1u << 1,
        Ammo       = 1u << 2,
        WeaponMod  = 1u << 3,
        Equipment  = 1u << 4,
        Medical    = 1u << 5,
        Diet       = 1u << 6,
        Key        = 1u << 7,
        Container  = 1u << 8,
        TradeGoods = 1u << 9
    };

    [[nodiscard]] constexpr std::uint32_t item_category_bit(const ItemCategory category) noexcept
    {
        return static_cast<std::uint32_t>(category);
    }

    inline constexpr std::uint32_t all_item_category_mask =
        item_category_bit(ItemCategory::Other) |
        item_category_bit(ItemCategory::Weapon) |
        item_category_bit(ItemCategory::Ammo) |
        item_category_bit(ItemCategory::WeaponMod) |
        item_category_bit(ItemCategory::Equipment) |
        item_category_bit(ItemCategory::Medical) |
        item_category_bit(ItemCategory::Diet) |
        item_category_bit(ItemCategory::Key) |
        item_category_bit(ItemCategory::Container) |
        item_category_bit(ItemCategory::TradeGoods);

    [[nodiscard]] constexpr std::string_view item_category_name(const ItemCategory category) noexcept
    {
        switch (category)
        {
        case ItemCategory::Weapon: return "weapon throwable grenade";
        case ItemCategory::Ammo: return "ammo ammunition";
        case ItemCategory::WeaponMod: return "weapon mod attachment magazine";
        case ItemCategory::Equipment: return "equipment armor helmet plate tactical rig vest backpack";
        case ItemCategory::Medical: return "medical medicine stim";
        case ItemCategory::Diet: return "diet food drink provision";
        case ItemCategory::Key: return "key keycard";
        case ItemCategory::Container: return "container case";
        case ItemCategory::TradeGoods: return "trade goods barter utility valuable";
        case ItemCategory::Other:
        default: return "other";
        }
    }

    [[nodiscard]] constexpr std::string_view item_category_label_key(const ItemCategory category) noexcept
    {
        switch (category)
        {
        case ItemCategory::Weapon: return "Weapons";
        case ItemCategory::Ammo: return "Ammunition";
        case ItemCategory::WeaponMod: return "Weapon Mods";
        case ItemCategory::Equipment: return "Equipment";
        case ItemCategory::Medical: return "Medical Items";
        case ItemCategory::Diet: return "Diet";
        case ItemCategory::Key: return "Keys";
        case ItemCategory::Container: return "Containers";
        case ItemCategory::TradeGoods: return "Trade Goods";
        case ItemCategory::Other:
        default: return "Other Items";
        }
    }

    struct ItemCatalogEntry
    {
        std::string id;
        std::string name;
        bool wishlisted = false;
        std::string search_text;
        ItemCategory category = ItemCategory::Other;
        std::string short_name;
        int max_stack_size = 1;
    };

    struct ItemCatalogSnapshot
    {
        std::vector<ItemCatalogEntry> items;
        std::unordered_map<std::string, std::size_t> index_by_id;
        std::size_t static_catalog_size = 0;
        bool loading = false;
        bool ready = false;
    };

    inline bool looks_like_template_id(const std::string_view value)
    {
        if (value.size() != 24)
            return false;

        return std::all_of(value.begin(), value.end(), [](const unsigned char c)
        {
            return std::isxdigit(c) != 0;
        });
    }

    inline bool matches_search(const ItemCatalogEntry& item, const std::string_view needle)
    {
        if (needle.empty())
            return true;

        std::size_t start = 0;
        while (start < needle.size())
        {
            const std::size_t end = needle.find(' ', start);
            const std::string_view term = needle.substr(start, end == std::string_view::npos ? end : end - start);
            if (!term.empty() && item.search_text.find(term) == std::string::npos)
                return false;
            if (end == std::string_view::npos)
                break;
            start = end + 1;
        }
        return true;
    }
}
