-- TOG Tools — Mail Log sub-tab (UI)
-- Renders inside the Logs outer tab's nested AceGUI TabGroup. Layout:
--   Filter row:  Character ▾   Direction ▾   Date range ▾   [ Clear ]
--   Data table:  RowList — Time · Dir · Player · Other · Subject · Items · Money
--
-- Filter state lives on the module table so it survives Draw rebuilds
-- triggered by capture refresh, but resets on /reload. SV persistence
-- for filters isn't worth the storage right now.

local _, addon = ...
local AceGUI = LibStub("AceGUI-3.0")

local MailLog = addon.logCategories["mail"]

-- ---------------------------------------------------------------------------
-- Filter state (per-session, not persisted)
-- ---------------------------------------------------------------------------
MailLog._filterPlayer       = MailLog._filterPlayer       or nil  -- nil = All
MailLog._filterDir          = MailLog._filterDir          or nil  -- nil = both
MailLog._filterRange        = MailLog._filterRange        or addon.DateRangePicker.DEFAULT_KEY
MailLog._filterCustomFrom   = MailLog._filterCustomFrom   or nil  -- start-day ts for Custom range
MailLog._filterCustomTo     = MailLog._filterCustomTo     or nil  -- end-day ts; nil = single-day range
MailLog._filterCalendarOpen = MailLog._filterCalendarOpen or false

-- ---------------------------------------------------------------------------
-- Cell formatters
-- ---------------------------------------------------------------------------
local function fmtTime(ts)
    if not ts or ts <= 0 then return "—" end
    local now = (GetServerTime and GetServerTime()) or time()
    local diff = now - ts
    if diff < 60      then return string.format("%ds ago", math.floor(diff)) end
    if diff < 3600    then return string.format("%dm ago", math.floor(diff / 60)) end
    if diff < 86400   then return string.format("%dh ago", math.floor(diff / 3600)) end
    if diff < 604800  then return string.format("%dd ago", math.floor(diff / 86400)) end
    return date("%m/%d/%y", ts)
end

local function fmtMoney(copper)
    copper = copper or 0
    if copper <= 0 then return "—" end
    local g = math.floor(copper / 10000)
    local s = math.floor((copper % 10000) / 100)
    local c = copper % 100
    local parts = {}
    if g > 0 then tinsert(parts, "|cffffd700" .. g .. "g|r") end
    if s > 0 then tinsert(parts, "|cffc7c7cf" .. s .. "s|r") end
    if c > 0 and g == 0 then tinsert(parts, "|cffeda55f" .. c .. "c|r") end
    return table.concat(parts, " ")
end

-- Direction prefix folded into the Other column to save horizontal space.
-- Green left-arrow for received (other -> me), orange right-arrow for sent
-- (me -> other). Without this the table needed a separate Dir column and
-- the total fixed widths overflowed the default window — auto-width
-- Subject column went negative and right-anchored cells spilled off the
-- left edge.
local function fmtOtherWithDir(other, dir)
    other = other or "?"
    if dir == "in"  then return "|cff66ff66<-|r " .. other end
    if dir == "out" then return "|cffffaa55->|r " .. other end
    return other
end

