--========================================================
-- ItemInspectionUI - Stats helpers + registry
--========================================================

local CategoryTree = require "InspectSystem/Core/Inspect_CategoryTree"

-- Traducción segura con fallback
function InspectUI_tr(key, fallback)
    if not key or key == "" then
        return fallback or ""
    end
    local text = getText(key)
    if text == key and fallback then
        return fallback
    end
    return text
end

-- Formateo numérico genérico
function InspectUI_fmtNumber(value, decimals)
    if value == nil then return nil end
    if type(value) ~= "number" then return tostring(value) end
    decimals = decimals or 0
    local fmt = "%." .. tostring(decimals) .. "f"
    return string.format(fmt, value)
end

local InspectUI_scaledStats = {}

function InspectUI_isScaledStatId(statId)
    return InspectUI_scaledStats[statId] == true
end

function InspectUI_scaleStatValue(statId, value)
    if not InspectUI_scaledStats[statId] then return value end
    if value == nil then return nil end

    if type(value) ~= "number" then
        local num = tonumber(value)
        if not num then return value end
        value = num
    end

    if value > 0 and value <= 2 then
        value = value * 100
    end

    return value
end

-- Formateo inteligente para porcentajes (acepta 0-1 o 0-100)
function InspectUI_fmtPercentSmart(value)
    if value == nil then return nil end
    if type(value) ~= "number" then
        local num = tonumber(value)
        if not num then return tostring(value) end
        value = num
    end
    if value <= 1 then
        value = value * 100
    end
    return InspectUI_fmtNumber(value, 0) .. "%"
end

-- Detectar si un HandWeapon es de rango (firearm)
function InspectUI_isRangedWeapon(item)
    if not item or not instanceof(item, "HandWeapon") then
        return false
    end

    if item.isRanged then
        local ok, result = pcall(function() return item:isRanged() end)
        if ok then return result end
    end

    if item.getAmmoType then
        local t = item:getAmmoType()
        return t ~= nil and t ~= ""
    end

    return false
end


-- Detect if the item is an actual liquid container (bottles, buckets, jugs, etc.)
function InspectUI_isFluidContainer(item)
    if not item then return false end

    local function safeCall(target, methodName)
        if not target or not target[methodName] then return false end
        local ok, result = pcall(function() return target[methodName](target) end)
        return ok and result == true
    end

    local function safeLower(str)
        if not str then return "" end
        return string.lower(tostring(str))
    end

    local displayCategory = safeLower(item.getDisplayCategory and item:getDisplayCategory())
    local name            = safeLower(item.getDisplayName and item:getDisplayName())

    local isDrainable = false
    if item.getDrainableUsesInt then
        local ok, uses = pcall(function() return item:getDrainableUsesInt() end)
        if ok and uses and uses > 0 then
            isDrainable = true
        end
    end
    if not isDrainable and item.getUseDelta then
        local ok, ud = pcall(function() return item:getUseDelta() end)
        if ok and ud and ud > 0 and ud < 1 then
            isDrainable = true
        end
    end
    if not isDrainable and instanceof and instanceof(item, "DrainableComboItem") then
        isDrainable = true
    end

    local hasFluidContainer = false
    if item.getFluidContainer then
        local ok, fc = pcall(function() return item:getFluidContainer() end)
        hasFluidContainer = ok and fc ~= nil
    end

    local script = item.getScriptItem and item:getScriptItem() or nil
    local canStoreWater = safeCall(item, "isWaterSource")
        or safeCall(item, "canStoreWater")
        or safeCall(script, "isWaterSource")
        or safeCall(script, "canStoreWater")

    local looksLikeFluid = false
    local dcTokens = { "water", "liquid", "fluid", "drink", "beverage", "fuel", "gas" }
    for _, tok in ipairs(dcTokens) do
        if displayCategory:find(tok, 1, true) then
            looksLikeFluid = true
            break
        end
    end
    if not looksLikeFluid then
        local nameTokens = {
            "water", "bottle", "bucket", "jug", "mug", "cup", "canteen", "thermos",
            "kettle", "bowl", "barrel", "flask", "glass", "jar", "watering",
            "petrol", "gas", "fuel", "bleach", "wine", "beer", "propane",
        }
        for _, tok in ipairs(nameTokens) do
            if name:find(tok, 1, true) then
                looksLikeFluid = true
                break
            end
        end
    end

    if hasFluidContainer then
        return true
    end

    return isDrainable and (canStoreWater or looksLikeFluid)
