local _, ns = ...
local L = ns.L or {}
local DATA = ns.Data or {}
local DELVES_GREAT_VAULT_ITEM_LEVEL = DATA.DELVES_GREAT_VAULT_ITEM_LEVEL or {}

local function getWeeklyRewardLayout()
    local rewardType = Enum and Enum.WeeklyRewardChestThresholdType
    if not rewardType then
        return nil
    end

    return {
        {
            key = "raid",
            type = rewardType.Raid,
            label = L.SUBTAB_VAULT_RAIDS or "Raids",
        },
        {
            key = "dungeon",
            type = rewardType.Activities,
            label = L.SUBTAB_VAULT_DUNGEONS or "Dungeons",
        },
        {
            key = "world",
            type = rewardType.World,
            label = L.SUBTAB_VAULT_WORLD or "World / Delves / Prey",
        },
    }
end

local function sortActivitiesByIndex(a, b)
    return (tonumber(a and a.index) or 0) < (tonumber(b and b.index) or 0)
end

local Util = ns.Util or {}
local safeCopyTable = Util.SafeCopyTable
local getWeeklyResetInfo = Util.GetWeeklyResetInfo
local getDelimitedText = Util.GetDelimitedText
local formatDurationShort = Util.FormatDurationShort
local normalizeText = Util.NormalizeText
local containsPattern = Util.ContainsPattern
local stringContainsAnyPattern = Util.StringContainsAnyPattern

local WoWApi = ns.WoWApi or {}
local getCurrentRenownLevel = WoWApi.GetCurrentRenownLevel
local getMajorFactionNameByID = WoWApi.GetMajorFactionNameByID
local getSpellNameByID = WoWApi.GetSpellNameByID
local getMapNameByID = WoWApi.GetMapNameByID
local getActivePreyMapName = WoWApi.GetActivePreyMapName
local getCurrencyInfoByID = WoWApi.GetCurrencyInfoByID
local getItemCountByID = WoWApi.GetItemCountByID

local function getActivityRewardLinks(activityID)
    if not C_WeeklyRewards or type(C_WeeklyRewards.GetExampleRewardItemHyperlinks) ~= "function" then
        return nil, nil
    end

    local ok, rewardLink, upgradeRewardLink = pcall(C_WeeklyRewards.GetExampleRewardItemHyperlinks, activityID)
    if not ok then
        return nil, nil
    end

    if rewardLink == "" then
        rewardLink = nil
    end
    if upgradeRewardLink == "" then
        upgradeRewardLink = nil
    end

    return rewardLink, upgradeRewardLink
end

local function getActivityRewardLinkFromRewards(activity)
    if type(activity) ~= "table"
        or type(activity.rewards) ~= "table"
        or not C_WeeklyRewards
        or type(C_WeeklyRewards.GetItemHyperlink) ~= "function"
    then
        return nil
    end

    for _, reward in ipairs(activity.rewards) do
        local itemRewardType = Enum and Enum.CachedRewardType and Enum.CachedRewardType.Item or nil
        local isItemReward = reward and ((itemRewardType and reward.type == itemRewardType) or tonumber(reward.type) == 1)
        if isItemReward and reward.itemDBID then
            local ok, link = pcall(C_WeeklyRewards.GetItemHyperlink, reward.itemDBID)
            if ok and type(link) == "string" and link ~= "" then
                return link
            end
        end
    end

    return nil
end

local function getDetailedItemLevel(itemLink)
    if not itemLink or itemLink == "" or not C_Item or type(C_Item.GetDetailedItemLevelInfo) ~= "function" then
        return nil
    end

    local ok, itemLevel = pcall(C_Item.GetDetailedItemLevelInfo, itemLink)
    if ok and tonumber(itemLevel) and tonumber(itemLevel) > 0 then
        return tonumber(itemLevel)
    end

    return nil
end

local function getNextActivitiesIncreaseItemLevel(activityTierID, level)
    if not C_WeeklyRewards or type(C_WeeklyRewards.GetNextActivitiesIncrease) ~= "function" then
        return nil
    end

    activityTierID = tonumber(activityTierID)
    level = tonumber(level)
    if not activityTierID or activityTierID <= 0 or level == nil then
        return nil
    end

    local ok, hasSeasonData, _, nextLevel, itemLevel = pcall(C_WeeklyRewards.GetNextActivitiesIncrease, activityTierID, math.max(0, level))
    if ok and hasSeasonData and tonumber(itemLevel) and tonumber(itemLevel) > 0 then
        return tonumber(itemLevel), tonumber(nextLevel) or 0
    end

    return nil
