-- TOG Tools — Name Prefix module (engine)
-- Prepends a configurable nickname prefix to outgoing chat messages.
--
-- Hook strategy (most reliable across all versions):
--   Retail 12.0+  : EventRegistry "ChatFrame.OnEditBoxPreSendText"
--   Classic/older : wrap the global ChatEdit_OnEnterPressed (the editbox
--                   Enter-key script handler), which is upstream of
--                   ChatEdit_SendText in the user-typed chat flow.
--
-- We deliberately do NOT wrap ChatEdit_SendText on Classic. OPie's Rewire
-- library dispatches macro lines via `securecall(ChatEdit_SendText, box,
-- false)` (Libs/ActionBook/Rewire.lua), and replacing that global with a
-- non-secure wrapper taints OPie's secure execution context — breaking
-- any OPie ring entry whose action is delivered as macrotext (e.g. custom
-- mount macros). ChatEdit_OnEnterPressed isn't on OPie's path, so taint
-- there doesn't propagate. The user-typed chat flow still goes through
-- it before reaching ChatEdit_SendText, so prefixing still works.
--
-- The hook only modifies editBox:SetText() BEFORE the message is sent.
-- It never calls SendChatMessage directly.

local _, addon = ...
local gv = addon.gameVersion

local NamePrefix = {}
addon.NamePrefix = NamePrefix

-- ---------------------------------------------------------------------------
-- Internal state
-- ---------------------------------------------------------------------------
local _hooked = false   -- set true once the hook is installed

-- ---------------------------------------------------------------------------
-- Core: decide whether to prefix and do it
-- ---------------------------------------------------------------------------
local function ModifyMessage(editBox)
    local cfg = addon.DB and addon.DB.char.namePrefix
    if not cfg then return end
    if not cfg.enabled then return end

    -- InCombatLockdown guard: SetText taints the protected SendChatMessage
    -- call that follows on some clients. Message still sends as-is.
    if InCombatLockdown() then return end

    local nick = cfg.nickname
    if not nick or nick == "" then return end

    -- Optionally suppress when nickname matches current character name
    if cfg.hideIfCharName then
        local charName = UnitName("player")
        if charName and nick == charName then return end
    end

    local msg = editBox:GetText()
    if not msg or msg == "" then return end

    -- Skip slash commands and macros entirely.
    if string.sub(msg, 1, 1) == "/" then return end

    -- Skip messages starting with "!" (addon commands like !keys)
    if cfg.skipExclamation and string.sub(msg, 1, 1) == "!" then return end

    -- Determine chat type
    local chatType = editBox:GetAttribute("chatType") or editBox.chatType or "SAY"

    local shouldPrefix = false
    if     chatType == "GUILD"         and cfg.guild    then shouldPrefix = true
    elseif chatType == "OFFICER"       and cfg.officer  then shouldPrefix = true
    elseif chatType == "PARTY"         and cfg.party    then shouldPrefix = true
    elseif chatType == "PARTY_LEADER"  and cfg.party    then shouldPrefix = true
    elseif chatType == "RAID"          and cfg.raid     then shouldPrefix = true
    elseif chatType == "RAID_WARNING"  and cfg.raid     then shouldPrefix = true
    elseif chatType == "INSTANCE_CHAT" and cfg.instance then shouldPrefix = true
    elseif chatType == "CHANNEL"       and cfg.channel and cfg.channel ~= "" then
        local channelTarget = editBox:GetAttribute("channelTarget") or editBox.channelTarget
        if channelTarget then
            local _, chname = GetChannelName(channelTarget)
            if chname and strupper(cfg.channel) == strupper(chname) then
                shouldPrefix = true
            end
        end
    end

    if not shouldPrefix then return end

    -- Build prefix from format string (safe substitution)
    local fmt = (cfg.format and cfg.format ~= "") and cfg.format or "(%s): "
    local prefix = string.format(fmt, nick)
    editBox:SetText(prefix .. msg)
end

-- ---------------------------------------------------------------------------
-- Hook installation
-- ---------------------------------------------------------------------------
function NamePrefix:Install()
    if _hooked then return end

    if gv.isRetail120Plus and EventRegistry then
        -- Retail 12.0+: this event fires BEFORE SendText reads the editbox,
        -- so our SetText reaches the message that gets sent.
        EventRegistry:RegisterCallback(
            "ChatFrame.OnEditBoxPreSendText",
            function(_event, editBox)
                local ok, err = pcall(ModifyMessage, editBox)
                if not ok and addon.DB and addon.DB.char.namePrefix.enabled then
                    print("|cffFF8000TOGTools|r NamePrefix error: " .. tostring(err))
                end
            end,
            NamePrefix
        )
        _hooked = true
    else
        -- Classic / older Retail: wrap ChatEdit_OnEnterPressed so ModifyMessage
        -- runs BEFORE the editbox's text is read and dispatched. hooksecurefunc
        -- fires post-call so we cannot use it; we replace the global.
        --
        -- NOT ChatEdit_SendText — OPie's Rewire calls that via securecall and
        -- our wrapper would taint its secure context, breaking OPie macrotext
        -- ring entries (custom mounts, etc.).
        local orig = ChatEdit_OnEnterPressed
        if orig then
            ChatEdit_OnEnterPressed = function(editBox, ...)
                local ok, err = pcall(ModifyMessage, editBox)
                if not ok and addon.DB and addon.DB.char.namePrefix.enabled then
                    print("|cffFF8000TOGTools|r NamePrefix error: " .. tostring(err))
                end
                return orig(editBox, ...)
            end
            _hooked = true
        end
    end
end

function NamePrefix:OnEnable()
    self:Install()
end

-- ---------------------------------------------------------------------------
-- Register as a module (engine only — no tab key; tab is in NamePrefixTab.lua)
-- ---------------------------------------------------------------------------
-- The tab registration lives in GUI/NamePrefixTab.lua which loads after this
-- file and merges its Draw/WINDOW_SIZE into addon.modules["nameprefix"].
addon.modules["nameprefix"] = NamePrefix
