-- TOG Tools — Mailbox Stale Watcher (engine)
-- Captures per-alt mailbox snapshots when the alt visits the mailbox, then
-- alerts on login when mail is approaching the 30-day auto-deletion.
--
-- Snapshot stamps absolute expiresAt = GetServerTime() + (daysLeft * 86400)
-- so days-remaining math stays accurate even when the alt does not re-visit
-- a mailbox for a while. Same content-derived-timestamp pattern TOGPM uses.
--
-- Storage:
--   db.global.mailbox = {
--       thresholdDays = 2,
--       snapshots = { [charKey] = { snapshotAt, items = {{...}} } },
--   }

local _, addon = ...

local Mailbox = {}
addon.modules["mailbox"] = Mailbox

-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------
local function CharKey()
    local realm = (GetRealmName() or ""):gsub("%s+", "")
    return (UnitName("player") or "?") .. "-" .. realm
end

local function MailDB()
    return addon.DB and addon.DB.global and addon.DB.global.mailbox
end

-- ---------------------------------------------------------------------------
-- Capture
-- ---------------------------------------------------------------------------
-- MAIL_INBOX_UPDATE fires repeatedly while the inbox loads. We just overwrite
-- on each event; the last call (after data settles) wins. Headers with nil
-- daysLeft are skipped — they represent rows still loading.
local function Capture()
    local db = MailDB()
    if not db then return end

    local n = GetInboxNumItems() or 0
    local now = GetServerTime()
    local items = {}
    for i = 1, n do
        local _, _, sender, subject, money, COD, daysLeft, itemCount = GetInboxHeaderInfo(i)
        if daysLeft and daysLeft > 0 then
            items[#items + 1] = {
                sender    = sender  or "?",
                subject   = subject or "",
                expiresAt = now + (daysLeft * 86400),
                hasItems  = (itemCount or 0) > 0,
                hasMoney  = (money or 0) > 0 or (COD or 0) > 0,
            }
        end
    end

    db.snapshots = db.snapshots or {}
    db.snapshots[CharKey()] = {
        snapshotAt = now,
        items      = items,
    }
end

-- ---------------------------------------------------------------------------
-- Read API — used by Login Digest + the tab
-- ---------------------------------------------------------------------------
-- Returns: totalCount, altCount, thresholdDays, perAlt = { [charKey] = count }
function Mailbox:GetExpiringSoon()
    local db = MailDB()
    if not db or not db.snapshots then
        return 0, 0, (db and db.thresholdDays) or 2, {}
    end
    local now = GetServerTime()
    local thresholdSec = (db.thresholdDays or 2) * 86400
    local total, altCount = 0, 0
    local perAlt = {}
    for charKey, snap in pairs(db.snapshots) do
        local count = 0
        for _, item in ipairs(snap.items or {}) do
            local left = item.expiresAt - now
            if left > 0 and left <= thresholdSec then
                count = count + 1
            end
        end
        if count > 0 then
            total      = total + count
            altCount   = altCount + 1
            perAlt[charKey] = count
        end
    end
    return total, altCount, db.thresholdDays or 2, perAlt
end

-- Returns sorted summaries for the tab.
-- Each entry: { charKey, snapshotAt, items = {expiringItemsSortedBySoonest},
--               allCount, expiringCount }
-- Order: alts with expiring mail first (soonest first), then alts without.
function Mailbox:GetAltSummaries()
    local db = MailDB()
    if not db or not db.snapshots then return {} end
    local now = GetServerTime()
    local thresholdSec = (db.thresholdDays or 2) * 86400

    local summaries = {}
    for charKey, snap in pairs(db.snapshots) do
        local expiring = {}
        for _, item in ipairs(snap.items or {}) do
            local left = item.expiresAt - now
            if left > 0 and left <= thresholdSec then
                expiring[#expiring + 1] = item
            end
        end
        table.sort(expiring, function(a, b) return a.expiresAt < b.expiresAt end)
        summaries[#summaries + 1] = {
            charKey       = charKey,
            snapshotAt    = snap.snapshotAt,
            items         = expiring,
            allCount      = #(snap.items or {}),
            expiringCount = #expiring,
        }
    end
    table.sort(summaries, function(a, b)
        if (a.expiringCount > 0) ~= (b.expiringCount > 0) then
            return a.expiringCount > 0
        end
        if a.expiringCount > 0 then
            return a.items[1].expiresAt < b.items[1].expiresAt
        end
        return a.charKey < b.charKey
    end)
    return summaries
end

-- ---------------------------------------------------------------------------
-- Login alert
-- ---------------------------------------------------------------------------
local function PrintLoginAlert()
    local total, altCount, thresholdDays = Mailbox:GetExpiringSoon()
    if total <= 0 then return end
    local plural    = (total == 1) and "" or "s"
    local altPlural = (altCount == 1) and "" or "s"
    print(string.format(
        "|c%s[TOGTools]|r %d mail%s expiring in <=%d day%s across %d alt%s. /togt mail for details.",
        addon.BrandColor or "ffFF8000",
        total, plural,
        thresholdDays, (thresholdDays == 1) and "" or "s",
        altCount, altPlural
    ))
end

-- ---------------------------------------------------------------------------
-- Event wiring
-- ---------------------------------------------------------------------------
local _frame = CreateFrame("Frame")
_frame:RegisterEvent("MAIL_INBOX_UPDATE")
_frame:RegisterEvent("PLAYER_LOGIN")
_frame:SetScript("OnEvent", function(_, event)
    if event == "MAIL_INBOX_UPDATE" then
        Capture()
        -- Live-refresh the Mailbox tab if it's currently showing, so the
        -- list reflects the new snapshot without forcing the user to tab
        -- away and back. MAIL_INBOX_UPDATE fires multiple times as the
        -- inbox loads; the redraw is cheap so we don't debounce.
        if addon.MainWindow and addon.MainWindow.activeTab == "mailbox" then
            addon.MainWindow:Refresh()
        end
    elseif event == "PLAYER_LOGIN" then
        -- Defer so the alert lands after Ace's "Loaded" line and other
        -- addons settle their login spam.
        C_Timer.After(2, PrintLoginAlert)
    end
end)

function Mailbox:OnEnable()
    -- Event wiring above runs at file load. Hook is here for module-pattern
    -- consistency in case OnEnable is needed later.
end
