local addonName, NS = ...

-- ============================================================================
-- VAULTSKINS: REWARD RARITY
-- ============================================================================

local RewardRarity = {}
NS.RewardRarity = RewardRarity

local activityCache = setmetatable({}, { __mode = "k" })
local analysisDirty = true
local analyzedActivityCount = 0

-- ======================== CACHE AND COLOR CONSTANTS =========================
local function ClearActivityCache()
    for activity in pairs(activityCache) do
        activityCache[activity] = nil
    end
end
local lastSummary

local ITEM_QUALITY_EPIC = Enum and Enum.ItemQuality and Enum.ItemQuality.Epic or 4
local ITEM_QUALITY_LEGENDARY = Enum and Enum.ItemQuality and Enum.ItemQuality.Legendary or 5
local CACHED_REWARD_ITEM = Enum and Enum.CachedRewardType and Enum.CachedRewardType.Item or 1

local TIER_COLOR = { r = 1.00, g = 0.78, b = 0.28, a = 1 }
local MOUNT_COLOR = { r = 0.34, g = 0.88, b = 1.00, a = 1 }
local RARE_COLOR = { r = 1.00, g = 0.48, b = 0.16, a = 1 }

-- ============================ SAFE ITEM HELPERS =============================
local function IsSecret(value)
    return type(issecretvalue) == "function" and issecretvalue(value)
end

local function SafeNumber(value, fallback)
    if value == nil or IsSecret(value) then return fallback end
    value = tonumber(value)
    return value or fallback
end

local function SafeBoolean(value)
    if value == nil or IsSecret(value) then return false end
    return value and true or false
end

local function IsKeystone(itemID)
    if not itemID or not C_Item or type(C_Item.IsItemKeystoneByID) ~= "function" then return false end
    local ok, result = pcall(C_Item.IsItemKeystoneByID, itemID)
    return ok and SafeBoolean(result) or false
end

local function IsItemReward(rewardInfo)
    return type(rewardInfo) == "table" and rewardInfo.type == CACHED_REWARD_ITEM and rewardInfo.id and not IsSecret(rewardInfo.id)
end

local function GetItemQualityColorSafe(quality)
    if type(GetItemQualityColor) == "function" and quality then
        local ok, r, g, b = pcall(GetItemQualityColor, quality)
        if ok and r and not IsSecret(r) and not IsSecret(g) and not IsSecret(b) then
            return { r = r, g = g, b = b, a = 1 }
        end
    end
end

local function GetDisplayedItemDBID(activity)
    if not activity then return nil end
    local itemDBID
    if type(activity.GetDisplayedItemDBID) == "function" then
        local ok, value = pcall(activity.GetDisplayedItemDBID, activity)
        if ok then itemDBID = value end
    end
    if not itemDBID and activity.ItemFrame then
        itemDBID = activity.ItemFrame.displayedItemDBID
    end
    if IsSecret(itemDBID) then return nil end
    return itemDBID
end

local function GetHyperlink(itemDBID)
    if not itemDBID or not C_WeeklyRewards or type(C_WeeklyRewards.GetItemHyperlink) ~= "function" then return nil end
    local ok, hyperlink = pcall(C_WeeklyRewards.GetItemHyperlink, itemDBID)
    if not ok or IsSecret(hyperlink) then return nil end
    return hyperlink
end

local function GetItemIDFromLink(hyperlink)
    if not hyperlink or not C_Item or type(C_Item.GetItemInfoInstant) ~= "function" then return nil end
    local ok, itemID = pcall(C_Item.GetItemInfoInstant, hyperlink)
    if not ok or IsSecret(itemID) then return nil end
    return tonumber(itemID)
end

local function SelectFallbackReward(activity)
    local info = activity and activity.info
    if type(info) ~= "table" or type(info.rewards) ~= "table" then return nil end

    local bestReward, bestQuality, bestLevel = nil, -1, -1
    for _, rewardInfo in ipairs(info.rewards) do
        if IsItemReward(rewardInfo) and not IsKeystone(rewardInfo.id) then
            local quality, level = 0, 0
            if C_Item and type(C_Item.GetItemInfo) == "function" then
                local ok, _, _, itemQuality, itemLevel = pcall(C_Item.GetItemInfo, rewardInfo.id)
                if ok then
                    quality = SafeNumber(itemQuality, 0)
                    level = SafeNumber(itemLevel, 0)
                end
            end
            if not bestReward or quality > bestQuality or (quality == bestQuality and level > bestLevel) then
                bestReward, bestQuality, bestLevel = rewardInfo, quality, level
            end
        end
    end
    return bestReward
end

