-- TOG Tools
-- Author: Pimptasty
-- A collection of lightweight utility tools for WoW Classic and Retail.

local addonName, addon = ...

-- ---------------------------------------------------------------------------
-- Addon global
-- ---------------------------------------------------------------------------
TOGTools       = TOGTools or {}
TOGTools.addon = addon
addon.name     = addonName

addon.BrandColor = "ffFF8000"   -- Orange, consistent with the TOG suite

local _GetAddOnMetadata = (C_AddOns and C_AddOns.GetAddOnMetadata) or GetAddOnMetadata
addon.Version = _GetAddOnMetadata(addonName, "Version") or "dev"

-- ---------------------------------------------------------------------------
-- Version flags
-- ---------------------------------------------------------------------------
local gv = {}
addon.gameVersion = gv
do
    local tocVersion = select(4, GetBuildInfo())
    gv.isVanilla = tocVersion < 20000
    gv.isTBC     = tocVersion >= 20000 and tocVersion < 30000
    gv.isWrath   = tocVersion >= 30000 and tocVersion < 40000
    gv.isCata    = tocVersion >= 40000 and tocVersion < 50000
    gv.isMists   = tocVersion >= 50000 and tocVersion < 60000
    gv.isClassic = gv.isVanilla or gv.isTBC or gv.isWrath or gv.isCata or gv.isMists
    gv.isRetail  = not gv.isClassic
    -- Retail 12.0+ uses the EventRegistry path for chat hooks.
    gv.isRetail120Plus = tocVersion >= 120000
end

-- ---------------------------------------------------------------------------
-- AceAddon
-- ---------------------------------------------------------------------------
local Ace = LibStub("AceAddon-3.0"):NewAddon(
    addonName,
    "AceConsole-3.0",
    "AceEvent-3.0",
    "AceHook-3.0"
)
addon.lib = Ace