end

local function getActivityTierItemLevel(activityTierID, level)
    level = tonumber(level) or 0
    if level <= 0 then
        return nil
    end

    local itemLevel, nextLevel = getNextActivitiesIncreaseItemLevel(activityTierID, level - 1)
    if itemLevel and (nextLevel <= 0 or nextLevel == level) then
        return itemLevel
    end

    return nil
end

local function getBestRewardLabel(activity, rewardItemLevel)
    local tier = tonumber(activity and activity.level) or 0
    if rewardItemLevel and tier > 0 then
        return string.format("Tier %d / %d ilvl", tier, rewardItemLevel)
    end
    if rewardItemLevel then
        return string.format("%d ilvl", rewardItemLevel)
    end
    if tier > 0 then
        return string.format("Tier %d", tier)
    end
    return nil
end

local function getActivityRewardData(rowKey, activity)
    if type(activity) ~= "table" then
        return nil, nil, nil, nil
    end

    local rewardLink, upgradeRewardLink = getActivityRewardLinks(activity.id)
    if not rewardLink then
        rewardLink = getActivityRewardLinkFromRewards(activity)
    end
    local rewardItemLevel = getDetailedItemLevel(rewardLink)
    local upgradeItemLevel = getDetailedItemLevel(upgradeRewardLink)
    local activityTierID = tonumber(activity.activityTierID) or 0
    local activityLevel = tonumber(activity.level) or 0

    if not rewardItemLevel then
        rewardItemLevel = getActivityTierItemLevel(activityTierID, activityLevel)
    end

    if not upgradeItemLevel then
        upgradeItemLevel = getNextActivitiesIncreaseItemLevel(activityTierID, activityLevel)
    end

    if rowKey == "world" and not rewardItemLevel then
        rewardItemLevel = DELVES_GREAT_VAULT_ITEM_LEVEL[activityLevel]
    end

    if rowKey == "world" and not upgradeItemLevel then
        upgradeItemLevel = DELVES_GREAT_VAULT_ITEM_LEVEL[activityLevel + 1]
    end

    return rewardItemLevel, rewardLink, upgradeItemLevel, upgradeRewardLink
end

local function buildVaultGoalSlots(displayedCount, slots)
    local goalSlots = {}
    if type(slots) ~= "table" then
        return goalSlots
    end

    for index, slot in ipairs(slots) do
        local threshold = tonumber(slot and slot.threshold) or 0
        local slotProgress = threshold > 0 and math.min(displayedCount, threshold) or 0
        local remaining = threshold > 0 and math.max(0, threshold - displayedCount) or 0

        goalSlots[index] = {
            threshold = threshold,
            count = slotProgress,
            displayText = (threshold <= 0) and "--"
                or ((remaining <= 0) and (L.VAULT_GOAL_REACHED or "Reached")
                or getDelimitedText(L.VAULT_GOAL_LEFT or "%d left", remaining)),
            countText = string.format("%d/%d", slotProgress, threshold),
        }
    end

    return goalSlots
end

local function getVaultGoalTargetItemLevel(rowKey, slots)
    if type(slots) ~= "table" then
        return 0
    end

    local targetItemLevel = 0
    for _, slot in ipairs(slots) do
        local rewardItemLevel = math.max(0, tonumber(slot and slot.rewardItemLevel) or 0)
        targetItemLevel = math.max(targetItemLevel, rewardItemLevel)
    end

    if rowKey == "world" then
        targetItemLevel = math.max(targetItemLevel, tonumber(DELVES_GREAT_VAULT_ITEM_LEVEL[8]) or 0)
    end

    return targetItemLevel
end

local function getFirstWorldMaxRewardLevel()
    local maxItemLevel = 0
    for _, itemLevel in pairs(DELVES_GREAT_VAULT_ITEM_LEVEL) do
        maxItemLevel = math.max(maxItemLevel, tonumber(itemLevel) or 0)
    end

    if maxItemLevel <= 0 then
        return nil
    end

    local maxRewardLevel
    for level, itemLevel in pairs(DELVES_GREAT_VAULT_ITEM_LEVEL) do
        level = tonumber(level)
        itemLevel = tonumber(itemLevel) or 0
        if level and itemLevel >= maxItemLevel and ((not maxRewardLevel) or level < maxRewardLevel) then
            maxRewardLevel = level
        end
    end

    return maxRewardLevel