local function FindDisplayedReward(activity, itemDBID)
    local info = activity and activity.info
    if type(info) ~= "table" or type(info.rewards) ~= "table" then return nil end
    if itemDBID then
        for _, rewardInfo in ipairs(info.rewards) do
            if IsItemReward(rewardInfo) and rewardInfo.itemDBID == itemDBID then
                return rewardInfo
            end
        end
    end
    return SelectFallbackReward(activity)
end

local function GetEquippedAverageItemLevel()
    if type(GetAverageItemLevel) ~= "function" then return 0 end
    local ok, overall, equipped = pcall(GetAverageItemLevel)
    if not ok then return 0 end
    equipped = SafeNumber(equipped, nil)
    overall = SafeNumber(overall, 0)
    return equipped or overall or 0
end

-- ============================ ACTIVITY ANALYSIS =============================
local function AnalyzeActivityRaw(activity)
    local profile = {
        hasReward = activity and activity.hasRewards and true or false,
        itemLevel = 0,
        quality = 0,
        levelScore = 0.5,
        glowMultiplier = 1,
        importance = 0,
    }
    if not profile.hasReward then return profile end

    local itemDBID = GetDisplayedItemDBID(activity)
    local hyperlink = GetHyperlink(itemDBID)
    local rewardInfo = FindDisplayedReward(activity, itemDBID)
    local itemID = GetItemIDFromLink(hyperlink) or (rewardInfo and tonumber(rewardInfo.id))

    profile.itemDBID = itemDBID
    profile.hyperlink = hyperlink
    profile.rewardInfo = rewardInfo
    profile.itemID = itemID

    if not itemID or IsKeystone(itemID) then
        profile.importance = 1
        return profile
    end

    local itemName, itemLink, itemQuality, baseItemLevel, equipLocation, icon, setID
    if C_Item and type(C_Item.GetItemInfo) == "function" then
        local ok, nameValue, linkValue, qualityValue, levelValue, _, _, _, _, equipValue, iconValue, _, _, _, _, _, setValue = pcall(C_Item.GetItemInfo, itemID)
        if ok then
            itemName = nameValue
            itemLink = linkValue
            itemQuality = qualityValue
            baseItemLevel = levelValue
            equipLocation = equipValue
            icon = iconValue
            setID = setValue
        end
    end

    profile.name = itemName
    profile.hyperlink = profile.hyperlink or itemLink
    profile.quality = SafeNumber(itemQuality, 0)
    profile.baseItemLevel = SafeNumber(baseItemLevel, 0)
    profile.equipLocation = equipLocation
    profile.icon = icon
    profile.setID = SafeNumber(setID, nil)

    if C_Item and type(C_Item.GetDetailedItemLevelInfo) == "function" then
        local ok, detailedLevel = pcall(C_Item.GetDetailedItemLevelInfo, profile.hyperlink or itemID)
        if ok then profile.itemLevel = SafeNumber(detailedLevel, profile.baseItemLevel) end
    end
    if profile.itemLevel <= 0 then profile.itemLevel = profile.baseItemLevel end

    if C_Item and type(C_Item.IsEquippableItem) == "function" then
        local ok, result = pcall(C_Item.IsEquippableItem, itemID)
        profile.isEquippable = ok and SafeBoolean(result) or false
    end

    if profile.setID and C_Item and type(C_Item.IsItemSpecificToPlayerClass) == "function" then
        local ok, result = pcall(C_Item.IsItemSpecificToPlayerClass, itemID)
        profile.isTier = ok and SafeBoolean(result) or false
    end

    if C_MountJournal and type(C_MountJournal.GetMountFromItem) == "function" then
        local ok, mountID = pcall(C_MountJournal.GetMountFromItem, itemID)
        if ok and mountID and not IsSecret(mountID) then
            profile.mountID = mountID
            profile.isMount = true
        end
    end

    profile.isLegendary = profile.quality >= ITEM_QUALITY_LEGENDARY
    profile.isRareNonGear = not profile.isEquippable and profile.quality >= ITEM_QUALITY_EPIC
    profile.isRare = profile.isMount or profile.isLegendary or profile.isRareNonGear

    if profile.isMount then
        profile.effectColor = MOUNT_COLOR
        profile.effectKind = "mount"
        profile.importance = 4
    elseif profile.isRare then
        profile.effectColor = GetItemQualityColorSafe(profile.quality) or RARE_COLOR
        profile.effectKind = "rare"
        profile.importance = 4
    elseif profile.isTier then
        profile.effectColor = TIER_COLOR
        profile.effectKind = "tier"
        profile.importance = 3
    else
        profile.effectColor = GetItemQualityColorSafe(profile.quality)
        profile.effectKind = "standard"
        profile.importance = 1
    end

    return profile
end

