-- TOG Tools — Settings panel
-- AceConfig-3.0 options table registered with the Blizzard Interface Options
-- (ESC → Options → Addons → TOG Tools).  Also opened directly via
-- addon:OpenSettings() — called by right-click on the minimap button.

local _, addon = ...
local Ace       = addon.lib

local AceConfig = LibStub("AceConfig-3.0",       true)
local AceDialog = LibStub("AceConfigDialog-3.0", true)

if not AceConfig or not AceDialog then return end

-- ---------------------------------------------------------------------------
-- Options table — two sub-groups rendered as tabs at the top of the panel
-- (`childGroups = "tab"`):
--   General  — minimap button, debug toggle, and other addon-wide settings
--              that don't belong to a specific tab/module.
--   Modules  — one toggle per registered tab/module. Disabling a module
--              hides its tab from the main window AND stops its engine
--              (capture / event hooks short-circuit on the same flag).
--              The Modules group's args are populated dynamically from
--              `addon.modules` at registration time so new modules
--              automatically get a toggle without per-module wiring here.
-- ---------------------------------------------------------------------------
local OPTIONS = {
    name        = "TOG Tools",
    handler     = addon,
    type        = "group",
    childGroups = "tab",
    args        = {

        general = {
            name  = "General",
            type  = "group",
            order = 1,
            args  = {
                minimapHeader = {
                    name  = "Minimap Button",
                    type  = "header",
                    order = 1,
                },
                minimapButton = {
                    name  = "Show minimap button",
                    desc  = "Toggle the minimap button on or off.",
                    type  = "toggle",
                    order = 2,
                    get   = function()
                        return not (addon.DB and addon.DB.char.minimapHide)
                    end,
                    set   = function(_, val)
                        if val then
                            addon:ShowMinimapButton()
                        else
                            addon:HideMinimapButton()
                        end
                    end,
                },

                debugHeader = {
                    name  = "Debug",
                    type  = "header",
                    order = 10,
                },
                debugToggle = {
                    name  = "Verbose debug output",
                    desc  = "When enabled, TOG Tools modules print diagnostic messages to chat (hook install confirmations, event fires, etc.). Off by default.",
                    type  = "toggle",
                    order = 11,
                    get   = function()
                        return addon.DB and addon.DB.global.debug or false
                    end,
                    set   = function(_, val)
                        if addon.DB then addon.DB.global.debug = val end
                    end,
                },

                devHeader = {
                    name  = "Developer Tools",
                    type  = "header",
                    order = 20,
                },
                devToolsToggle = {
                    name  = "Show developer tools",
                    desc  = "Reveal heavyweight developer/diagnostic tabs (e.g. the Item DB builder) in the main window. These are intended for addon development, not normal play — off by default.",
                    type  = "toggle",
                    width = "full",
                    order = 21,
                    get   = function()
                        return addon.DB and addon.DB.global.devTools or false
                    end,
                    set   = function(_, val)
                        if addon.DB then addon.DB.global.devTools = val and true or false end
                        -- Reveal / hide the dev tabs immediately if the main
                        -- window is open.
                        if addon.MainWindow and addon.MainWindow.Rebuild then
                            addon.MainWindow:Rebuild()
                        end
                    end,
                },
            },
        },

        modules = {
            name  = "Modules",
            type  = "group",
            order = 2,
            args  = {
                desc = {
                    type     = "description",
                    order    = 0,
                    name     = "Enable or disable individual TOG Tools features. A disabled feature is hidden from the main window's tab strip AND its engine stops running — but data already collected (mailbox snapshots, etc.) is preserved and reappears when you re-enable.\n",
                    fontSize = "medium",
                },
                -- Per-module toggle args are filled in at registration time
                -- from `addon.modules`. See the hooksecurefunc block below.
            },
        },

        -- ---------------------------------------------------------------
        -- Clear Data — centralised destructive actions, moved out of the
        -- per-tab Clear buttons that used to live next to each log's
        -- filter row. Consolidating here gives one obvious place to
        -- find every wipe operation, keeps the data tables themselves
        -- visually uncluttered, and uses AceConfig's native
        -- `confirm = true` popup for the two-step gate (replaces the
        -- old per-tab "Really? (5s)" countdown buttons).
        --
        -- Each `func` resolves the engine module from
        -- `addon.logCategories` and calls its `:ClearAll()`. Defensive
        -- existence checks so a disabled module doesn't crash the
        -- option handler.
        -- ---------------------------------------------------------------
        clearData = {
            name  = "Clear Data",
            type  = "group",
            order = 3,
            args  = {
                desc = {
                    type     = "description",
                    order    = 0,
                    name     = "Wipe persisted data for a specific log. Each button two-step confirms — Blizzard's popup appears between click and wipe. Capture continues afterwards; this only deletes what's already saved, it doesn't disable the engine. Use Settings > Modules to disable a module entirely.\n\n|cffff5555These actions cannot be undone.|r\n",
                    fontSize = "medium",
                },
                mailLog = {
                    type        = "execute",
                    order       = 10,
                    name        = "Clear Mail Log",
                    desc        = "Wipe every persisted mail entry across every alt on this account. Capture continues; this only deletes the saved history.",
                    confirm     = true,
                    confirmText = "Wipe every saved Mail Log entry?\n\n|cffff5555This cannot be undone.|r",
                    func        = function()
                        local m = addon.logCategories and addon.logCategories["mail"]
                        if m and m.ClearAll then m:ClearAll() end
                    end,
                },
                tradeLog = {
                    type        = "execute",
                    order       = 20,
                    name        = "Clear Trade Log",
                    desc        = "Wipe every persisted player-to-player trade across every alt on this account.",
                    confirm     = true,
                    confirmText = "Wipe every saved Trade Log entry?\n\n|cffff5555This cannot be undone.|r",
                    func        = function()
                        local m = addon.logCategories and addon.logCategories["trade"]
                        if m and m.ClearAll then m:ClearAll() end
                    end,
                },
                guildLog = {
                    type        = "execute",
                    order       = 30,
                    name        = "Clear Guild Log",
                    desc        = "Wipe every persisted guild event (joins / leaves / kicks / promotes / demotes / invites) across every guild bucket.",
                    confirm     = true,
                    confirmText = "Wipe every saved Guild Log entry across every guild bucket?\n\n|cffff5555This cannot be undone.|r",
                    func        = function()
                        local m = addon.logCategories and addon.logCategories["guild"]
                        if m and m.ClearAll then m:ClearAll() end
                    end,
                },
                guildBankLog = {
                    type        = "execute",
                    order       = 40,
                    name        = "Clear Guild Bank Log",
                    desc        = "Wipe every persisted guild bank transaction across every guild bucket. Capture continues the next time you open the guild bank.",
                    confirm     = true,
                    confirmText = "Wipe every saved Guild Bank Log transaction across every guild bucket?\n\n|cffff5555This cannot be undone.|r",
                    func        = function()
                        local m = addon.logCategories and addon.logCategories["guildbank"]
                        if m and m.ClearAll then m:ClearAll() end
                    end,
                },
                whisperLog = {
                    type        = "execute",
                    order       = 50,
                    name        = "Clear Whisper Log",
                    desc        = "Wipe every persisted whisper (in-game and Battle.net) across every alt on this account. Capture continues; this only deletes the saved history.",
                    confirm     = true,
                    confirmText = "Wipe every saved Whisper Log entry?\n\n|cffff5555This cannot be undone.|r",
                    func        = function()
                        local m = addon.logCategories and addon.logCategories["whispers"]
                        if m and m.ClearAll then m:ClearAll() end
                    end,
                },
            },
        },
    },
}

