-- TOG Tools — Login Digest (engine)
-- Prints a single-line state-of-account summary at PLAYER_LOGIN.
--
-- Fields (each toggleable in the tab; suppressed when 0/empty/N/A):
--   mail        — mail expiring soon across all alts (reads addon.modules.mailbox:GetExpiringSoon)
--   guildOnline — guild members currently online
--   hearth      — current char's hearthstone bind location
--   bags        — current char's bag-slot occupancy percentage
--   lastLogout  — how long since this character last logged out (per-char data)
--
-- Last logout is stamped on PLAYER_LOGOUT for the current character into
-- db.global.loginDigest.lastLogout[Name-Realm], read at login time.

local _, addon = ...

local LoginDigest = {}
addon.modules["logindigest"] = LoginDigest

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

local function DigestDB()
    return addon.DB and addon.DB.global and addon.DB.global.loginDigest
end

-- C_Container is the Retail 10+ namespace for bag APIs. Classic still uses
-- the unprefixed globals. Either path returns the same numbers.
local function BagSlots(bag)
    if C_Container and C_Container.GetContainerNumSlots then
        return C_Container.GetContainerNumSlots(bag) or 0
    end
    return (GetContainerNumSlots and GetContainerNumSlots(bag)) or 0
end

local function BagFree(bag)
    if C_Container and C_Container.GetContainerNumFreeSlots then
        return C_Container.GetContainerNumFreeSlots(bag) or 0
    end
    return (GetContainerNumFreeSlots and GetContainerNumFreeSlots(bag)) or 0
end

local function FormatDuration(seconds)
    if not seconds or seconds < 0 then return nil end
    if seconds < 3600  then return string.format("%dm", math.floor(seconds / 60))   end
    if seconds < 86400 then return string.format("%dh", math.floor(seconds / 3600)) end
    return string.format("%dd", math.floor(seconds / 86400))
end

-- ---------------------------------------------------------------------------
-- Field builders — each returns (text, true) on success, or (nil, false) to
-- suppress the field for this print.
-- ---------------------------------------------------------------------------
local fieldBuilders = {}

function fieldBuilders.mail()
    local Mailbox = addon.modules and addon.modules["mailbox"]
    if not (Mailbox and Mailbox.GetExpiringSoon) then return nil, false end
    local total, _, threshold = Mailbox:GetExpiringSoon()
    if total <= 0 then return nil, false end
    return string.format("|cffffaa55Mail:|r %d in <=%dd", total, threshold), true
end

function fieldBuilders.guildOnline()
    if not IsInGuild or not IsInGuild() then return nil, false end
    local total = (GetNumGuildMembers and GetNumGuildMembers()) or 0
    if total <= 0 then return nil, false end
    local online = 0
    for i = 1, total do
        local _, _, _, _, _, _, _, _, isOnline = GetGuildRosterInfo(i)
        if isOnline then online = online + 1 end
    end
    if online <= 0 then return nil, false end
    return string.format("|cff44ee44Guild:|r %d online", online), true
end

function fieldBuilders.hearth()
    local loc = GetBindLocation and GetBindLocation()
    if not loc or loc == "" then return nil, false end
    return string.format("|cffaaaaffHearth:|r %s", loc), true
end

function fieldBuilders.bags()
    local total, free = 0, 0
    -- Slots 0..4 = backpack + 4 carried bags. Bank slots are excluded;
    -- they're not relevant to the "do I need to clean up" signal at login.
    for bag = 0, 4 do
        total = total + BagSlots(bag)
        free  = free  + BagFree(bag)
    end
    if total <= 0 then return nil, false end
    local pct = math.floor(((total - free) / total) * 100 + 0.5)
    return string.format("|cffffcc66Bags:|r %d%% full", pct), true
end

function fieldBuilders.lastLogout()
    local db = DigestDB()
    if not (db and db.lastLogout) then return nil, false end
    local last = db.lastLogout[CharKey()]
    if not last then return nil, false end
    local delta = GetServerTime() - last
    local txt   = FormatDuration(delta)
    if not txt then return nil, false end
    return string.format("|cff999999Last logout:|r %s ago", txt), true
end

-- Field render order. Mail first because it's actionable (you can lose
-- mail to expiration). Last logout last because it's curiosity, not action.
local FIELD_ORDER = { "mail", "guildOnline", "hearth", "bags", "lastLogout" }

-- ---------------------------------------------------------------------------
-- Print
-- ---------------------------------------------------------------------------
function LoginDigest:Print()
    local db = DigestDB()
    if not (db and db.enabled) then return end

    local fields = db.fields or {}
    local parts = {}
    for _, key in ipairs(FIELD_ORDER) do
        if fields[key] ~= false then
            local builder = fieldBuilders[key]
            if builder then
                local ok, text, show = pcall(builder)
                if ok and show and text then
                    parts[#parts + 1] = text
                end
            end
        end
    end

    if #parts == 0 then return end

    print(string.format(
        "|c%s[TOGTools]|r %s",
        addon.BrandColor or "ffFF8000",
        table.concat(parts, "  |cff666666|||r  ")
    ))
end

-- ---------------------------------------------------------------------------
-- Logout stamp
-- ---------------------------------------------------------------------------
local function StampLogout()
    local db = DigestDB()
    if not db then return end
    db.lastLogout = db.lastLogout or {}
    db.lastLogout[CharKey()] = GetServerTime()
end

-- ---------------------------------------------------------------------------
-- Event wiring
-- ---------------------------------------------------------------------------
local _frame = CreateFrame("Frame")
_frame:RegisterEvent("PLAYER_LOGIN")
_frame:RegisterEvent("PLAYER_LOGOUT")
_frame:SetScript("OnEvent", function(_, event)
    if event == "PLAYER_LOGIN" then
        -- Kick the guild roster refresh; results land asynchronously via
        -- GUILD_ROSTER_UPDATE. The 3s deferral below gives roster + mail
        -- + bag data time to settle before we read them.
        if GuildRoster then pcall(GuildRoster) end
        C_Timer.After(3, function() LoginDigest:Print() end)
    elseif event == "PLAYER_LOGOUT" then
        StampLogout()
    end
end)

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