-- TOG Tools — Guild Bank Log sub-tab (UI)
--
-- Renders inside the Logs outer tab's nested AceGUI TabGroup. Layout:
--   Filter row: Guild ▾   Type ▾   Tab ▾   Date range ▾
--   Data table: Time · Guild · Type · Player · Item/Amount · Qty · Tab
--
-- Per-session filter state lives on the module table.

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

local GuildBankLog = addon.logCategories["guildbank"]

GuildBankLog._filterGuild        = GuildBankLog._filterGuild        or nil
GuildBankLog._filterType         = GuildBankLog._filterType         or nil
GuildBankLog._filterTab          = GuildBankLog._filterTab          or nil
GuildBankLog._filterRange        = GuildBankLog._filterRange        or "all"
GuildBankLog._filterCustomFrom   = GuildBankLog._filterCustomFrom   or nil
GuildBankLog._filterCustomTo     = GuildBankLog._filterCustomTo     or nil
GuildBankLog._filterCalendarOpen = GuildBankLog._filterCalendarOpen or false

-- ---------------------------------------------------------------------------
-- Type metadata
-- ---------------------------------------------------------------------------
local TYPE_LABEL = {
    deposit         = "Deposit",
    withdraw        = "Withdraw",
    move            = "Move",
    repair          = "Repair",
    withdrawForTab  = "Tab buy",
    depositSummary  = "Deposit",
    buyTab          = "Bought tab",
}

local TYPE_COLOR = {
    deposit         = "ff66ff66",
    withdraw        = "ffffcc66",
    move            = "ff66ccff",
    repair          = "ffffaa55",
    withdrawForTab  = "ffffcc66",
    depositSummary  = "ff66ff66",
    buyTab          = "ffccaaff",
}

-- ---------------------------------------------------------------------------
-- 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 < 3600  then return "Just now" end
    if diff < 86400 then return string.format("%dh ago", math.floor(diff / 3600)) end
    if diff < 330 * 86400 then return date("%m/%d %H:00", ts) end
    return date("%m/%d/%y %H:00", ts)
end

local function fmtType(t)
    local label = TYPE_LABEL[t] or (t and t:sub(1,1):upper() .. t:sub(2)) or "?"
    local color = TYPE_COLOR[t] or "ffffffff"
    return "|c" .. color .. label .. "|r"
end

local function fmtPlayer(name)
    if not name or name == "" then return "|cff888888—|r" end
    return name
end

local function fmtItem(e)
    if e.kind == "money" then
        local copper = tonumber(e.amount) or 0
        if GetCoinTextureString then return GetCoinTextureString(copper) end
        local g = math.floor(copper / 10000)
        local s = math.floor((copper % 10000) / 100)
        local c = copper % 100
        return string.format("%dg %ds %dc", g, s, c)
    end
    if e.itemLink and e.itemLink ~= "" then return e.itemLink end
    return "|cff888888—|r"
end

local function fmtQty(e)
    if e.kind == "money" then return "" end
    return tostring(e.count or 0)
end

local function fmtTab(e)
    if e.kind == "money" then return "|cffffd700$|r" end
    if e.type == "move" then
        -- ASCII `>` instead of the U+2192 rightwards-arrow glyph: WoW's
        -- default chat font on TBC / Anniversary doesn't carry that
        -- code point, so the cell rendered as a missing-glyph box. `>`
        -- isn't as pretty but always renders.
        return string.format("|cff888888%d > %d|r",
            tonumber(e.tab1) or 0, tonumber(e.tab2) or 0)
    end
    return tostring(tonumber(e.tab1) or 0)
end

local function fmtGuild(key)
    if not key or key == "" then return "|cff888888?|r" end
    -- Strip "-Realm" suffix for same-realm guilds to keep the column compact.
    local own = (GetRealmName() or ""):gsub("%s+", "")
    local guildName, realm = key:match("^(.+)%-([^%-]+)$")
    if guildName and realm == own then return guildName end
    return key
end

-- (The in-tab "Clear data" button + two-step confirm helper were
-- removed in v0.5.2; clearing is centralised in Settings > Clear Data.)

-- ---------------------------------------------------------------------------
-- Filter dropdowns
-- ---------------------------------------------------------------------------
-- Small helper — every AceGUI widget needs the same `widget.frame` reachable
-- before UI.AttachTooltip can hook OnEnter/OnLeave on it. Inlined per-call
-- rather than module-level so the bail-out on missing UI helper is explicit.
local function attachTip(widget, title, body)
    if widget and addon.GUI and addon.GUI.AttachTooltip then
        addon.GUI.AttachTooltip(widget, title, body)
    end
end