-- ============================ PUBLIC RARITY API =============================
function RewardRarity:Reset()
    ClearActivityCache()
    lastSummary = nil
    analysisDirty = true
    analyzedActivityCount = 0
end

function RewardRarity:Invalidate(activity)
    if activity then activityCache[activity] = nil end
    analysisDirty = true
end

function RewardRarity:GetProfile(activity)
    if not activity then return nil end
    local profile = activityCache[activity]
    if not profile then
        profile = AnalyzeActivityRaw(activity)
        activityCache[activity] = profile
    end
    return profile
end

function RewardRarity:AnalyzeActivities(activities)
    local activityCount = activities and #activities or 0
    if not analysisDirty and lastSummary and activityCount == analyzedActivityCount then
        local complete = true
        for index = 1, activityCount do
            if not activityCache[activities[index]] then
                complete = false
                break
            end
        end
        if complete then
            return lastSummary
        end
    end

    ClearActivityCache()

    local summary = {
        rewardCount = 0,
        tierCount = 0,
        rareCount = 0,
        mountCount = 0,
        minItemLevel = nil,
        maxItemLevel = nil,
        equippedItemLevel = GetEquippedAverageItemLevel(),
        maxImportance = 0,
        jackpotLevel = 0,
    }

    for _, activity in ipairs(activities or {}) do
        local profile = AnalyzeActivityRaw(activity)
        activityCache[activity] = profile
        if profile.hasReward then
            summary.rewardCount = summary.rewardCount + 1
            if profile.isTier then summary.tierCount = summary.tierCount + 1 end
            if profile.isRare then summary.rareCount = summary.rareCount + 1 end
            if profile.isMount then summary.mountCount = summary.mountCount + 1 end
            if profile.itemLevel and profile.itemLevel > 0 then
                summary.minItemLevel = summary.minItemLevel and math.min(summary.minItemLevel, profile.itemLevel) or profile.itemLevel
                summary.maxItemLevel = summary.maxItemLevel and math.max(summary.maxItemLevel, profile.itemLevel) or profile.itemLevel
            end
        end
    end

    local levelRange = summary.minItemLevel and summary.maxItemLevel and (summary.maxItemLevel - summary.minItemLevel) or 0
    for _, activity in ipairs(activities or {}) do
        local profile = activityCache[activity]
        if profile and profile.hasReward then
            local relativeScore = 0.5
            if profile.itemLevel and profile.itemLevel > 0 and levelRange > 0 then
                relativeScore = (profile.itemLevel - summary.minItemLevel) / levelRange
            elseif profile.itemLevel and profile.itemLevel > 0 and summary.rewardCount == 1 then
                relativeScore = 0.72
            end

            local upgradeScore = 0.5
            if profile.itemLevel and profile.itemLevel > 0 and summary.equippedItemLevel > 0 then
                local delta = profile.itemLevel - summary.equippedItemLevel
                upgradeScore = math.max(0, math.min(1, (delta + 8) / 28))
                profile.upgradeDelta = delta
            end

            profile.levelScore = math.max(0, math.min(1, relativeScore * 0.68 + upgradeScore * 0.32))
            profile.isHighestItemLevel = summary.maxItemLevel and profile.itemLevel == summary.maxItemLevel and summary.rewardCount > 1 or false
            profile.glowMultiplier = 0.78 + (profile.levelScore * 0.88)
            if profile.isHighestItemLevel then
                profile.glowMultiplier = profile.glowMultiplier + 0.14
                profile.importance = math.max(profile.importance, 2)
            end
            if profile.isTier then profile.glowMultiplier = math.max(profile.glowMultiplier, 1.42) end
            if profile.isRare then profile.glowMultiplier = math.max(profile.glowMultiplier, 1.58) end
            profile.glowMultiplier = math.min(1.82, profile.glowMultiplier)
            summary.maxImportance = math.max(summary.maxImportance, profile.importance or 0)
        end
    end

    if summary.rewardCount >= 7 then
        summary.jackpotLevel = 3
    elseif summary.rewardCount >= 5 then
        summary.jackpotLevel = 2
    elseif summary.rewardCount >= 3 then
        summary.jackpotLevel = 1
    end

    lastSummary = summary
    analysisDirty = false
    analyzedActivityCount = activityCount
    return summary
end

-- ======================== SUMMARY AND AUDIO ROUTING =========================
function RewardRarity:GetSummary()
    return lastSummary
end

function RewardRarity:GetSoundKind(profile)
    if not profile or not profile.hasReward then return "reward" end
    if profile.isMount or profile.isRare then return "rewardRare" end
    if profile.isTier then return "rewardTier" end
    if profile.importance and profile.importance >= 2 then return "rewardImportant" end
    return "reward"
end