-- ---------------------------------------------------------------------------
-- AceDB schema
-- ---------------------------------------------------------------------------
local DB_DEFAULTS = {
    char = {
        minimapPos  = 220,
        minimapHide = false,
        lastTab     = nil,         -- last-selected MainWindow tabKey
        frames = {
            mainWindow = { width = 640, height = 460 },
        },
    },
    global = {
        -- Account-wide debug flag. When true, modules emit verbose chat
        -- prints (hook install, fire-time diagnostics, etc.). Off by default.
        debug = false,
        -- Developer tools master switch. When true, modules flagged
        -- `devOnly` (e.g. the Item DB builder) get a tab in the main window;
        -- when false they're hidden entirely. Off by default so end users
        -- never see the heavyweight dev tooling. Toggled in Settings >
        -- General > Developer Tools.
        devTools = false,
        -- NamePrefix module settings — account-wide so the nickname only
        -- needs to be configured once and applies to every alt. Toons that
        -- shouldn't self-prefix rely on `hideIfCharName` (default true) to
        -- suppress the prefix when the nickname equals the character name.
        namePrefix = {
            enabled  = true,
            nickname = "",
            format   = "(%s): ",   -- %s is replaced with nickname
            say      = false,
            guild    = false,
            officer  = false,
            party    = false,
            raid     = false,
            instance = false,
            channel  = "",         -- custom channel name filter (empty = off)
            skipExclamation  = true,
            hideIfCharName   = true,
        },
        -- Mailbox Stale Watcher: per-alt mail snapshots, account-wide so
        -- every alt's expiring mail is visible from any character.
        mailbox = {
            enabled       = true,
            thresholdDays = 2,
            snapshots     = {},   -- [Name-Realm] = { snapshotAt, items }
        },
        -- Addon Load Monitor: passive read-only display. Only the master
        -- enable flag lives here — disabling hides the tab; the underlying
        -- data still arrives via the !TOGT companion regardless.
        addonLoad = {
            enabled = true,
        },
        -- Login Digest: one-line state-of-account summary at PLAYER_LOGIN.
        -- lastLogout is per-character (Name-Realm key) so each alt
        -- reports its own "since last seen" value.
        loginDigest = {
            enabled    = true,
            fields = {
                mail        = true,
                guildOnline = true,
                hearth      = true,
                bags        = true,
                lastLogout  = true,
            },
            lastLogout = {},   -- [Name-Realm] = serverTime
        },
        -- Logs nexus — outer tab settings shared across every sub-log
        -- (Mail / Trade / Guild as they ship). retentionDays prunes old
        -- entries on every append so SV doesn't grow unbounded.
        logs = {
            enabled       = true,
            retentionDays = 90,
        },
        -- Mail Log: every mail received (inbox takes) and sent (compose
        -- via SendMail) appended to `entries`. Per-entry `player` field
        -- supports the alt-filter dropdown so a single account-wide
        -- store covers every character.
        mailLog = {
            enabled = true,
            entries = {},   -- { ts, dir, player, other, subject, items, money, cod }
        },
        -- Trade Log: every completed player-to-player trade appended to
        -- `entries`. Slot 7 = enchant; tracked separately from the item
        -- list because it has its own visual treatment in the sub-tab.
        -- Trades inside arena/BG instances are skipped.
        tradeLog = {
            enabled = true,
            entries = {},   -- { ts, player, other, given, received, moneyGiven, moneyReceived, enchantGiven, enchantReceived, zone }
        },
        -- Guild Log: snapshots Blizzard's in-game guild event log on
        -- demand and persists entries beyond the ~100-entry cap. Keyed
        -- by `GuildName-Realm` so alts in different guilds keep
        -- independent histories. Entry shape stores Blizzard's raw
        -- (year, month, day, hour) tuple because that's the precision
        -- the API exposes — no minute/second granularity available.
        guildLog = {
            enabled = true,
            guilds  = {},   -- [GuildName-Realm] = { entries = {{ y, m, d, h, type, actor, target, rank }} }
        },
        -- Guild Bank Log: snapshots Blizzard's in-game guild bank
        -- transaction log every time the bank frame opens, and persists
        -- entries beyond Blizzard's small rolling window. Same per-guild
        -- bucket structure as guildLog. Each entry carries a stable
        -- cross-viewer id (prefix "gblog|") so future TOGBankClassic
        -- integration / cross-account sync can dedupe across players.
        guildBankLog = {
            enabled = true,
            guilds  = {},   -- [GuildName-Realm] = { entries = {{ kind, type, name, itemLink, count, tab1, tab2, y, m, d, h, ts, amount, id }} }
        },
        -- Whisper Log: every whisper sent or received — in-game and
        -- Battle.net — appended to `entries`. `isBN` separates BN from
        -- in-game; `isGM` flags Blizzard staff whispers; `lineID` is
        -- used by the engine's 5-second recent-set to dedup repeats
        -- when chat events re-fire through filter chains.
        whisperLog = {
            enabled = true,
            entries = {},   -- { ts, dir, player, other, text, isGM, isBN, guid, bnSenderID, lineID, zone }
        },
        -- Gratz: profile manager for auto-congratulations on events
        -- (achievements, level-ups, etc.). Each profile names a single
        -- trigger type + its criteria, message template, channel set,
        -- and cooldown. Profiles keyed by name; create/edit/delete via
        -- the Gratz tab (FGI Filters-tab pattern). Cooldown state is
        -- per-session (in-memory) — not persisted because session
        -- restarts are a fine reset point and SV bloat is the larger
        -- concern.
        gratz = {
            enabled  = true,
            profiles = {},   -- [name] = { name, enabled, trigger, ..., count }
        },
        -- Item DB (developer tool): a locally-built, searchable copy of the
        -- game's item catalog. Built by walking every item ID and recording
        -- what the server returns (see Modules/ItemDB). Bucketed by item
        -- class so type/subtype filtering stays fast. `cursor` is the resume
        -- point; `locale`/`build` stamp the capture so the tab can flag a
        -- rebuild after a language or client-version change.
        itemDB = {
            enabled  = true,
            classes  = {},     -- [classID] = { [idStr] = "name\31quality\31subClassID\31equipLoc" }
            pending  = {},     -- [idStr] = true: requested but reply not yet stored (resume/gap-fill)
            count    = 0,
            cursor   = 1,      -- resume point for the forward walk
            topID    = nil,    -- set per-build from the engine default
            locale   = nil,
            build    = nil,
            complete = false,
            builtAt  = nil,
        },
    },
}