local function buildGuildDropdown(onChange)
    local dd = AceGUI:Create("Dropdown")
    dd:SetLabel("Guild")
    dd:SetWidth(180)
    local current = GuildBankLog:GetCurrentGuildKey()
    local list, order = { __all = "All guilds" }, { "__all" }
    if current then
        list[current] = fmtGuild(current) .. " (current)"
        tinsert(order, current)
    end
    for _, k in ipairs(GuildBankLog:GetGuildKeys()) do
        if k ~= current then
            list[k] = fmtGuild(k)
            tinsert(order, k)
        end
    end
    local default = GuildBankLog._filterGuild or current or "__all"
    if not list[default] then default = "__all" end
    dd:SetList(list, order)
    dd:SetValue(default)
    dd:SetCallback("OnValueChanged", function(_, _, key)
        GuildBankLog._filterGuild = key
        onChange()
    end)
    attachTip(dd, "Guild filter",
        "Which guild's bank history to show. Defaults to your currently logged-in character's guild. Switch to |cffffd700All guilds|r to see entries from every guild your alts are members of merged into one view; same-realm guilds drop the realm suffix for compactness, cross-realm shows Guild-Realm.")
    return dd
end

local function buildTypeDropdown(onChange)
    local dd = AceGUI:Create("Dropdown")
    dd:SetLabel("Type")
    dd:SetWidth(150)
    dd:SetList(
        { __all    = "All types",
          deposit  = "Deposit",
          withdraw = "Withdraw",
          move     = "Move",
          repair   = "Repair",
          buyTab   = "Bought tab" },
        { "__all", "deposit", "withdraw", "move", "repair", "buyTab" })
    dd:SetValue(GuildBankLog._filterType or "__all")
    dd:SetCallback("OnValueChanged", function(_, _, key)
        GuildBankLog._filterType = (key == "__all") and nil or key
        onChange()
    end)
    attachTip(dd, "Transaction type filter",
        "Narrow the table to one transaction type. |cff66ff66Deposit|r — items placed into the bank. |cffffcc66Withdraw|r — items taken out. |cff66ccffMove|r — items shifted between tabs. |cffffaa55Repair|r — gold used to pay for armor repairs at the bank. |cffccaaffBought tab|r — gold spent purchasing a new bank tab. Money log entries (deposit gold / withdraw gold) appear under the matching deposit / withdraw filter.")
    return dd
end

-- Bank tab filter. The last index in Blizzard's MAX_GUILDBANK_TABS slot
-- is the money log (tab 8 on every release that has it), so the item-
-- tab range is 1..MAX_GUILDBANK_TABS-1. The "Money log" entry maps to
-- the money-only filter on the engine side. Fallback to 7 when the
-- constant isn't defined (Classic Era pre-TBC didn't ship guild banks
-- so the global is absent; the dropdown still renders so the tab
-- gracefully degrades to "select but no results").
local function buildTabDropdown(onChange)
    local dd = AceGUI:Create("Dropdown")
    dd:SetLabel("Tab")
    dd:SetWidth(120)
    local list  = { __all = "All tabs" }
    local order = { "__all" }
    local maxTabs = (MAX_GUILDBANK_TABS or 8) - 1
    for i = 1, maxTabs do
        local key = tostring(i)
        list[key] = "Tab " .. i
        tinsert(order, key)
    end
    list["money"] = "Money log"
    tinsert(order, "money")
    local current = GuildBankLog._filterTab
    local default = current and tostring(current) or "__all"
    if not list[default] then default = "__all" end
    dd:SetList(list, order)
    dd:SetValue(default)
    dd:SetCallback("OnValueChanged", function(_, _, key)
        if key == "__all" then
            GuildBankLog._filterTab = nil
        elseif key == "money" then
            GuildBankLog._filterTab = "money"
        else
            GuildBankLog._filterTab = tonumber(key)
        end
        onChange()
    end)
    attachTip(dd, "Bank tab filter",
        "Restrict the table to transactions touching a specific bank tab. |cffffd700Tab N|r shows deposits, withdraws, and repairs from that tab plus any |cff66ccffMove|r whose source OR destination is tab N (so a move from Tab 2 to Tab 5 appears under both filters). |cffffd700Money log|r shows only gold deposits and withdrawals (no item transactions). |cffffd700All tabs|r is the default and shows everything.")
    return dd
end

