-- EnglishLinks - database-backed English link replacer
-- Author: NightsPrower
local EL = {}
EnglishLinks = EL
EnglishLinks_Spells          = {}
EnglishLinks_Items           = {}
EnglishLinks_Mounts          = {}
EnglishLinks_Pets            = {}
EnglishLinks_TradeSkillCats  = {}
EnglishLinks_Achievements    = {}
EnglishLinks_Currencies      = {}
EnglishLinks_Keystones       = {}
EnglishLinks_JournalInstances  = {}
EnglishLinks_JournalEncounters = {}
EnglishLinks_TransmogSets    = {}

local function loadInto(target, data)
    for id, name in data:gmatch("(%d+)\t([^\r\n]+)") do
        target[tonumber(id)] = name
    end
end

function EnglishLinks_LoadChunk_spells(data)         loadInto(EnglishLinks_Spells,          data) end
function EnglishLinks_LoadChunk_items(data)          loadInto(EnglishLinks_Items,           data) end
function EnglishLinks_LoadChunk_mounts(data)         loadInto(EnglishLinks_Mounts,          data) end
function EnglishLinks_LoadChunk_pets(data)           loadInto(EnglishLinks_Pets,            data) end
function EnglishLinks_LoadChunk_tradeskillcats(data) loadInto(EnglishLinks_TradeSkillCats,  data) end
function EnglishLinks_LoadChunk_achievements(data)   loadInto(EnglishLinks_Achievements,    data) end
function EnglishLinks_LoadChunk_currencies(data)     loadInto(EnglishLinks_Currencies,      data) end
function EnglishLinks_LoadChunk_keystones(data)      loadInto(EnglishLinks_Keystones,       data) end
function EnglishLinks_LoadChunk_journalinstances(data)  loadInto(EnglishLinks_JournalInstances,  data) end
function EnglishLinks_LoadChunk_journalencounters(data) loadInto(EnglishLinks_JournalEncounters, data) end
function EnglishLinks_LoadChunk_transmogsets(data)   loadInto(EnglishLinks_TransmogSets,    data) end