-- ---------------------------------------------------------------------------
-- Module registry — tabs are built from this table, sorted alphabetically
-- by their `label` field.  Each module file registers itself here.
-- ---------------------------------------------------------------------------
addon.modules = {}

-- ---------------------------------------------------------------------------
-- OnInitialize / OnEnable
-- ---------------------------------------------------------------------------
function Ace:OnInitialize()
    addon.DB = LibStub("AceDB-3.0"):New("TOGTools_DB", DB_DEFAULTS, true)

    -- One-shot migration: v0.3.2 moved namePrefix from char to global.
    -- If this character has a pre-migration char.namePrefix table with a
    -- non-empty nickname AND the new global table is still at defaults
    -- (empty nickname), copy the per-character settings up. Picks whichever
    -- alt logs in first after the upgrade; subsequent alts just inherit
    -- the now-global config. Leftover char.namePrefix is harmless.
    do
        local oldCfg = rawget(addon.DB.char, "namePrefix")
        local newCfg = addon.DB.global.namePrefix
        if oldCfg and oldCfg.nickname and oldCfg.nickname ~= ""
           and (not newCfg.nickname or newCfg.nickname == "") then
            for k, v in pairs(oldCfg) do newCfg[k] = v end
        end
    end

    -- Expose Version so VersionCheck-1.0 reads it from the host object directly.
    Ace.Version = addon.Version

    -- Register with VersionCheck-1.0 on Classic; the library is not yet
    -- available on Retail so we guard with the version flag.
    if not gv.isRetail then
        local VC = LibStub("VersionCheck-1.0", true)
        if VC then
            VC:Enable(Ace)
        end
    end

end

function Ace:OnEnable()
    -- Enable all registered modules
    for _, mod in pairs(addon.modules) do
        if mod.OnEnable then mod:OnEnable() end
    end
end

-- ---------------------------------------------------------------------------
-- Debug print helper — gated on `db.global.debug` (toggle in the Settings
-- panel). Use from any module for verbose diagnostics that should be silent
-- in normal play but visible when troubleshooting.
--
-- Output shape: `TOGTools [HH:MM:SS.mmm] <message>`
--
-- The timestamp comes from `date("%H:%M:%S")` (server-local wall clock) with
-- millisecond precision derived from `GetTime()`. Wall clock matches WoW's
-- own chat timestamps so you can correlate addon traces with surrounding
-- chat events; the ms suffix matters when triaging event bursts that fire
-- many times per second (e.g. GUILDBANK_UPDATE_TABS x24 on bank-open).
-- ---------------------------------------------------------------------------
function addon:Debug(msg)
    if addon.DB and addon.DB.global and addon.DB.global.debug then
        local t = (GetTime and GetTime()) or 0
        local ms = math.floor((t - math.floor(t)) * 1000)
        print(string.format("%s [%s.%03d] %s",
            addon.UI.Brand("TOGTools"),
            date("%H:%M:%S"),
            ms,
            tostring(msg)))
    end
end

-- ---------------------------------------------------------------------------
-- Module enabled-state — read via the tab module's `configKey` field, which
-- names the SV section that holds the module's settings (including its
-- `enabled` boolean). Returns true when the module/section isn't found so
-- legacy code paths and freshly registered tabs default to "on" until they
-- explicitly opt into the toggle.
-- ---------------------------------------------------------------------------
function addon:IsModuleEnabled(tabKey)
    if not (addon.DB and addon.DB.global) then return true end
    local mod = addon.modules and addon.modules[tabKey]
    if not mod then return true end
    local key = mod.configKey
    if not key then return true end
    local section = addon.DB.global[key]
    if not section then return true end
    if section.enabled == nil then return true end
    return section.enabled and true or false
end
