local _, ns = ...
local L = ns.L or {}

local Util = ns.Util or {}
local safeCopyTable = Util.SafeCopyTable
local getWeeklyResetInfo = Util.GetWeeklyResetInfo
local getDelimitedText = Util.GetDelimitedText
local formatDurationShort = Util.FormatDurationShort

local Vault = ns.Vault or {}
local getWeeklyRewardLayout = Vault.GetWeeklyRewardLayout
local sortActivitiesByIndex = Vault.SortActivitiesByIndex
local getActivityRewardData = Vault.GetActivityRewardData
local getBestRewardLabel = Vault.GetBestRewardLabel
local buildVaultGoalSlots = Vault.BuildVaultGoalSlots
local buildVaultGoalProgress = Vault.BuildVaultGoalProgress
local buildVaultRowNote = Vault.BuildVaultRowNote

function ns:BuildCurrentVaultSnapshot(existingSnapshot)
    local layout = getWeeklyRewardLayout()
    if not layout or not C_WeeklyRewards or type(C_WeeklyRewards.GetActivities) ~= "function" then
        return nil
    end

    local secondsUntilReset, resetAt = getWeeklyResetInfo()
    local snapshot = {
        generatedAt = time(),
        resetAt = resetAt,
        rows = {},
        summary = {},
    }

    local existingRows = type(existingSnapshot) == "table" and type(existingSnapshot.rows) == "table" and existingSnapshot.rows or nil
    local hasAnyRow = false

    for _, rowInfo in ipairs(layout) do
        local ok, activities = pcall(C_WeeklyRewards.GetActivities, rowInfo.type)
        if ok and type(activities) == "table" and #activities > 0 then
            table.sort(activities, sortActivitiesByIndex)

            local row = {
                key = rowInfo.key,
                label = rowInfo.label,
                thresholds = {},
                slots = {},
                completedCount = 0,
                advice = {},
            }

            local bestItemLevel
            local bestLabel

            for index = 1, math.min(#activities, 3) do
                local activity = activities[index]
                local threshold = math.max(0, tonumber(activity and activity.threshold) or 0)
                local progress = math.max(0, tonumber(activity and activity.progress) or 0)
                local rewardItemLevel, rewardLink, upgradeItemLevel, upgradeRewardLink = getActivityRewardData(rowInfo.key, activity)
                local rewardLabel = getBestRewardLabel(activity, rewardItemLevel)

                row.thresholds[#row.thresholds + 1] = threshold
                row.completedCount = math.max(row.completedCount, progress)
                row.slots[index] = {
                    index = tonumber(activity and activity.index) or index,
                    threshold = threshold,
                    progress = progress,
                    level = tonumber(activity and activity.level) or 0,
                    activityTierID = tonumber(activity and activity.activityTierID) or 0,
                    rewardItemLevel = rewardItemLevel,
                    rewardLink = rewardLink,
                    upgradeItemLevel = upgradeItemLevel,
                    upgradeRewardLink = upgradeRewardLink,
                    rewardLabel = rewardLabel,
                }

                if rewardItemLevel and ((not bestItemLevel) or rewardItemLevel > bestItemLevel) then
                    bestItemLevel = rewardItemLevel
                    bestLabel = rewardLabel
                elseif not bestLabel and rewardLabel then
                    bestLabel = rewardLabel
                end
            end

            local maxThreshold = tonumber(row.thresholds[#row.thresholds]) or 0
            local displayedCount = maxThreshold > 0 and math.min(row.completedCount, maxThreshold) or row.completedCount
            local missingToNext = 0

            for _, threshold in ipairs(row.thresholds) do
                if displayedCount < threshold then
                    missingToNext = threshold - displayedCount
                    break
                end
            end

            local goalDisplayCount, goalMissingToNext, goalMissingToMax, goalTargetItemLevel = buildVaultGoalProgress(rowInfo.key, row.slots, displayedCount, rowInfo.type)

            row.advice.bestLabel = bestLabel
            row.note = buildVaultRowNote(missingToNext, goalMissingToMax, maxThreshold)
            row.goal = {
                label = L.VAULT_GOAL_LABEL or "Max reward",
                displayCount = goalDisplayCount,
                maxThreshold = maxThreshold,
                displayCountText = string.format("%d/%d", goalDisplayCount, maxThreshold),
                displayText = (maxThreshold <= 0) and "--"
                    or ((goalMissingToMax <= 0) and (L.VAULT_GOAL_REACHED or "Reached")
                    or getDelimitedText(L.VAULT_MAX_REWARD_LEFT or "%d left to max reward", goalMissingToMax)),
                slots = buildVaultGoalSlots(goalDisplayCount, row.slots),
                missingToNext = goalMissingToNext,
                missingToMax = goalMissingToMax,
                targetItemLevel = goalTargetItemLevel,
            }

            snapshot.rows[rowInfo.key] = row
            hasAnyRow = true
        elseif existingRows and existingRows[rowInfo.key] then
            snapshot.rows[rowInfo.key] = existingRows[rowInfo.key]
            hasAnyRow = true
        end
    end

    if not hasAnyRow then
        return nil
    end

    snapshot.summary.headline = string.format(
        L.VAULT_SUMMARY_RESET or "Tracks the live Great Vault and resets in %s.",
        formatDurationShort(secondsUntilReset)
    )

    return snapshot
end

function ns:RefreshCurrentCharacterVaultData()
    local record = self:RefreshCurrentCharacterRecord()
    self:ClearExpiredVaultSnapshot(record)

    local existingSnapshot = type(record.vault) == "table" and record.vault.snapshot or nil
    local snapshot = self:BuildCurrentVaultSnapshot(existingSnapshot)
    if not snapshot then
        return nil
    end

    record.vault = {
        resetAt = tonumber(snapshot.resetAt) or select(2, getWeeklyResetInfo()),
        snapshot = snapshot,
    }

    return snapshot
end

function ns:GetLiveVaultSnapshot(keyOrCharacter)
    local key = keyOrCharacter
    if type(keyOrCharacter) == "table" then
        key = keyOrCharacter.key
    end

    if self:IsCurrentCharacterKey(key) then
        local liveSnapshot = self:RefreshCurrentCharacterVaultData()
        if liveSnapshot then
            return liveSnapshot
        end

        self:ScheduleLiveDataRefresh(1.0, self:BuildLiveRefreshSpec("vault"))
    end

    local storedSnapshot = self:GetStoredVaultSnapshot(key)

    local character = self:GetLiveVaultloomCharacterRecord(key)
    local baseSnapshot
    if type(character) == "table" then
        local currentWeek = character.currentWeek
        if type(currentWeek) == "string" and currentWeek ~= "" then
            local weeklyRecord = type(character.weekly) == "table" and character.weekly[currentWeek] or nil
            baseSnapshot = type(weeklyRecord) == "table" and weeklyRecord.snapshot or nil
        end
    end

    if not storedSnapshot then
        return baseSnapshot
    end

    if type(baseSnapshot) ~= "table" then
        return storedSnapshot
    end

    local merged = safeCopyTable(baseSnapshot)
    local mergedRows = safeCopyTable(baseSnapshot.rows)
    for rowKey, rowData in pairs(storedSnapshot.rows or {}) do
        mergedRows[rowKey] = rowData
    end

    merged.rows = mergedRows
    merged.summary = type(storedSnapshot.summary) == "table" and storedSnapshot.summary or merged.summary
    merged.generatedAt = storedSnapshot.generatedAt or merged.generatedAt
    merged.resetAt = storedSnapshot.resetAt or merged.resetAt

    return merged
end
