--[[
============================================================================
CollectionHistory.lua  (added in v14.1.0)
============================================================================

Tracks WHEN each transmog appearance was first observed as collected, so the
"Recently Collected" sort mode has real timestamps to order on.

The module listens for `TRANSMOG_COLLECTION_SOURCE_ADDED`, records `time()`
under the source's `itemID`. On first run it backfills currently-collected
items with timestamp 0 (so they all sort at the bottom in recent-first order
and show up at the top in oldest-first order — they were collected before we
started tracking, so we genuinely don't know when).

Why a separate module:
  - The collect-event responsibility is independent from filter/sort logic.
  - Saved-variable persistence is a single, self-contained concern.
  - Future features (collection-streak counter, recent-collection toast) all
    naturally hang off the same event subscription.

Saved variable: ETBCollectionHistoryDB = { [itemID] = unixTimestamp, ... }
============================================================================
]]--
local ADDON_NAME, NS = ...
local ETB = NS.ETB

local CH = {}
ETB.CollectionHistory = CH

local STORAGE_KEY = "ETBCollectionHistoryDB"

-- Cap on stored entries — avoid unbounded growth on long-lived characters.
-- Beyond this we evict oldest. Hysteresis to avoid thrashing on every collect.
local MAX_ENTRIES = 5000
local TRIM_TO = 4500

-- =============================================================================
-- INITIALIZATION & PERSISTENCE
-- =============================================================================

function CH:Initialize()
    if self.initialized then return end

    -- WoW client provides the saved variable as a global; create if absent.
    _G[STORAGE_KEY] = _G[STORAGE_KEY] or {}
    self.db = _G[STORAGE_KEY]

    -- One-time backfill: items already collected before we started tracking
    -- get timestamp 0 so they sort to the chronological start (oldest end).
    -- Detect via a flag on the DB itself, not the entry count, so we don't
    -- redundantly backfill if the user happens to start with empty history.
    if not self.db._backfilled then
        self:Backfill()
        self.db._backfilled = true
    end

    self:RegisterEvents()
    self.initialized = true
    if ETB.Debug then ETB:Debug("CollectionHistory", "Initialized") end
end

function CH:Backfill()
    -- Walk every transmog category and seed any already-collected source
    -- with timestamp 0. Best-effort: if the API call fails we just skip.
    if not C_TransmogCollection or not C_TransmogCollection.GetCategoryAppearances then
        return
    end

    local seeded = 0
    for categoryID = 1, 28 do
        local appearances = C_TransmogCollection.GetCategoryAppearances(categoryID)
        if appearances then
            for _, app in ipairs(appearances) do
                if app.isCollected then
                    local sources = C_TransmogCollection.GetAppearanceSources(app.visualID)
                    if sources then
                        for _, src in ipairs(sources) do
                            local itemID = src.itemID or src.itemId
                            if itemID and src.isCollected and not self.db[itemID] then
                                self.db[itemID] = 0
                                seeded = seeded + 1
                            end
                        end
                    end
                end
            end
        end
    end

    if ETB.Debug then
        ETB:Debug("CollectionHistory", "Backfilled " .. seeded .. " pre-existing collections")
    end
end

-- =============================================================================
-- EVENTS
-- =============================================================================

function CH:RegisterEvents()
    if self.frame then return end
    self.frame = CreateFrame("Frame")
    self.frame:RegisterEvent("TRANSMOG_COLLECTION_SOURCE_ADDED")
    self.frame:SetScript("OnEvent", function(_, event, sourceID)
        if event == "TRANSMOG_COLLECTION_SOURCE_ADDED" and sourceID then
            CH:OnSourceCollected(sourceID)
        end
    end)
end

function CH:OnSourceCollected(sourceID)
    -- The event provides a sourceID, not an itemID. Resolve via the API.
    if not C_TransmogCollection or not C_TransmogCollection.GetSourceInfo then return end
    local info = C_TransmogCollection.GetSourceInfo(sourceID)
    if not info then return end
    local itemID = info.itemID or info.itemId
    if not itemID then return end

    self.db[itemID] = time()
    self:TrimIfNeeded()

    if ETB.EventBus then
        ETB.EventBus:Fire("ETB_ITEM_COLLECTED", itemID, self.db[itemID])
    end
end

-- =============================================================================
-- QUERY
-- =============================================================================

-- Returns the unix timestamp the item was recorded as collected, or nil if
-- the item has no record. Pre-tracking entries (backfilled) return 0.
function CH:GetTimestamp(itemID)
    if not itemID then return nil end
    return self.db and self.db[itemID]
end

-- =============================================================================
-- TRIM
-- =============================================================================

function CH:TrimIfNeeded()
    local count = 0
    for k in pairs(self.db) do
        if type(k) == "number" then count = count + 1 end
    end
    if count <= MAX_ENTRIES then return end

    -- Build sortable list, drop oldest until count <= TRIM_TO.
    local entries = {}
    for k, v in pairs(self.db) do
        if type(k) == "number" then
            entries[#entries + 1] = { id = k, ts = v }
        end
    end
    table.sort(entries, function(a, b) return a.ts < b.ts end)

    local toRemove = count - TRIM_TO
    for i = 1, toRemove do
        if entries[i] then self.db[entries[i].id] = nil end
    end

    if ETB.Debug then
        ETB:Debug("CollectionHistory", "Trimmed " .. toRemove .. " old entries")
    end
end