-- =============================================================
-- ITEM LINKS (posicional - suporta |A:...|a no TWW)
-- =============================================================
local function replaceItemLinks(msg)
    local result = {}
    local pos = 1
    while pos <= #msg do
        local hstart = msg:find("|Hitem:", pos, true)
        if not hstart then result[#result+1] = msg:sub(pos); break end
        result[#result+1] = msg:sub(pos, hstart - 1)
        local payloadEnd = msg:find("|h", hstart + 7, true)
        if not payloadEnd then result[#result+1] = msg:sub(hstart); break end
        local payload   = msg:sub(hstart + 7, payloadEnd - 1)
        local itemID    = tonumber(payload:match("^(%d+)"))
        local dispStart = payloadEnd + 2
        if msg:sub(dispStart, dispStart) ~= "[" then
            result[#result+1] = msg:sub(hstart, dispStart - 1)
            pos = dispStart
        else
            local dispEnd = msg:find("]|h", dispStart + 1, true)
            if not dispEnd then result[#result+1] = msg:sub(hstart); break end
            local display = msg:sub(dispStart + 1, dispEnd - 1)
            local engName = itemID and EnglishLinks_Items[itemID]
            if engName and engName ~= "" then
                local qs = display:match("(|A:.+|a)%s*$") or ""
                if qs ~= "" then
                    result[#result+1] = "|Hitem:" .. payload .. "|h[" .. engName .. " " .. qs .. "]|h"
                else
                    result[#result+1] = "|Hitem:" .. payload .. "|h[" .. engName .. "]|h"
                end
            else
                result[#result+1] = "|Hitem:" .. payload .. "|h[" .. display .. "]|h"
            end
            pos = dispEnd + 3
        end
    end
    return table.concat(result)
end

-- =============================================================
-- PROCESSADOR GENERICO
-- =============================================================
local function makeReplacer(linkType, db, getID)
    return function(msg)
        if not msg:find("|H" .. linkType .. ":", 1, true) then return msg end
        return (msg:gsub("|H" .. linkType .. ":([^|]+)|h%[(.-)%]|h",
            function(payload, display)
                local id   = getID(payload)
                local name = id and db[id]
                if name and name ~= "" then
                    return "|H" .. linkType .. ":" .. payload .. "|h[" .. name .. "]|h"
                end
                return "|H" .. linkType .. ":" .. payload .. "|h[" .. display .. "]|h"
            end))
    end
end

local firstNum  = function(p) return tonumber(p:match("^(%d+)")) end
local secondNum = function(p)
    local i = 0
    for v in p:gmatch("[^:]+") do i = i + 1; if i == 2 then return tonumber(v) end end
end

local replaceSpells       = makeReplacer("spell",       EnglishLinks_Spells,        firstNum)
local replaceMounts       = makeReplacer("mount",       EnglishLinks_Mounts,        firstNum)
local replacePets         = makeReplacer("battlepet",   EnglishLinks_Pets,          firstNum)
local replaceTrade        = makeReplacer("trade",       EnglishLinks_Spells,        secondNum)
local replaceAchievements = makeReplacer("achievement", EnglishLinks_Achievements,  firstNum)
local replaceCurrencies   = makeReplacer("currency",    EnglishLinks_Currencies,    firstNum)
-- Keystone para AddMessage: captura |cnIQ4:|Hkeystone:...|h[...]|h|r completo
local function replaceKeystones(msg)
    if not msg:find("|Hkeystone:", 1, true) then return msg end
    return (msg:gsub("(|cnIQ%d+:)(|Hkeystone:)([^|]+)(|h%[.-%]|h)(|r?)",
        function(colorPfx, hh, payload, dispPart, rsfx)
            local parts = {}
            for v in payload:gmatch("[^:]+") do parts[#parts+1] = v end
            local mapID = tonumber(parts[2])
            local level = parts[3] or ""
            local engName = mapID and EnglishLinks_Keystones[mapID]
            if engName and engName ~= "" then
                local newDisp = engName
                if level ~= "" and level ~= "0" then newDisp = newDisp .. " (" .. level .. ")" end
                return colorPfx .. hh .. payload .. "|h[" .. newDisp .. "]|h" .. rsfx
            end
            return colorPfx .. hh .. payload .. dispPart .. rsfx
        end))
end

-- Keystone para InsertLink/editbox: sem prefixo |cnIQ4: (link nao tem cor ainda)
local function replaceKeystoneInsert(link)
    if not link:find("|Hkeystone:", 1, true) then return link end
    return (link:gsub("(|Hkeystone:)([^|]+)(|h%[.-%]|h)",
        function(hh, payload, dispPart)
            local parts = {}
            for v in payload:gmatch("[^:]+") do parts[#parts+1] = v end
            local mapID = tonumber(parts[2])
            local level = parts[3] or ""
            local engName = mapID and EnglishLinks_Keystones[mapID]
            if engName and engName ~= "" then
                local newDisp = engName
                if level ~= "" and level ~= "0" then newDisp = newDisp .. " (" .. level .. ")" end
                return hh .. payload .. "|h[" .. newDisp .. "]|h"
            end
            return hh .. payload .. dispPart
        end))
end
local replaceTransmogSets = makeReplacer("transmogset", EnglishLinks_TransmogSets,  firstNum)
local replaceTalents      = makeReplacer("talent",      EnglishLinks_Spells,        firstNum)
local replaceEnchants     = makeReplacer("enchant",     EnglishLinks_Spells,        firstNum)

local function replaceJournal(msg)
    if not msg:find("|Hjournal:", 1, true) then return msg end
    return (msg:gsub("|Hjournal:([^|]+)|h%[(.-)%]|h",
        function(payload, display)
            local parts = {}
            for v in payload:gmatch("[^:]+") do parts[#parts+1] = v end
            local jtype = tonumber(parts[1]) or 0
            local jid   = tonumber(parts[2])
            local name
            if jtype == 0 then name = jid and EnglishLinks_JournalInstances[jid]
            else               name = jid and EnglishLinks_JournalEncounters[jid] end
            if name and name ~= "" then
                return "|Hjournal:" .. payload .. "|h[" .. name .. "]|h"
            end
            return "|Hjournal:" .. payload .. "|h[" .. display .. "]|h"
        end))
end

local function doReplaceAll(msg)
    if msg:find("|Hitem:",        1, true) then msg = replaceItemLinks(msg)    end
    if msg:find("|Hspell:",       1, true) then msg = replaceSpells(msg)       end
    if msg:find("|Hmount:",       1, true) then msg = replaceMounts(msg)       end
    if msg:find("|Hbattlepet:",   1, true) then msg = replacePets(msg)         end
    if msg:find("|Htrade:",       1, true) then msg = replaceTrade(msg)        end
    if msg:find("|Hachievement:", 1, true) then msg = replaceAchievements(msg) end
    if msg:find("|Hcurrency:",    1, true) then msg = replaceCurrencies(msg)   end
    if msg:find("|Hkeystone:",    1, true) then msg = replaceKeystones(msg)    end
    if msg:find("|Hjournal:",     1, true) then msg = replaceJournal(msg)      end
    if msg:find("|Htransmogset:", 1, true) then msg = replaceTransmogSets(msg) end
    if msg:find("|Htalent:",      1, true) then msg = replaceTalents(msg)      end
    if msg:find("|Henchant:",     1, true) then msg = replaceEnchants(msg)     end
    return msg
end

-- Respeita chatEnabled
local function replaceLinkDisplay(msg)
    if type(msg) ~= "string" then return msg end
    if not EnglishLinksDB or not EnglishLinksDB.chatEnabled then return msg end
    return doReplaceAll(msg)
end

-- Sempre converte ao enviar (SendChatMessage / BNSendWhisper)
local function replaceLinkForSend(msg)
    if type(msg) ~= "string" then return msg end
    return doReplaceAll(msg)
end

EL.ReplaceLinks = replaceLinkDisplay

-- =============================================================
-- HOOKS EDITBOX
-- =============================================================
local _processingEditBox = false

local function hookEditBox(editBox)
    if not editBox or editBox._EL_hooked then return end
    if not editBox.GetText or not editBox.SetText then return end
    editBox._EL_hooked = true
    editBox:HookScript("OnTextChanged", function(self, userInput)
        if not userInput or _processingEditBox then return end
        local text = self:GetText()
        if not text or not text:find("|H", 1, true) then return end
        if EnglishLinksDB and EnglishLinksDB.chatEnabled and not EnglishLinksDB.reverseMode then
            local newText = text
            if newText:find("|Hitem:", 1, true) then
                newText = replaceItemLinks(newText)
            end
            -- Keystones: NAO modificar no editbox (SetText invalida o link)
            -- A substituicao acontece no AddMessage ao aparecer no chat
            if newText ~= text then
                _processingEditBox = true
                local cursor = self:GetCursorPosition()
                self:SetText(newText)
                self:SetCursorPosition(cursor)
                _processingEditBox = false
            end
        end
    end)
end

local function hookEditBoxesInFrame(frame, depth)
    if not frame or (depth or 0) > 5 then return end
    if frame.GetText and frame.SetText and frame.HookScript then hookEditBox(frame) end
    if frame.GetChildren then
        for _, child in ipairs({ frame:GetChildren() }) do
            hookEditBoxesInFrame(child, (depth or 0) + 1)
        end
    end
end

local function hookAllEditBoxes()
    for i = 1, 50 do
        local fr = _G["ChatFrame" .. i]
        if fr then
            local eb = fr.editBox or _G["ChatFrame" .. i .. "EditBox"]
            if eb then hookEditBox(eb) end
        end
    end
    for _, n in ipairs({ "ChatFrameEditBox", "FloatingChatFrameEditBox", "BNConversationEditBox" }) do
        local eb = _G[n]; if eb then hookEditBox(eb) end
    end
    if BNConversationFrame then hookEditBoxesInFrame(BNConversationFrame, 0) end
    for i = 1, 20 do
        local f = _G["BNConversationFrame" .. i]
        if f then hookEditBoxesInFrame(f, 0) end
    end
    for gname, obj in pairs(_G) do
        if type(gname) == "string" and type(obj) == "table" and obj.GetChildren then
            local lower = gname:lower()
            if lower:find("whisper") or lower:find("bnconv") then
                hookEditBoxesInFrame(obj, 0)
            end
        end
    end
end

-- =============================================================
-- HOOKS CHAT FRAMES
-- =============================================================
local function hookChatFrames()
    for i = 1, 50 do
        local frame = _G["ChatFrame" .. i]
        if frame and frame.AddMessage and not frame._EL_msghooked then
            frame._EL_msghooked = true
            local origAdd = frame.AddMessage
            frame.AddMessage = function(self, msg, ...)
                if type(msg) == "string" then msg = replaceLinkDisplay(msg) end
                return origAdd(self, msg, ...)
            end
        end
    end
end

-- =============================================================
-- HOOKS ENVIO
-- =============================================================
local function hookSendFunctions()
    if SendChatMessage and not SendChatMessage._EL_wrapped then
        local orig = SendChatMessage
        SendChatMessage = function(msg, chatType, language, target)
            if type(msg) == "string" and EnglishLinksDB and EnglishLinksDB.chatEnabled then
                msg = replaceLinkForSend(msg)
            end
            return orig(msg, chatType, language, target)
        end
        SendChatMessage._EL_wrapped = true
    end
    if BNSendWhisper and not BNSendWhisper._EL_wrapped then
        local orig = BNSendWhisper
        BNSendWhisper = function(presenceID, msg)
            if type(msg) == "string" and EnglishLinksDB and EnglishLinksDB.chatEnabled then
                msg = replaceLinkForSend(msg)
            end
            return orig(presenceID, msg)
        end
        BNSendWhisper._EL_wrapped = true
    end
end

-- =============================================================
-- NEED/GREED ROLLS
-- =============================================================
local function hookLootRollFrame()
    if not EnglishLinksDB or not EnglishLinksDB.lootEnabled then return end
    if LootRollFrame_SetUp and not LootRollFrame_SetUp._EL_wrapped then
        hooksecurefunc("LootRollFrame_SetUp", function(frame, rollID)
            if not EnglishLinksDB or not EnglishLinksDB.lootEnabled then return end
            local link = GetLootRollItemLink(rollID)
            if not link then return end
            local itemID = tonumber(link:match("|Hitem:(%d+)"))
            local engName = itemID and EnglishLinks_Items[itemID]
            if engName and engName ~= "" then
                local nameText = frame.ItemName or _G[(frame:GetName() or "") .. "ItemName"]
                if nameText and nameText.SetText then
                    nameText:SetText(engName)
                end
            end
        end)
        LootRollFrame_SetUp._EL_wrapped = true
    end
end

-- =============================================================
-- LOOT FINAL MYTHIC+
-- =============================================================
local function hookMythicPlusEndFrame()
    if not EnglishLinksDB or not EnglishLinksDB.lootEnabled then return end

    local function scanAndReplace(f, depth)
        if (depth or 0) > 8 then return end
        if f.GetRegions then
            for _, region in ipairs({ f:GetRegions() }) do
                if region.GetText and region.SetText then
                    local txt = region:GetText()
                    if txt and txt:find("|Hitem:", 1, true) then
                        region:SetText(doReplaceAll(txt))
                    end
                end
            end
        end
        if f.GetChildren then
            for _, child in ipairs({ f:GetChildren() }) do
                scanAndReplace(child, (depth or 0) + 1)
            end
        end
    end

    local function tryHook()
        for _, fname in ipairs({ "EndOfDungeonFrame", "ScenarioChallengeModeFrame", "ChallengeModeLootFrame" }) do
            local fr = _G[fname]
            if fr and not fr._EL_hooked then
                fr._EL_hooked = true
                fr:HookScript("OnShow", function(self)
                    if EnglishLinksDB and EnglishLinksDB.lootEnabled then
                        C_Timer.After(0.2, function() scanAndReplace(self, 0) end)
                    end
                end)
            end
        end
    end

    tryHook()
    C_Timer.After(5, tryHook)
    C_Timer.After(30, tryHook)
end

-- =============================================================
-- INSTALAR HOOKS
-- =============================================================
local setupFrame = CreateFrame("Frame")
setupFrame:RegisterEvent("PLAYER_LOGIN")
setupFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
setupFrame:SetScript("OnEvent", function(self, event)
    hookAllEditBoxes()
    hookChatFrames()
    hookSendFunctions()
    hookLootRollFrame()
    hookMythicPlusEndFrame()

    if ChatEdit_InsertLink and not ChatEdit_InsertLink._EL_wrapped then
        local orig = ChatEdit_InsertLink
        ChatEdit_InsertLink = function(link, ...)
            if type(link) == "string" and EnglishLinksDB and EnglishLinksDB.chatEnabled then
                -- So substituir itens (keystone via SetText/InsertLink invalida o link)
                if link:find("|Hitem:", 1, true) then link = replaceItemLinks(link) end
            end
            return orig(link, ...)
        end
        ChatEdit_InsertLink._EL_wrapped = true
    end

    if event == "PLAYER_LOGIN" then self:UnregisterEvent("PLAYER_LOGIN") end
end)

-- =============================================================
-- SLASH: /el
-- =============================================================
SLASH_ENGLISHLINKS1 = "/el"
SlashCmdList.ENGLISHLINKS = function(msg)
    msg = (msg or ""):match("^%s*(.-)%s*$")

    if msg:lower() == "status" then
        local s,i,m,p,a,c,k,j,t = 0,0,0,0,0,0,0,0,0
        for _ in pairs(EnglishLinks_Spells)            do s=s+1 end
        for _ in pairs(EnglishLinks_Items)             do i=i+1 end
        for _ in pairs(EnglishLinks_Mounts)            do m=m+1 end
        for _ in pairs(EnglishLinks_Pets)              do p=p+1 end
        for _ in pairs(EnglishLinks_Achievements)      do a=a+1 end
        for _ in pairs(EnglishLinks_Currencies)        do c=c+1 end
        for _ in pairs(EnglishLinks_Keystones)         do k=k+1 end
        for _ in pairs(EnglishLinks_JournalInstances)  do j=j+1 end
        for _ in pairs(EnglishLinks_JournalEncounters) do j=j+1 end
        for _ in pairs(EnglishLinks_TransmogSets)      do t=t+1 end
        print("|cff66ccffEnglish Links|r spells:"..s.." items:"..i.." mounts:"..m
            .." pets:"..p.." achieve:"..a.." currency:"..c
            .." keystones:"..k.." journal:"..j.." transmog:"..t)
        return
    end

    -- /el sem args = abrir config
    if EnglishLinks_Config and EnglishLinks_Config.openPanel then
        EnglishLinks_Config.openPanel()
    end
end

print("|cff66ccffEnglish Links|r loaded.")