end

-- Simple category resolver for stat display (mirrors Inspect_Descriptions logic)
local function InspectUI_parseCategoryPath(path)
    if not path or path == "" then return nil, nil end
    local slash = string.find(path, "/", 1, true)
    if not slash then
        return path, nil
    end

    local parent = string.sub(path, 1, slash - 1)
    local child = string.sub(path, slash + 1)
    if child == "" then child = nil end
    return parent, child
end

local function InspectUI_childExtends(base, candidate)
    if not candidate then return false end
    if not base then return true end
    if candidate == base then return false end
    if #candidate <= #base then return false end
    return candidate:sub(1, #base) == base and candidate:sub(#base + 1, #base + 1) == "/"
end

function InspectUI_resolveCategory(item)
    if not item then return nil, nil end

    local matchedParent, matchedChild = nil, nil

    for _, rule in ipairs(CategoryTree) do
        if rule and type(rule.match) == "function" then
            local ok, match = pcall(rule.match, item, nil)
            if ok and match then
                local parent, child = InspectUI_parseCategoryPath(rule.category)
                if parent then
                    if not matchedParent then
                        matchedParent, matchedChild = parent, child
                    elseif parent == matchedParent and InspectUI_childExtends(matchedChild, child) then
                        matchedChild = child
                    end
                end
            end
        end
    end

    return matchedParent, matchedChild
end

-- Simple helper to derive food freshness flags
function InspectUI_foodFlags(item)
    if not item or not instanceof(item, "Food") then
        return false, false, false, false
    end

    local age = item.getAge and item:getAge() or 0
    local off = 0

    if item.getOffAge then
        off = item:getOffAge() or 0
    end

    if off <= 0 then
        local script = item.getScriptItem and item:getScriptItem()
        if script and script.getOffAge then
            off = script:getOffAge() or 0
        end
    end

    local burnt  = (item.isBurnt and item:isBurnt()) or false
    local rotten = (item.isRotten and item:isRotten()) or (off > 0 and age >= off)

    local stale = false
    local fresh = false
    if off > 0 then
        stale = (not rotten) and age >= (off * 0.5)
        fresh = (not rotten) and (not stale)
    end

    return fresh, stale, rotten, burnt
end

--------------------------------------------------------
-- v3.5 - Dynamic ScriptItem Stats
--------------------------------------------------------
ItemInspection = ItemInspection or {}

local function FormatKeyValue(key, value)
    if not key or value == nil then return nil end

    local ignored = {
        DisplayCategory = true,
        IconsForMainMenu = true,
        WorldStaticModel = true,
        Tags = true,
        SoundParameter = true,
        ReplaceOnUse = true,
        ReplaceOnBreak = true,
        TwoHandWeapon = true,
        SwingSound = true,
        HitSound = true,
    }
    if ignored[key] then return nil end

    if key == "Insulation" then
        local pct = tonumber(value) and (tonumber(value) * 100) or value
        return string.format("Insulation: +%s%%", pct)
    end

    if key == "WindResistance" then
        return string.format("Wind Resistance: %s%%", tostring(value))
    end

    if key == "BiteDefense" then
        return string.format("Bite Defense: %s", tostring(value))
    end

    if key == "ScratchDefense" then
        return string.format("Scratch Defense: %s", tostring(value))
    end

    return tostring(key) .. ": " .. tostring(value)
end

-- NOTE:
-- BuildDynamicStats muestra propiedades dinamicas del ScriptItem,
-- excluyendo claves ignoradas. Este bloque NO debe modificarse.
local function appendDynamicStat(results, key, value)
    local line = FormatKeyValue(key, value)
    if line then
        table.insert(results, line)
    end
end

local function collectDynamicPropsFromLuaTable(props, results)
    for key, value in pairs(props) do
        appendDynamicStat(results, key, value)
    end
    return true
end

local function collectDynamicPropsFromJavaList(props, results)
    if not props or not props.size or not props.get then
        return false
    end

    local okSize, size = pcall(function() return props:size() end)
    if not okSize or not size or size <= 0 then
        return true
    end

    for i = 0, size - 1 do
        local okEntry, entry = pcall(function() return props:get(i) end)
        if okEntry and entry then
            local key, value = nil, nil
            if type(entry) == "table" then
                key = entry.key or entry[1]
                value = entry.value or entry[2]
            else
                local okKey, k = pcall(function() return entry.getKey and entry:getKey() end)
                local okVal, v = pcall(function() return entry.getValue and entry:getValue() end)
                if okKey then key = k end
                if okVal then value = v end
            end

            if key ~= nil then
                appendDynamicStat(results, key, value)
            end
        end
    end

    return true
end

local function collectDynamicPropsFromJavaMap(props, results)
    if not props or not props.entrySet then
        return false
    end

    local okEntries, entries = pcall(function() return props:entrySet() end)
    if not okEntries or not entries or not entries.iterator then
        return false
    end

    local okIterator, iterator = pcall(function() return entries:iterator() end)
    if not okIterator or not iterator then
        return false
    end

    while true do
        local okHasNext, hasNext = pcall(function() return iterator:hasNext() end)
        if not okHasNext or not hasNext then
            break
        end

        local okEntry, entry = pcall(function() return iterator:next() end)
        if okEntry and entry then
            local okKey, key = pcall(function() return entry.getKey and entry:getKey() end)
            local okVal, value = pcall(function() return entry.getValue and entry:getValue() end)
            if okKey and key ~= nil then
                appendDynamicStat(results, key, okVal and value or nil)
            end
        end
    end

    return true
end

function BuildDynamicStats(item)
    local script = item and item.getScriptItem and item:getScriptItem()
    if not script then return {} end

    local props = script.getAllProperties and script:getAllProperties()
    if not props then return {} end

    local results = {}
    local propType = type(props)

    if propType == "table" then
        collectDynamicPropsFromLuaTable(props, results)
        return results
    end

    -- B42 can return Java collections here; avoid pairs() on userdata.
    if collectDynamicPropsFromJavaMap(props, results) then
        return results
    end

    if collectDynamicPropsFromJavaList(props, results) then
        return results
    end

    return results
end

ItemInspection.BuildDynamicStats = BuildDynamicStats
ItemInspection.FormatKeyValue = FormatKeyValue
--========================================================
-- STAT_DEFS - modular registry
--========================================================
STAT_DEFS  = {}
STAT_ORDER = {}

local modules = {
    require "InspectSystem/Core/Stats/Stats_Base",
    require "InspectSystem/Core/Stats/Stats_Weapons",
    require "InspectSystem/Core/Stats/Stats_Weapons_Melee",
    require "InspectSystem/Core/Stats/Stats_Weapons_Ranged",
    require "InspectSystem/Core/Stats/Stats_Ammo",
    require "InspectSystem/Core/Stats/Stats_Fluid",
    require "InspectSystem/Core/Stats/Stats_Food",
    require "InspectSystem/Core/Stats/Stats_Clothing",
    require "InspectSystem/Core/Stats/Stats_Electronics",
    require "InspectSystem/Core/Stats/Stats_Books",
    require "InspectSystem/Core/Stats/Stats_Dynamic",
}

local seenOrder = {}

for _, mod in ipairs(modules) do
    if mod and mod.defs then
        for _, def in ipairs(mod.defs) do
            table.insert(STAT_DEFS, def)
            if def.id then
                STAT_DEFS[def.id] = def
            end
        end
    end

    if mod and mod.order then
        for _, id in ipairs(mod.order) do
            if not seenOrder[id] then
                table.insert(STAT_ORDER, id)
                seenOrder[id] = true
            end
        end
    end
end

--========================================================
-- Helper: construir mapa id -> stat
--========================================================
function InspectUI_statsById(stats)
    local map = {}
    if not stats then return map end
    for _, s in ipairs(stats) do
        if s.id then
            map[s.id] = s
        end
    end
    return map
end

return true