-- ---------------------------------------------------------------------------
-- Registration — hook OnInitialize so the DB is ready when we register.
-- Capture BOTH return values from AddToBlizOptions. The first is the panel
-- frame (unreliable as an open target — older Ace builds set categoryID on
-- it but Midnight builds don't). The second is the opaque numeric category
-- ID, which is what Settings.OpenToCategory wants. Pattern lifted from
-- FastGuildInvite / Grouper after extensive debugging — see FGI's
-- GUI/SettingsPanel.lua for the full backstory.
-- ---------------------------------------------------------------------------
local _blizPanel  = nil
local _categoryID = nil
hooksecurefunc(Ace, "OnInitialize", function()
    -- Build per-module toggles into OPTIONS.args.modules.args. Modules
    -- register themselves in `addon.modules` before OnInitialize fires
    -- (file-load-time registration), so the list is complete here. Order
    -- by `label` so the toggles appear in the same order tabs appear in
    -- the main window's strip. Multiply by 10 to leave room for an inline
    -- sub-group right after the Logs toggle (Log categories — Mail /
    -- Trade / Guild sub-toggles).
    local modulesArgs = OPTIONS.args.modules.args
    local sorted = {}
    for _, mod in pairs(addon.modules) do
        -- devOnly modules are gated solely by the Developer Tools switch in
        -- General; they don't get a per-module toggle here (end users
        -- shouldn't see a toggle for a tab they can't reach).
        if mod.label and mod.configKey and mod.tabKey and not mod.devOnly then
            sorted[#sorted + 1] = mod
        end
    end
    table.sort(sorted, function(a, b) return a.label < b.label end)
    local logsOrder = nil
    for i, mod in ipairs(sorted) do
        modulesArgs[mod.tabKey] = {
            name  = mod.label,
            desc  = "Show the " .. mod.label .. " tab and run its background engine. When off, the tab is hidden from the main window and its capture / event hooks short-circuit — re-enabling restores both.",
            type  = "toggle",
            width = "full",
            order = i * 10,
            get   = function()
                local s = addon.DB and addon.DB.global and addon.DB.global[mod.configKey]
                return s and s.enabled ~= false
            end,
            set   = function(_, val)
                local s = addon.DB and addon.DB.global and addon.DB.global[mod.configKey]
                if s then s.enabled = val and true or false end
                if addon.MainWindow and addon.MainWindow.Rebuild then
                    addon.MainWindow:Rebuild()
                end
            end,
        }
        if mod.tabKey == "logs" then logsOrder = i * 10 end
    end

    -- Inline sub-group of per-log-type toggles. Greys out when the Logs
    -- master toggle is off so users see the relationship at a glance.
    -- Each sub-engine (MailLog / future TradeLog / GuildLog) reads its
    -- own enabled flag via addon:IsLogCategoryEnabled(subKey), which
    -- ANDs the per-category flag with the Logs master flag — so disabling
    -- the master here doesn't require flipping every sub-category flag.
    if logsOrder then
        local function buildSubToggle(label, configKey, desc, order)
            return {
                type  = "toggle",
                name  = label,
                desc  = desc,
                order = order,
                width = "full",
                get   = function()
                    local s = addon.DB and addon.DB.global and addon.DB.global[configKey]
                    return s and s.enabled ~= false
                end,
                set   = function(_, val)
                    local s = addon.DB and addon.DB.global and addon.DB.global[configKey]
                    if s then s.enabled = val and true or false end
                    if addon.MainWindow and addon.MainWindow.Refresh then
                        addon.MainWindow:Refresh()
                    end
                end,
            }
        end
        modulesArgs.logCategoriesGroup = {
            type     = "group",
            inline   = true,
            name     = "Log categories",
            order    = logsOrder + 5,
            disabled = function() return not addon:IsModuleEnabled("logs") end,
            args = {
                mailLog = buildSubToggle(
                    "Mail Log",
                    "mailLog",
                    "Capture every mail received (inbox takes) and sent (composed via SendMail). When off, the Mail sub-tab is hidden and the capture hooks short-circuit. Existing entries are preserved and reappear on re-enable.",
                    10),
                tradeLog = buildSubToggle(
                    "Trade Log",
                    "tradeLog",
                    "Capture every completed player-to-player trade — both sides' items, money, and slot 7 enchants. Arena/BG trades are skipped. When off, the Trade sub-tab is hidden and the capture short-circuits. Existing entries are preserved and reappear on re-enable.",
                    20),
                guildLog = buildSubToggle(
                    "Guild Log",
                    "guildLog",
                    "Snapshot Blizzard's in-game guild event log (joins / leaves / kicks / promotes / demotes) and persist entries beyond the ~100-entry cap Blizzard keeps. Per-guild buckets so alts in different guilds keep independent histories. When off, the Guild sub-tab is hidden and the periodic query stops. Existing entries are preserved and reappear on re-enable.",
                    30),
                guildBankLog = buildSubToggle(
                    "Guild Bank Log",
                    "guildBankLog",
                    "Snapshot Blizzard's in-game guild bank transaction log every time you open the guild bank, and persist entries beyond the small rolling window Blizzard keeps. Per-guild buckets so alts in different guilds keep independent histories. Each entry carries a stable cross-viewer identifier reserved for future sync with TOGBankClassic and other TOG suite addons. When off, the Guild Bank sub-tab is hidden and capture short-circuits. Existing entries are preserved and reappear on re-enable. Classic Era has no guild banks; the tab stays present for a future TOGBankClassic integration.",
                    40),
                whisperLog = buildSubToggle(
                    "Whisper Log",
                    "whisperLog",
                    "Capture every whisper sent or received — in-game whispers and Battle.net whispers — into a per-alt searchable log. When off, the Whispers sub-tab is hidden and the chat event handlers short-circuit. Existing entries are preserved and reappear on re-enable.",
                    50),
            },
        }
    end

    AceConfig:RegisterOptionsTable("TOGTools", OPTIONS)
    _blizPanel, _categoryID = AceDialog:AddToBlizOptions("TOGTools", "TOG Tools")

    -- ---------------------------------------------------------------------
    -- Scoped tooltip re-anchor for OUR Settings panel.
    --
    -- AceConfigDialog renders option tooltips with `GameTooltip:SetOwner(
    -- widgetFrame, "ANCHOR_TOPRIGHT")` hard-coded. On WoW Classic / TBC at
    -- typical UI scales the right anchor puts long-description tooltips
    -- far off to the right of the option (sometimes off-screen). We want
    -- our smart `addon.Tooltip.AnchorTo` flip-anchor behaviour on these
    -- tooltips — but ONLY for OUR panel, not every AceConfigDialog
    -- consumer system-wide.
    --
    -- Hook target: `hooksecurefunc(GameTooltip, "SetOwner", ...)`. Fires
    -- AFTER every SetOwner call — this is the right join point because:
    --   * AceConfigDialog calls SetOwner each time you slide between
    --     options, even when the tooltip stays continuously visible —
    --     a `GameTooltip:OnShow` hook would miss those owner changes
    --     since OnShow only fires on hidden→shown transitions.
    --   * Re-anchoring INSIDE the SetOwner hook happens BEFORE the
    --     subsequent SetText / AddLine / Show calls in AceConfigDialog's
    --     sequence, so by the time the tooltip is actually shown its
    --     points already match our flip rules.
    --
    -- Gated on `ourPanelShown` (flipped by `_blizPanel:OnShow/OnHide`)
    -- so the system-wide SetOwner hook is a cheap no-op except while
    -- the TOG Tools page is the active Settings panel.
    -- ---------------------------------------------------------------------
    -- ---------------------------------------------------------------------
    -- Scoped tooltip re-anchor for OUR Settings panel.
    --
    -- AceConfigDialog hard-codes `GameTooltip:SetOwner(widgetFrame,
    -- "ANCHOR_TOPRIGHT")` on every option tooltip — fine for some
    -- layouts, but at TBC / Anniversary UI scales the right anchor
    -- puts long-description tooltips far off to the right, often
    -- partially off-screen. We want our smart `addon.Tooltip.AnchorTo`
    -- flip-and-offset behaviour for these tooltips.
    --
    -- An earlier always-on global `hooksecurefunc` in `GUI/UI.lua`
    -- broke this by also re-anchoring Blizzard's native tooltips
    -- (corpse / unit / item / etc.) — user reported visible wonkiness.
    -- Scoped here behind `_blizPanel:OnShow/OnHide` so the SetOwner
    -- hook is a cheap no-op everywhere except while the TOG Tools
    -- page is the active Settings panel.
    --
    -- `hooksecurefunc` on SetOwner (not OnShow) because AceConfigDialog
    -- re-owners an already-visible tooltip when the user slides between
    -- options without it hiding in between — OnShow only fires on
    -- hidden→shown transitions and would miss those.
    -- ---------------------------------------------------------------------
    if _blizPanel and _blizPanel.HookScript then
        local ourPanelShown = false

        _blizPanel:HookScript("OnShow", function() ourPanelShown = true  end)
        _blizPanel:HookScript("OnHide", function() ourPanelShown = false end)

        hooksecurefunc(GameTooltip, "SetOwner", function(self_, owner)
            if not ourPanelShown then return end
            if not owner then return end
            if addon.Tooltip and addon.Tooltip.AnchorTo then
                addon.Tooltip.AnchorTo(self_, owner)
            end
        end)
    end
end)

-- ---------------------------------------------------------------------------
-- Direct open — navigates the native Blizzard Interface Options panel to
-- the TOG Tools page. Called by the gear icon and by RMB on the minimap.
--
-- Detection order (API presence, not version flag):
--   1. Settings.OpenToCategory(categoryID)  — Retail 10.0+ AND Classic
--      Era 1.15+. Pass the OPAQUE NUMERIC ID returned by AddToBlizOptions;
--      passing the string "TOG Tools" crashes on Retail Midnight because
--      C_SettingsUtil.OpenSettingsPanel validates the arg as an integer
--      and the string fails the type check. Called twice so the panel
--      reliably navigates past the Settings landing page into our
--      sub-category (calling once sometimes only opens the root).
--   2. InterfaceOptionsFrame_OpenToCategory(panel)  — older Classic
--      (TBC / Wrath / Cata / Mists pre-1.15) — takes the panel frame,
--      not the ID. Also called twice for the same navigation reason.
-- ---------------------------------------------------------------------------
function addon:OpenSettings()
    -- Set the inhibit flag BEFORE Settings actually opens — the call
    -- below triggers CloseSpecialWindows synchronously, which would
    -- otherwise fire the proxy OnHide and close the main window. The
    -- one-time SettingsPanel:OnHide hook is also installed here so the
    -- flag gets cleared (deferred a frame) whenever Settings closes.
    if addon.MainWindow then
        addon.MainWindow._settingsOpen = true
        if addon.MainWindow._HookSettingsClose then
            addon.MainWindow:_HookSettingsClose()
        end
    end
    if Settings and Settings.OpenToCategory and _categoryID then
        Settings.OpenToCategory(_categoryID)
        Settings.OpenToCategory(_categoryID)
    elseif InterfaceOptionsFrame_OpenToCategory and _blizPanel then
        InterfaceOptionsFrame_OpenToCategory(_blizPanel)
        InterfaceOptionsFrame_OpenToCategory(_blizPanel)
    end
end