end

local function getWorldSortedMaxRewardProgress(rewardType, maxThreshold)
    if not C_WeeklyRewards or type(C_WeeklyRewards.GetSortedProgressForActivity) ~= "function" then
        return nil
    end

    local maxRewardLevel = getFirstWorldMaxRewardLevel()
    if not maxRewardLevel then
        return nil
    end

    local ok, progressEntries = pcall(C_WeeklyRewards.GetSortedProgressForActivity, rewardType, true)
    if not ok or type(progressEntries) ~= "table" then
        return nil
    end

    local progress = 0
    for _, entry in ipairs(progressEntries) do
        local difficulty = tonumber(entry and entry.difficulty)
        local points = math.max(0, tonumber(entry and entry.numPoints) or 0)
        if difficulty and difficulty >= maxRewardLevel then
            progress = progress + points
        end
    end

    maxThreshold = math.max(0, tonumber(maxThreshold) or 0)
    if maxThreshold > 0 then
        progress = math.min(progress, maxThreshold)
    end
    return math.max(0, progress)
end

local function buildVaultGoalProgress(rowKey, slots, fallbackDisplayedCount, rewardType)
    local maxThreshold = tonumber(type(slots) == "table" and slots[#slots] and slots[#slots].threshold) or 0
    local targetItemLevel = getVaultGoalTargetItemLevel(rowKey, slots)
    local goalDisplayCount = 0

    local sortedGoalProgress = rowKey == "world" and getWorldSortedMaxRewardProgress(rewardType, maxThreshold) or nil
    if sortedGoalProgress ~= nil then
        goalDisplayCount = sortedGoalProgress
    elseif type(slots) == "table" and targetItemLevel > 0 then
        for _, slot in ipairs(slots) do
            local rewardItemLevel = math.max(0, tonumber(slot and slot.rewardItemLevel) or 0)
            local threshold = math.max(0, tonumber(slot and slot.threshold) or 0)
            if rewardItemLevel >= targetItemLevel and threshold > goalDisplayCount then
                goalDisplayCount = threshold
            end
        end
    elseif maxThreshold > 0 then
        goalDisplayCount = math.min(math.max(0, tonumber(fallbackDisplayedCount) or 0), maxThreshold)
    else
        goalDisplayCount = math.max(0, tonumber(fallbackDisplayedCount) or 0)
    end

    local missingToNext = 0
    if type(slots) == "table" then
        for _, slot in ipairs(slots) do
            local threshold = math.max(0, tonumber(slot and slot.threshold) or 0)
            if goalDisplayCount < threshold then
                missingToNext = threshold - goalDisplayCount
                break
            end
        end
    end

    local missingToMax = maxThreshold > 0 and math.max(0, maxThreshold - goalDisplayCount) or 0
    return goalDisplayCount, missingToNext, missingToMax, targetItemLevel
end

local function buildVaultRowNote(missingToNext, missingToMax, maxThreshold)
    if maxThreshold <= 0 then
        return L.VAULT_ROW_NOTE_NONE or "No reward thresholds found yet."
    end

    if missingToMax <= 0 then
        return L.VAULT_ROW_NOTE_READY or "All rewards unlocked for this week."
    end

    if missingToNext > 0 then
        return string.format(L.VAULT_ROW_NOTE_PROGRESS or "%d left to the next reward, %d left to max.", missingToNext, missingToMax)
    end

    return string.format(L.VAULT_ROW_NOTE_MAX_ONLY or "%d left to max reward.", missingToMax)
end

local Vault = ns.Vault or {}
ns.Vault = Vault

Vault.GetWeeklyRewardLayout = getWeeklyRewardLayout
Vault.SortActivitiesByIndex = sortActivitiesByIndex
Vault.GetActivityRewardData = getActivityRewardData
Vault.GetBestRewardLabel = getBestRewardLabel
Vault.BuildVaultGoalSlots = buildVaultGoalSlots
Vault.BuildVaultGoalProgress = buildVaultGoalProgress
Vault.BuildVaultRowNote = buildVaultRowNote