local function fmtItems(items)
    if not items or #items == 0 then return "|cff888888—|r" end
    -- Lead with the first item's link; append "+N more" if the entry has
    -- more than one distinct itemLink. Item links carry their own colour
    -- escapes; the count suffix multiplies stack size when > 1.
    local first = items[1]
    local s = first.link or ""
    if (first.count or 1) > 1 then s = s .. " x" .. first.count end
    if #items > 1 then
        s = s .. " |cffaaaaaa+" .. (#items - 1) .. " more|r"
    end
    return s
end

-- ---------------------------------------------------------------------------
-- (The in-tab "Clear data" button was removed in v0.5.2; clearing is
-- centralised in Settings > Clear Data, which uses AceConfig's native
-- two-step confirm popup. See `GUI/Settings.lua`'s `clearData` group.)

-- ---------------------------------------------------------------------------
-- Filter dropdown builders
-- ---------------------------------------------------------------------------
local function buildPlayerDropdown(onChange)
    local dd = AceGUI:Create("Dropdown")
    dd:SetLabel("Character")
    dd:SetWidth(180)
    local list, order = { __all = "All characters" }, { "__all" }
    for _, k in ipairs(MailLog:GetPlayerKeys()) do
        list[k] = k
        tinsert(order, k)
    end
    dd:SetList(list, order)
    dd:SetValue(MailLog._filterPlayer or "__all")
    dd:SetCallback("OnValueChanged", function(_, _, key)
        MailLog._filterPlayer = (key == "__all") and nil or key
        onChange()
    end)
    return dd
end

local function buildDirDropdown(onChange)
    local dd = AceGUI:Create("Dropdown")
    dd:SetLabel("Direction")
    dd:SetWidth(120)
    dd:SetList({ __both = "Both", ["in"] = "Received", ["out"] = "Sent" },
               { "__both", "in", "out" })
    dd:SetValue(MailLog._filterDir or "__both")
    dd:SetCallback("OnValueChanged", function(_, _, key)
        MailLog._filterDir = (key == "__both") and nil or key
        onChange()
    end)
    return dd
end

-- ---------------------------------------------------------------------------
-- Draw
-- ---------------------------------------------------------------------------
function MailLog:Draw(container)
    container:SetLayout("Flow")

    local function rebuild()
        if addon.MainWindow and addon.MainWindow.Refresh then
            addon.MainWindow:Refresh()
        end
    end

    -- ---- Filter row -----------------------------------------------------
    local filterRow = AceGUI:Create("SimpleGroup")
    filterRow:SetFullWidth(true)
    filterRow:SetLayout("Flow")
    container:AddChild(filterRow)

    filterRow:AddChild(buildPlayerDropdown(rebuild))
    filterRow:AddChild(buildDirDropdown(rebuild))
    filterRow:AddChild(addon.DateRangePicker:Create({
        default  = self._filterRange,
        onChange = function(key)
            -- Entering Custom mode (from any other preset) auto-opens
            -- the calendar so the user doesn't have to discover a
            -- separate "open calendar" button.
            local enteringCustom = (key == "custom" and self._filterRange ~= "custom")
            self._filterRange = key
            if enteringCustom then self._filterCalendarOpen = true end
            rebuild()
        end,
    }))

    -- "Pick range..." button visible only when in Custom mode with a
    -- range already picked and the calendar collapsed — re-opens the
    -- calendar so the user can change the selection. Date dropdown
    -- won't fire OnValueChanged when re-clicking the same value, so
    -- an explicit button is required for this re-open path.
    if self._filterRange == "custom" and self._filterCustomFrom
       and not self._filterCalendarOpen then
        local pickBtn = AceGUI:Create("Button")
        pickBtn:SetWidth(140)
        pickBtn:SetText("Pick range...")
        pickBtn:SetCallback("OnClick", function()
            self._filterCalendarOpen = true
            rebuild()
        end)
        filterRow:AddChild(pickBtn)
    end

    -- ---- Optional inline calendar (Custom range only) -------------------
    -- Shown when EITHER no range is picked yet (the calendar IS the
    -- prompt) OR the user explicitly opened it via "Pick range..." /
    -- entering Custom mode.
    if self._filterRange == "custom"
       and (not self._filterCustomFrom or self._filterCalendarOpen) then
        local unbounded = MailLog:GetEntries(self._filterPlayer, self._filterDir, nil, nil)
        local timestamps = {}
        for _, e in ipairs(unbounded) do tinsert(timestamps, e.ts) end
        container:AddChild(addon.DateRangePicker:CreateCalendar({
            defaultFrom = self._filterCustomFrom,
            defaultTo   = self._filterCustomTo,
            timestamps  = timestamps,
            onApply     = function(fromTs, toTs)
                self._filterCustomFrom   = fromTs
                self._filterCustomTo     = toTs
                self._filterCalendarOpen = false
                rebuild()
            end,
        }))
        -- No range picked yet → table area is meaningless; skip body.
        if not self._filterCustomFrom then return end
    end

    -- Visual spacer between filter row and the data table. AceGUI Flow
    -- otherwise butts the RowList header directly against the dropdowns.
    local sp = AceGUI:Create("Label")
    sp:SetFullWidth(true); sp:SetText(" ")
    container:AddChild(sp)

    -- ---- Data table -----------------------------------------------------
    local body = AceGUI:Create("SimpleGroup")
    body:SetFullWidth(true)
    body:SetFullHeight(true)
    body:SetLayout("List")
    container:AddChild(body)

    local fromTs, toTs = addon.DateRangePicker:Resolve(
        self._filterRange, self._filterCustomFrom, self._filterCustomTo)
    local entries = MailLog:GetEntries(self._filterPlayer, self._filterDir, fromTs, toTs)

    if #entries == 0 then
        local empty = AceGUI:Create("Label")
        empty:SetFullWidth(true)
        empty:SetText("|cff888888No mail entries match the current filters.|r\n\nMail Log captures every mail received and sent on every alt — entries appear here automatically as you take inbox items or send mail. Try widening the date range or selecting |cffffd700All characters|r.")
        body:AddChild(empty)
        return
    end

    local rl = addon.RowList:New(body.content, {
        rowHeight = 16,
        columns = {
            { key = "ts", header = "Time", autoFit = true, minWidth = 50, justify = "LEFT",
              sortKey = "ts", sortDescDefault = true,
              headerTip = "Time",
              headerTipDesc = "When the mail was sent or taken. Recent entries show as \"Nm/h/d ago\"; older entries fall back to MM/DD/YY. Click to sort by time. Column auto-sizes to fit the longest visible timestamp.",
              format = function(e) return fmtTime(e.ts) end },

            { key = "player", header = "Character", autoFit = true, minWidth = 60, justify = "LEFT",
              headerTip = "Character",
              headerTipDesc = "The alt that sent or received this mail. Mail Log is account-wide — every alt's activity lands in the same store. Use the Character filter dropdown above to narrow to one alt. Column auto-sizes to fit the longest visible name.",
              format = function(e) return e.player or "?" end },

            { key = "other", header = "Other", autoFit = true, minWidth = 70, justify = "LEFT",
              headerTip = "Other",
              headerTipDesc = "The other side of the mail. Green |cff66ff66<-|r prefix = received from this sender; orange |cffffaa55->|r prefix = sent to this recipient. Lets you scan direction without a separate column. Column auto-sizes to fit the longest visible sender/recipient.",
              format = function(e) return fmtOtherWithDir(e.other, e.dir) end },

            { key = "subject", header = "Subject", justify = "LEFT",
              headerTip = "Subject",
              headerTipDesc = "The subject line of the mail. Empty subjects show as \"(no subject)\". This is the rightmost stretchy column — the only column that may truncate when the window is too narrow; every other column auto-sizes to fit its content.",
              format = function(e)
                  if not e.subject or e.subject == "" then
                      return "|cff888888(no subject)|r"
                  end
                  return e.subject
              end },

            { key = "items", header = "Items", autoFit = true, minWidth = 60, justify = "LEFT",
              headerTip = "Items",
              headerTipDesc = "Attached items. Leads with the first item link (×count if stacked); multi-item mails show \"+N more\" on the right. Inbox takes from the same sender within 5 seconds are merged into one entry with items stacked by link. Column auto-sizes to fit the longest visible item summary.",
              format = function(e) return fmtItems(e.items) end },

            { key = "money", header = "Money", autoFit = true, minWidth = 50, justify = "RIGHT",
              sortKey = "money", sortDescDefault = true,
              headerTip = "Money",
              headerTipDesc = "Gold / silver / copper attached to the mail. Click to sort heaviest-first. Column auto-sizes to fit the longest visible money string.",
              format = function(e) return fmtMoney(e.money) end },
        },
    })
    rl:SetSort("ts", true)   -- newest first
    rl:SetData(entries)

    body:SetCallback("OnRelease", function() rl:Detach() end)
end