-- ---------------------------------------------------------------------------
-- Draw
-- ---------------------------------------------------------------------------
function GuildBankLog: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(buildGuildDropdown(rebuild))
    filterRow:AddChild(buildTypeDropdown(rebuild))
    filterRow:AddChild(buildTabDropdown(rebuild))
    local dateDD = addon.DateRangePicker:Create({
        default  = self._filterRange,
        onChange = function(key)
            local enteringCustom = (key == "custom" and self._filterRange ~= "custom")
            self._filterRange = key
            if enteringCustom then self._filterCalendarOpen = true end
            rebuild()
        end,
    })
    filterRow:AddChild(dateDD)
    if addon.GUI and addon.GUI.AttachTooltip then
        addon.GUI.AttachTooltip(dateDD, "Date range filter",
            "Restrict the table to transactions within a time window. |cffffd700Last 24 hours / 7 days / 30 days|r are quick presets. |cffffd700All|r shows everything captured. |cffffd700Custom...|r opens an inline calendar where you pick a start day with one click and an end day with a second; a third click resets. Blizzard's bank API only exposes hour-precision timestamps, so the smallest meaningful window is one hour.")
    end

    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)
        if addon.GUI and addon.GUI.AttachTooltip then
            addon.GUI.AttachTooltip(pickBtn, "Pick a different range",
                "Re-open the calendar to change the custom date range. The current pick stays applied until you commit a new one.")
        end
    end

    -- Resolve the effective guild filter once — used by both the calendar
    -- availability query and the table query below.
    local guildKey = self._filterGuild
    if guildKey == "__all" then guildKey = nil end
    if not guildKey then
        guildKey = GuildBankLog:GetCurrentGuildKey()
        if self._filterGuild == "__all" then guildKey = nil end
    end

    if self._filterRange == "custom"
       and (not self._filterCustomFrom or self._filterCalendarOpen) then
        local unbounded = GuildBankLog:GetEntries(guildKey, self._filterType, nil, nil, self._filterTab)
        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,
        }))
        if not self._filterCustomFrom then return end
    end

    -- Spacer
    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 = GuildBankLog:GetEntries(guildKey, self._filterType, fromTs, toTs, self._filterTab)

    if #entries == 0 then
        local empty = AceGUI:Create("Label")
        empty:SetFullWidth(true)
        local heading = (addon.UI and addon.UI.Brand)
            and addon.UI.Brand("Guild Bank Log") or "Guild Bank Log"
        local msg = heading .. "\n|cff888888No guild bank transactions match the current filters.|r"
        if not GuildBankLog:GetCurrentGuildKey() then
            msg = msg .. "\n\nThis character isn't in a guild — Guild Bank Log only captures events for guilds your alts are members of."
        elseif not QueryGuildBankLog then
            msg = msg .. "\n\nThis game version doesn't have a guild bank. (Guild banks were added in TBC.) The tab is here so a future TOGBankClassic integration can feed entries directly."
        else
            msg = msg .. "\n\nOpen your guild bank to capture transactions — every visit refreshes the log automatically. If you don't have View Tab permission on a tab, its transactions stay invisible."
        end
        empty:SetText(msg)
        body:AddChild(empty)
        return
    end

    local rl = addon.RowList:New(body.content, {
        rowHeight = 16,
        columns = {
            { key = "ts", header = "Time", autoFit = true, minWidth = 60, justify = "LEFT",
              sortKey = "ts", sortDescDefault = true,
              headerTip = "Time",
              headerTipDesc = "When the transaction happened. Blizzard's bank API only exposes hour precision (no minute/second), so older entries show as MM/DD HH:00. Recent entries show as \"Nh ago\". Click to sort by time. Column auto-sizes to fit the longest visible timestamp.",
              format = function(e) return fmtTime(e.ts) end },

            { key = "guildKey", header = "Guild", autoFit = true, minWidth = 60, justify = "LEFT",
              headerTip = "Guild",
              headerTipDesc = "Which guild the transaction was in. Same-realm guilds drop the realm suffix for compactness. The Guild filter dropdown above defaults to your current guild. Column auto-sizes to fit the longest visible guild.",
              format = function(e) return fmtGuild(e.guildKey) end },

            { key = "type", header = "Type", autoFit = true, minWidth = 50, justify = "LEFT",
              headerTip = "Transaction type",
              headerTipDesc = "Deposit / Withdraw / Move / Repair / Bought tab. Colour-coded: green = deposit, yellow = withdraw, blue = move, orange = repair, purple = tab purchase. Column auto-sizes to the widest type label.",
              format = function(e) return fmtType(e.type) end },

            { key = "name", header = "Player", autoFit = true, minWidth = 60, justify = "LEFT",
              headerTip = "Player",
              headerTipDesc = "The character who performed the transaction. Column auto-sizes to fit the longest visible name.",
              format = function(e) return fmtPlayer(e.name) end },

            { key = "item", header = "Item / Amount", justify = "LEFT",
              headerTip = "Item or money amount",
              headerTipDesc = "For item transactions: the item link. For money transactions: the gold/silver/copper amount with coin icons. This is the stretchy column — the one that may truncate when the window is too narrow; every other column auto-sizes to fit its content.",
              format = function(e) return fmtItem(e) end },

            { key = "qty", header = "Qty", autoFit = true, minWidth = 35, justify = "RIGHT",
              headerTip = "Quantity",
              headerTipDesc = "Stack size for item transactions. Empty for money. Column auto-sizes to fit the largest visible quantity.",
              format = function(e) return fmtQty(e) end },

            { key = "tab", header = "Tab", autoFit = true, minWidth = 45, justify = "CENTER",
              headerTip = "Bank tab",
              headerTipDesc = "Which bank tab the item came from (or moved between, shown as \"from > to\"). $ for money log entries. Use the |cffffd700Tab|r filter dropdown in the filter row to restrict to a specific tab — moves match either side. Column auto-sizes to fit the longest visible tab indicator.",
              format = function(e) return fmtTab(e) end },
        },
    })
    rl:SetSort("ts", true)
    rl:SetData(entries)

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