-- Great Vault progress + weekly reset countdown.
-- Split file so its locals get a fresh Lua chunk (MythicMate.lua is near the
-- 200-locals-per-chunk limit) and so this data can be reused anywhere (minimap
-- tooltip today, a dedicated tab later) via the namespace.
--
-- All Blizzard API calls are guarded (type + pcall): a missing/renamed API must
-- degrade to "no data" instead of erroring — a single error can break the addon.

local MythicMateNS = _G.MythicMate
if not MythicMateNS then return end

local Vault = {}
MythicMateNS.Vault = Vault

------------------------------------------------------------
-- Weekly reset
------------------------------------------------------------
-- Seconds until the next weekly reset, or nil if the API is unavailable.
function Vault.GetWeeklyResetSeconds()
    if type(C_DateAndTime) == "table" and type(C_DateAndTime.GetSecondsUntilWeeklyReset) == "function" then
        local ok, secs = pcall(C_DateAndTime.GetSecondsUntilWeeklyReset)
        if ok and type(secs) == "number" and secs > 0 then return secs end
    end
    return nil
end

-- "3d 4h" style; falls back to Util.FormatTime when present.
local function fmtDuration(seconds)
    local util = MythicMateNS.Util
    if util and type(util.FormatTime) == "function" then
        local ok, v = pcall(util.FormatTime, seconds)
        if ok and v then return v end
    end
    seconds = tonumber(seconds) or 0
    local d = math.floor(seconds / 86400); seconds = seconds % 86400
    local h = math.floor(seconds / 3600); seconds = seconds % 3600
    local m = math.floor(seconds / 60)
    local parts = {}
    if d > 0 then parts[#parts + 1] = d .. "d" end
    if h > 0 then parts[#parts + 1] = h .. "h" end
    if m > 0 and d == 0 then parts[#parts + 1] = m .. "m" end
    return (#parts > 0) and table.concat(parts, " ") or "<1m"
end

------------------------------------------------------------
-- Great Vault (C_WeeklyRewards)
------------------------------------------------------------
-- Map a weekly-reward activity type to a human label. Enum values differ across
-- patches, so we resolve through Enum.WeeklyRewardChestThresholdType when it
-- exists and fall back to the historically-stable numeric values.
local function labelForType(t)
    local E = _G.Enum and _G.Enum.WeeklyRewardChestThresholdType
    if E then
        if t == E.Activities then return "Mythic+" end
        if t == E.Raid then return "Raid" end
        if E.World ~= nil and t == E.World then return "World" end
        if E.RankedPvP ~= nil and t == E.RankedPvP then return "PvP" end
    end
    -- Fallback for clients without the enum populated.
    if t == 1 then return "Mythic+" end
    if t == 3 then return "Raid" end
    if t == 6 then return "World" end
    if t == 2 then return "PvP" end
    return "Other"
end

-- Fixed display order so the tooltip is stable week to week.
local TYPE_ORDER = { "Mythic+", "Raid", "World", "PvP", "Other" }

-- Returns an array of vault rows:
--   { label = "Mythic+", progress = 5, threshold = 8, earned = 2, total = 3 }
-- or nil when the Great Vault API isn't available.
function Vault.GetGreatVaultRows()
    if type(C_WeeklyRewards) ~= "table" or type(C_WeeklyRewards.GetActivities) ~= "function" then
        return nil
    end
    local ok, activities = pcall(C_WeeklyRewards.GetActivities)
    if not ok or type(activities) ~= "table" then return nil end

    local byLabel = {}
    for _, a in ipairs(activities) do
        if type(a) == "table" then
            local label = labelForType(a.type)
            local row = byLabel[label]
            if not row then
                row = { label = label, progress = 0, threshold = 0, earned = 0, total = 0, thresholds = {} }
                byLabel[label] = row
            end
            row.total = row.total + 1
            local progress = tonumber(a.progress) or 0
            local threshold = tonumber(a.threshold) or 0
            if progress > row.progress then row.progress = progress end
            if threshold > row.threshold then row.threshold = threshold end
            if threshold > 0 then row.thresholds[#row.thresholds + 1] = threshold end
            if threshold > 0 and progress >= threshold then
                row.earned = row.earned + 1
            end
        end
    end
    -- Sort each category's thresholds ascending so "next slot" logic is stable.
    for _, row in pairs(byLabel) do
        table.sort(row.thresholds)
    end

    local rows = {}
    for _, label in ipairs(TYPE_ORDER) do
        local row = byLabel[label]
        if row and row.total > 0 then rows[#rows + 1] = row end
    end
    return (#rows > 0) and rows or nil
end

-- Actionable "what to run next" hints per Great Vault category. Each entry: { text, label }.
-- For each category we find the smallest threshold you haven't reached yet and report how
-- much more is needed to unlock the next slot. Categories with all slots earned are skipped.
function Vault.GetNextSlotHints(rows)
    rows = rows or Vault.GetGreatVaultRows()
    local hints = {}
    if type(rows) ~= "table" then return hints end
    for _, row in ipairs(rows) do
        if row.earned < row.total and type(row.thresholds) == "table" then
            local nextThreshold = nil
            for _, th in ipairs(row.thresholds) do
                if row.progress < th then nextThreshold = th; break end
            end
            if nextThreshold then
                local need = nextThreshold - row.progress
                local unit = (row.label == "Raid") and "boss(es)" or "run(s)"
                hints[#hints + 1] = {
                    label = row.label,
                    text = ("%s: %d more %s for slot %d"):format(row.label, need, unit, row.earned + 1),
                }
            end
        end
    end
    return hints
end

-- Colored summary lines for a tooltip. Each entry: { text, r, g, b }.
-- Includes the weekly reset countdown, one line per Great Vault category, and
-- "what to run next" hints.
function Vault.GetSummaryLines()
    local lines = {}

    local secs = Vault.GetWeeklyResetSeconds()
    if secs then
        lines[#lines + 1] = { text = "Weekly reset in " .. fmtDuration(secs), r = 0.8, g = 0.8, b = 0.9 }
    end

    local rows = Vault.GetGreatVaultRows()
    if rows then
        lines[#lines + 1] = { text = "Great Vault:", r = 1, g = 0.82, b = 0 }
        for _, row in ipairs(rows) do
            local r, g, b
            if row.earned >= row.total then
                r, g, b = 0.3, 1, 0.3       -- all slots earned
            elseif row.earned > 0 then
                r, g, b = 1, 0.82, 0        -- partial
            else
                r, g, b = 0.6, 0.6, 0.6     -- none yet
            end
            local text = ("  %s: %d/%d slots (%d/%d)"):format(
                row.label, row.earned, row.total, row.progress, row.threshold)
            lines[#lines + 1] = { text = text, r = r, g = g, b = b }
        end

        local hints = Vault.GetNextSlotHints(rows)
        if #hints > 0 then
            lines[#lines + 1] = { text = "Next slot:", r = 1, g = 0.82, b = 0 }
            for _, h in ipairs(hints) do
                lines[#lines + 1] = { text = "  " .. h.text, r = 0.6, g = 0.85, b = 1 }
            end
        end
    end

    return lines
end

-- Convenience: true if the player has an unclaimed Great Vault reward waiting.
function Vault.HasAvailableRewards()
    if type(C_WeeklyRewards) == "table" and type(C_WeeklyRewards.HasAvailableRewards) == "function" then
        local ok, v = pcall(C_WeeklyRewards.HasAvailableRewards)
        if ok then return v and true or false end
    end
    return false
end

------------------------------------------------------------
-- Weekly login reminder
------------------------------------------------------------
-- Print a chat reminder once per session, shortly after entering the world, ONLY when there
-- are unclaimed Great Vault rewards. Kept minimal (no spam): nothing prints on a normal login
-- with nothing to collect. Can be disabled via MythicMateDB.settings.vaultReminder = false.
local reminderShown = false
local function maybeShowReminder()
    if reminderShown then return end
    local st = MythicMateDB and MythicMateDB.settings
    if st and st.vaultReminder == false then return end
    -- C_WeeklyRewards data isn't always ready immediately after login.
    if not Vault.HasAvailableRewards() then return end
    reminderShown = true
    print("|cffc868ffMythicMate:|r your |cffffd000Great Vault|r has rewards waiting — don't forget to collect them!")
end

local reminderFrame = CreateFrame("Frame")
reminderFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
reminderFrame:RegisterEvent("WEEKLY_REWARDS_UPDATE")
reminderFrame:SetScript("OnEvent", function(_, event)
    if event == "PLAYER_ENTERING_WORLD" then
        -- Give the weekly-rewards API a moment to populate, then check.
        if C_Timer and type(C_Timer.After) == "function" then
            C_Timer.After(6, maybeShowReminder)
        else
            maybeShowReminder()
        end
    else
        maybeShowReminder()
    end
end)
