-- TOG Tools — Trade Log sub-tab (UI)
-- Renders inside the Logs outer tab's nested AceGUI TabGroup. Layout:
--   Filter row:  Date range ▾   [🔍 search]
--   Data table:  Time · Character · Partner · Given · Received · Enchant
--
-- The table is two-sided so each trade shows both Given and Received
-- cells, plus a dedicated Enchant column for trade slot 7 (often empty;
-- visible when applying an enchant to/from a partner's item).
--
-- Character filtering now lives in the column-header menus and the single
-- search box (case-insensitive substring across every column). Date-range
-- state lives on the module table so it survives Draw rebuilds, but resets
-- on /reload.

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

local TradeLog = addon.logCategories["trade"]

TradeLog._filterRange        = TradeLog._filterRange        or addon.DateRangePicker.DEFAULT_KEY
TradeLog._filterCustomFrom   = TradeLog._filterCustomFrom   or nil
TradeLog._filterCustomTo     = TradeLog._filterCustomTo     or nil
TradeLog._filterCalendarOpen = TradeLog._filterCalendarOpen or false
TradeLog._search             = TradeLog._search             or ""

-- ---------------------------------------------------------------------------
-- 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)
    if not copper or copper <= 0 then return nil 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

local function fmtSide(items, money)
    -- One side of the trade — items + money rolled into one cell. Lead
    -- with the first item link; collapse extras into "+N more" so the
    -- column stays readable when several items were traded.
    local hasItems = items and #items > 0
    local moneyStr = fmtMoney(money)
    if not hasItems and not moneyStr then return "|cff888888—|r" end
    local parts = {}
    if hasItems then
        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
        tinsert(parts, s)
    end
    if moneyStr then tinsert(parts, moneyStr) end
    return table.concat(parts, ", ")
end

local function fmtEnchant(given, received)
    -- Visible when slot 7 was used on either side. "→" for given to
    -- partner; "←" for received from partner. Both is unusual but
    -- handled (mutual enchant trade).
    if given and received then
        return "|cffaa44ff->|r " .. given .. " |cffaa44ff/ <-|r " .. received
    end
    if given    then return "|cffaa44ff->|r " .. given    end
    if received then return "|cffaa44ff<-|r " .. received end
    return "|cff888888—|r"
end

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

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

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

    local rl   -- data table; forward-declared so the search box can reach it

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

    filterRow:AddChild(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,
    }))

    local search = AceGUI:Create("EditBox")
    search:SetWidth(240)
    search:DisableButton(true)
    search:SetText(self._search or "")
    search:SetCallback("OnTextChanged", function(_, _, text)
        self._search = text
        if rl then rl:SetSearch(text) end
    end)
    addon.UI.StyleSearchBox(search, true)
    filterRow:AddChild(search)

    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

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

    -- Spacer between filter row and the data table
    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 = TradeLog:GetEntries(nil, fromTs, toTs)

    if #entries == 0 then
        local empty = AceGUI:Create("Label")
        empty:SetFullWidth(true)
        empty:SetText("|cff888888No trade entries match the current filters.|r\n\nTrade Log captures every completed player-to-player trade — arena/BG trades are skipped intentionally. Widen the |cffffd700Date range|r, clear the |cffffd700search|r box, or check the column-header filters.")
        body:AddChild(empty)
        return
    end

    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 trade completed. 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 captured 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 completed this trade. Trade Log is account-wide; use this column's header filter to narrow to one alt. Column auto-sizes to fit the longest captured name.",
              format = function(e) return e.player or "?" end },

            { key = "other", header = "Partner", autoFit = true, minWidth = 60, justify = "LEFT",
              headerTip = "Partner",
              headerTipDesc = "The player on the other side of the trade. Cross-realm trades show the partner's full Name-Realm; same-realm trades may omit the realm suffix. Column auto-sizes to fit the longest captured partner.",
              format = function(e) return e.other or "?" end },

            { key = "given", header = "Given", justify = "LEFT",
              headerTip = "Given",
              headerTipDesc = "What YOU handed over: items + money. Leads with the first item link; multi-item trades show \"+N more\". Money appended after items with gold/silver/copper colouring. This is the rightmost-of-left-cluster 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 fmtSide(e.given, e.moneyGiven) end },

            { key = "received", header = "Received", autoFit = true, minWidth = 70, justify = "LEFT",
              headerTip = "Received",
              headerTipDesc = "What the PARTNER handed over to you: items + money. Same format as Given. Column auto-sizes to fit the longest captured received bundle.",
              format = function(e) return fmtSide(e.received, e.moneyReceived) end },

            { key = "enchant", header = "Enchant", autoFit = true, minWidth = 50, justify = "LEFT",
              headerTip = "Enchant",
              headerTipDesc = "Trade slot 7 contents (the enchant slot). |cffaa44ff->|r = enchant applied to partner's item; |cffaa44ff<-|r = partner applied an enchant to your item; both arrows = mutual enchant trade. Usually empty. Column auto-sizes to fit the longest captured enchant.",
              format = function(e) return fmtEnchant(e.enchantGiven, e.enchantReceived) end },
        },
    })
    rl:SetSort("ts", true)
    rl:SetData(entries)
    if self._search and self._search ~= "" then rl:SetSearch(self._search) end

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