local addonName, NS = ...
local GR = NS.GR

-- =========================================================================
-- UI: LEFT PANE (CONTROLS)
-- =========================================================================

function GR:BuildLeftPane(parent)
    local y = -15
    local function AddText(txt, font, dy)
        local t = parent:CreateFontString(nil, "OVERLAY", font)
        t:SetPoint("TOPLEFT", 15, y + (dy or 0)); t:SetText(txt)
    end
    
    AddText(NS.L["SEARCH_FILTERS"] or "SEARCH FILTERS", "GameFontNormal", 0); y = y - 30
    
    local dFilt = CreateFrame("Button", nil, parent); NS.CreateBg(dFilt, 0.2, 0.2, 0.2, 1)
    dFilt:SetSize(180, 24); dFilt:SetPoint("TOPLEFT", 15, y)
    local dFiltTxt = dFilt:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); dFiltTxt:SetPoint("LEFT", 5, 0); dFiltTxt:SetText(NS.L["FILTER_DEFAULT_ALL"] or "Filter: Default (All)")
    self.dFiltTxt = dFiltTxt
    
    local dFiltArrow = dFilt:CreateTexture(nil, "ARTWORK")
    dFiltArrow:SetTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up")
    dFiltArrow:SetSize(20, 20)
    dFiltArrow:SetPoint("RIGHT", -2, 0)
    
    local dFiltList = CreateFrame("Frame", nil, dFilt); NS.CreateBg(dFiltList, 0.1, 0.1, 0.1, 1); NS.CreateBorder(dFiltList, 0.5, 0.5, 0.5, 1)
    dFiltList:SetSize(180, 50); dFiltList:SetPoint("TOPLEFT", dFilt, "BOTTOMLEFT", 0, -2); dFiltList:SetFrameStrata("FULLSCREEN_DIALOG"); dFiltList:Hide()
    self.dFiltList = dFiltList; 
    -- RefreshMainFilterList is logic, handled in Engine.lua
    if GR.RefreshMainFilterList then GR:RefreshMainFilterList() end
    
    dFilt:SetScript("OnClick", function() 
        if GR.RefreshMainFilterList then GR:RefreshMainFilterList() end
        dFiltList:SetShown(not dFiltList:IsShown()) 
    end)
    
    local cIcon = CreateFrame("Button", nil, parent); cIcon:SetSize(24, 24); cIcon:SetPoint("LEFT", dFilt, "RIGHT", 5, 0)
    cIcon:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
    cIcon:SetScript("OnClick", function() if GR.FilterFrame then GR.FilterFrame.OpenCreate() end end)

    local eIcon = CreateFrame("Button", nil, parent); eIcon:SetSize(24, 24); eIcon:SetPoint("LEFT", cIcon, "RIGHT", 2, 0)
    eIcon:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up") 
    eIcon:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
    eIcon:SetScript("OnClick", function() 
        if GR.selectedFilterData then 
            GR.FilterFrame.OpenEdit(GR.selectedFilterData)
        else
            print("|cffff0000GRS: Cannot edit Default filter. Select a custom filter first.|r")
        end
    end)
    
    local dIcon = CreateFrame("Button", nil, parent); dIcon:SetSize(24, 24); dIcon:SetPoint("LEFT", eIcon, "RIGHT", 2, 0)
    dIcon:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
    dIcon:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
    dIcon:SetScript("OnClick", function()
        if GR.DeleteCurrentFilter then GR:DeleteCurrentFilter() end
    end)
    dIcon:SetScript("OnEnter", function(s)
        GameTooltip:SetOwner(s, "ANCHOR_RIGHT")
        GameTooltip:SetText("Delete Selected Filter")
        GameTooltip:AddLine("Removes the currently selected custom filter.", 1, 0.5, 0.5, true)
        GameTooltip:Show()
    end)
    dIcon:SetScript("OnLeave", function() GameTooltip:Hide() end)
    
    y = y - 40; AddText("Level Range", "GameFontHighlightSmall", 0); y = y - 20
    
    -- Container frame for level range row
    local lvlFrame = CreateFrame("Frame", nil, parent)
    lvlFrame:SetSize(130, 24); lvlFrame:SetPoint("TOPLEFT", 15, y)
    
    -- Min input container
    local minBox = CreateFrame("Frame", nil, lvlFrame)
    minBox:SetSize(45, 20); minBox:SetPoint("LEFT", 0, 0)
    NS.CreateBg(minBox, 0.1, 0.1, 0.1, 0.8); NS.CreateBorder(minBox, 0.3, 0.3, 0.3, 1)
    
    local lvlMin = CreateFrame("EditBox", nil, minBox)
    lvlMin:SetPoint("TOPLEFT", 4, -2); lvlMin:SetPoint("BOTTOMRIGHT", -4, 2)
    lvlMin:SetFontObject(GameFontHighlightSmall); lvlMin:SetAutoFocus(false)
    lvlMin:SetMaxLetters(3); lvlMin:SetJustifyH("LEFT")
    lvlMin:SetText(tostring(self.db.levelMin or 1))
    lvlMin:SetCursorPosition(0)
    lvlMin:SetScript("OnEscapePressed", function(s) s:ClearFocus() end)
    lvlMin:SetScript("OnEnterPressed", function(s) s:ClearFocus() end)
    lvlMin:SetScript("OnEditFocusLost", function(s)
        local v = tonumber((s:GetText():gsub("%D", ""))) or 1
        if v < 1 then v = 1 end; if v > 999 then v = 999 end
        s:SetText(tostring(v)); s:SetCursorPosition(0); self.db.levelMin = v
    end)
    minBox:SetScript("OnMouseDown", function() lvlMin:SetFocus() end)
    self.LevelMinInput = lvlMin
    
    -- Dash
    local dash = lvlFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    dash:SetPoint("LEFT", minBox, "RIGHT", 5, 0); dash:SetText("-")
    
    -- Max input container
    local maxBox = CreateFrame("Frame", nil, lvlFrame)
    maxBox:SetSize(45, 20); maxBox:SetPoint("LEFT", dash, "RIGHT", 5, 0)
    NS.CreateBg(maxBox, 0.1, 0.1, 0.1, 0.8); NS.CreateBorder(maxBox, 0.3, 0.3, 0.3, 1)
    
    local lvlMax = CreateFrame("EditBox", nil, maxBox)
    lvlMax:SetPoint("TOPLEFT", 4, -2); lvlMax:SetPoint("BOTTOMRIGHT", -4, 2)
    lvlMax:SetFontObject(GameFontHighlightSmall); lvlMax:SetAutoFocus(false)
    lvlMax:SetMaxLetters(3); lvlMax:SetJustifyH("LEFT")
    lvlMax:SetText(tostring(self.db.levelMax or 999))
    lvlMax:SetCursorPosition(0)
    lvlMax:SetScript("OnEscapePressed", function(s) s:ClearFocus() end)
    lvlMax:SetScript("OnEnterPressed", function(s) s:ClearFocus() end)
    lvlMax:SetScript("OnEditFocusLost", function(s)
        local v = tonumber((s:GetText():gsub("%D", ""))) or 999
        if v < 1 then v = 1 end; if v > 999 then v = 999 end
        s:SetText(tostring(v)); s:SetCursorPosition(0); self.db.levelMax = v
    end)
    maxBox:SetScript("OnMouseDown", function() lvlMax:SetFocus() end)
    self.LevelMaxInput = lvlMax
    
    y = y - 30; AddText("Scan Scope", "GameFontHighlightSmall", 0); y = y - 20
    
    local function MkRemBtn(text, mode, x)
        local b = CreateFrame("Button", nil, parent); NS.CreateBg(b, 0.2, 0.2, 0.2, 1); b:SetSize(80, 20); b:SetPoint("TOPLEFT", 15 + x, y)
        local t = b:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); t:SetPoint("CENTER"); t:SetText(text)
        b.bg = b.bg
        b:SetScript("OnClick", function() self.db.remixMode = mode; self:UpdateRemixVisuals() end)
        return b
    end
    -- [[ RETAIL RENAMED TO WORLD ]]
    self.btnRetail = MkRemBtn("World", "OFF", 0); 
    self.btnAllow = MkRemBtn("All", "ALLOW", 85); 
    -- self.btnRemix = MkRemBtn("Remix", "ONLY", 170) -- COMMENTED OUT
    
    -- [[ TOOLTIPS FOR SEARCH BUTTONS ]]
    self.btnRetail:SetScript("OnEnter", function(s)
        GameTooltip:SetOwner(s, "ANCHOR_RIGHT")
        GameTooltip:SetText("World Mode")
        GameTooltip:AddLine("Addon will search for players that are not inside of a dungeon or raid instance.", 1, 1, 1, true)
        GameTooltip:Show()
    end)
    self.btnRetail:SetScript("OnLeave", function() GameTooltip:Hide() end)

    self.btnAllow:SetScript("OnEnter", function(s)
        GameTooltip:SetOwner(s, "ANCHOR_RIGHT")
        GameTooltip:SetText("|cffff0000WARNING: All Mode|r")
        GameTooltip:AddLine("This will search and whisper players inside dungeons or raids.", 1, 0.5, 0.5, true)
        GameTooltip:AddLine("Some players do not take kindly to this and may report you.", 1, 0.5, 0.5, true)
        GameTooltip:AddLine("All responsibility is with the user of this addon.", 1, 0.2, 0.2, true)
        GameTooltip:Show()
    end)
    self.btnAllow:SetScript("OnLeave", function() GameTooltip:Hide() end)
    
    function GR:UpdateRemixVisuals()
        self.btnRetail.bg:SetColorTexture(0.2,0.2,0.2,1); self.btnAllow.bg:SetColorTexture(0.2,0.2,0.2,1); 
        -- self.btnRemix.bg:SetColorTexture(0.2,0.2,0.2,1) -- COMMENTED OUT
        
        if self.db.remixMode=="OFF" then self.btnRetail.bg:SetColorTexture(0,0.6,0,1) end
        if self.db.remixMode=="ALLOW" then self.btnAllow.bg:SetColorTexture(0,0.6,0,1) end
        -- if self.db.remixMode=="ONLY" then self.btnRemix.bg:SetColorTexture(0,0.6,0,1) end -- COMMENTED OUT
    end
    self:UpdateRemixVisuals()
    
    y = y - 40; AddText(NS.L["MESSAGE_TEMPLATES"] or "MESSAGE TEMPLATES", "GameFontNormal", 0); y = y - 30
    local dTemp = CreateFrame("Button", nil, parent); NS.CreateBg(dTemp, 0.2, 0.2, 0.2, 1); dTemp:SetSize(200, 24); dTemp:SetPoint("TOPLEFT", 15, y)
    local dTempTxt = dTemp:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); dTempTxt:SetPoint("LEFT", 5, 0); dTempTxt:SetText(self.db.messageTemplate)
    
    local dTempArrow = dTemp:CreateTexture(nil, "ARTWORK")
    dTempArrow:SetTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up")
    dTempArrow:SetSize(20, 20)
    dTempArrow:SetPoint("RIGHT", -2, 0)
    
    local dTempList = CreateFrame("Frame", nil, dTemp); NS.CreateBg(dTempList, 0.1, 0.1, 0.1, 1); NS.CreateBorder(dTempList, 0.5, 0.5, 0.5, 1)
    dTempList:SetSize(200, 100); dTempList:SetPoint("TOPLEFT", dTemp, "BOTTOMLEFT", 0, -2); dTempList:SetFrameStrata("FULLSCREEN_DIALOG"); dTempList:Hide()
    
    dTemp:SetScript("OnClick", function() 
        local kids = {dTempList:GetChildren()}; for _, k in ipairs(kids) do k:Hide() end
        local ty = -5
        for k, v in pairs(GR.db.templates) do
            local b = CreateFrame("Button", nil, dTempList); b:SetSize(190, 20); b:SetPoint("TOPLEFT", 5, ty)
            local t = b:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"); t:SetPoint("LEFT"); t:SetText(k)
            b:SetScript("OnClick", function() 
                GR.db.messageTemplate = k; dTempTxt:SetText(k); GR.InputBody:SetText(v); GR.InputName:SetText(k); dTempList:Hide() 
                if GR.InputBody:GetScript("OnTextChanged") then
                     GR.InputBody:GetScript("OnTextChanged")(GR.InputBody)
                end
                if GR.InputName:GetScript("OnTextChanged") then
                     GR.InputName:GetScript("OnTextChanged")(GR.InputName)
                end
            end)
            ty = ty - 20
        end
        dTempList:SetHeight(math.abs(ty) + 5)
        dTempList:SetShown(not dTempList:IsShown()) 
    end)
    
    local btnNew = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnNew:SetSize(50, 22); btnNew:SetPoint("LEFT", dTemp, "RIGHT", 5, 0); btnNew:SetText(NS.L["NEW"] or "New")
    btnNew:SetScript("OnClick", function() GR.InputName:SetText(""); GR.InputBody:SetText(""); GR.InputName:SetFocus() end)
    NS.SkinButton(btnNew)

    y = y - 30
    local nameBox = CreateFrame("Frame", nil, parent)
    nameBox:SetSize(250, 22); nameBox:SetPoint("TOPLEFT", 15, y)
    NS.CreateBg(nameBox, 0.1, 0.1, 0.1, 0.8); NS.CreateBorder(nameBox, 0.3, 0.3, 0.3, 1)
    
    local nameIn = CreateFrame("EditBox", nil, nameBox)
    nameIn:SetPoint("TOPLEFT", 4, -2); nameIn:SetPoint("BOTTOMRIGHT", -4, 2)
    nameIn:SetFontObject(GameFontHighlightSmall); nameIn:SetAutoFocus(false)
    nameIn:SetScript("OnEscapePressed", function(s) s:ClearFocus() end)
    self.InputName = nameIn
    local ph = nameBox:CreateFontString(nil, "ARTWORK", "GameFontDisable"); ph:SetPoint("LEFT", 6, 0); ph:SetText("Template Name...")
    
    nameIn:SetScript("OnTextChanged", function(s) 
        if s:GetText() ~= "" then ph:Hide() else ph:Show() end 
    end)
    
    nameIn:SetScript("OnEditFocusGained", function() ph:Hide() end)
    nameIn:SetScript("OnEditFocusLost", function(s) if s:GetText()=="" then ph:Show() end end)
    nameBox:SetScript("OnMouseDown", function() nameIn:SetFocus() end)
    
    y = y - 30
    -- FIXED: REDUCED CONTAINER WIDTH 275 -> 260
    local bodyF = CreateFrame("Frame", nil, parent); NS.CreateBg(bodyF, 0.1, 0.1, 0.1, 0.5); bodyF:SetSize(260, 100); bodyF:SetPoint("TOPLEFT", 15, y)
    
    -- [[ FIXED LAYOUT FOR MESSAGE TEMPLATE BOX ]]
    local scrollM = CreateFrame("ScrollFrame", nil, bodyF, "UIPanelScrollFrameTemplate")
    scrollM:SetPoint("TOPLEFT", 5, -5)
    scrollM:SetPoint("BOTTOMRIGHT", -20, 5) -- Default gap for scrollbar
    
    -- Style Scrollbar to match Raid Roster design
    NS.StyleScrollBar(scrollM)

    local editM = CreateFrame("EditBox", nil, scrollM)
    -- 2. Width 225 (Fits nicely in 260 container with margins)
    editM:SetSize(225, 100) 
    editM:SetMultiLine(true)
    editM:SetFontObject(ChatFontNormal) 
    editM:SetMaxLetters(255)
    editM:SetAutoFocus(false) 
    
    editM:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    editM:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
    editM:SetText(self.db.templates[self.db.messageTemplate] or "")
    scrollM:SetScrollChild(editM)
    self.InputBody = editM

    -- 3. Click container to focus
    bodyF:SetScript("OnMouseDown", function()
        editM:SetFocus()
    end)

    local charCount = bodyF:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    charCount:SetPoint("BOTTOMRIGHT", -30, 2)
    
    local function UpdateCharCount()
        local text = editM:GetText() or ""
        -- Strip any newlines that snuck in (multiline EditBox allows Enter insertion)
        local clean = text:gsub("[\n\r]", " ")
        if clean ~= text then
            local pos = editM:GetCursorPosition()
            editM:SetText(clean)
            editM:SetCursorPosition(math.min(pos, #clean))
        end
        local len = string.len(clean)
        charCount:SetText(len .. " / 255")
        if len >= 255 then charCount:SetTextColor(1, 0.2, 0.2) else charCount:SetTextColor(0.5, 0.5, 0.5) end
    end
    
    editM:SetScript("OnTextChanged", function(self)
        UpdateCharCount()
    end)
    
    -- 4. Focus resets cursor to start
    editM:SetScript("OnEditFocusGained", function(self) 
        self:SetCursorPosition(0)
        UpdateCharCount()
    end) 
    
    UpdateCharCount() 
    
    y = y - 110
    local btnSave = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate"); btnSave:SetSize(80, 22); btnSave:SetPoint("TOPLEFT", 15, y); btnSave:SetText(NS.L["SAVE"] or "Save")
    btnSave:SetScript("OnClick", function()
        local n, b = self.InputName:GetText(), self.InputBody:GetText()
        if n ~= "" and b ~= "" then
            -- Worst-Case Simulation Validation
            local msg = b:gsub("[\n\r]", " ")
            msg = msg:gsub("%[player%]", "Deathknight-BoreanTundra")
            msg = msg:gsub("%[Player%]", "Deathknight-BoreanTundra")
            msg = msg:gsub("%[ownname%]", "YourPlayerName")
            msg = msg:gsub("%[OwnName%]", "YourPlayerName")
            msg = msg:gsub("%[guild%]", "<Order of the Silent Nexus>")
            msg = msg:gsub("%[Guild%]", "<Order of the Silent Nexus>")
            msg = msg:gsub("%[playerclass%]", "Demon Hunter")
            msg = msg:gsub("%[PlayerClass%]", "Demon Hunter")
            
            local len = string.len(msg)
            if len > 255 then
                if GR.ShowTemplateValidationPopup then
                    GR:ShowTemplateValidationPopup(n, b, msg, len)
                end
                return -- Block saving visually
            end
            
            self.db.templates[n] = b; self.db.messageTemplate = n; dTempTxt:SetText(n); print("Saved Template: " .. n)
        end
    end)
    NS.SkinButton(btnSave)

    -- HELP BUTTON
    local btnHelp = CreateFrame("Button", nil, parent)
    btnHelp:SetSize(80, 22)
    btnHelp:SetPoint("LEFT", btnSave, "RIGHT", 5, 0)
    
    -- Custom Red Skinning
    btnHelp:SetNormalTexture("")
    btnHelp:SetHighlightTexture("")
    btnHelp:SetPushedTexture("")
    btnHelp:SetDisabledTexture("")
    NS.CreateBg(btnHelp, 0.4, 0.0, 0.0, 1)
    NS.CreateBorder(btnHelp, 0, 0, 0, 1)
    
    local hText = btnHelp:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    hText:SetPoint("CENTER"); hText:SetText(NS.L["HELP"] or "HELP"); hText:SetTextColor(1, 1, 1) 
    btnHelp:SetFontString(hText)
    
    btnHelp:SetScript("OnClick", function()
        if GR.HelpFrame then GR.HelpFrame:SetShown(not GR.HelpFrame:IsShown()) else GR:BuildHelpPopup() end
    end)
    
    btnHelp:SetScript("OnEnter", function(s) if s.bg then s.bg:SetColorTexture(0.6, 0.1, 0.1, 1) end end)
    btnHelp:SetScript("OnLeave", function(s) if s.bg then s.bg:SetColorTexture(0.4, 0.0, 0.0, 1) end end)
    btnHelp:SetScript("OnMouseDown", function(s) if s.bg then s.bg:SetColorTexture(0.2, 0.0, 0.0, 1) end end)
    btnHelp:SetScript("OnMouseUp", function(s) if s.bg then s.bg:SetColorTexture(0.6, 0.1, 0.1, 1) end end)

    -- DELETE TEMPLATE BUTTON
    local btnDel = CreateFrame("Button", nil, parent)
    btnDel:SetSize(60, 22)
    btnDel:SetPoint("LEFT", btnHelp, "RIGHT", 5, 0)
    
    btnDel:SetNormalTexture("")
    btnDel:SetHighlightTexture("")
    btnDel:SetPushedTexture("")
    btnDel:SetDisabledTexture("")
    NS.CreateBg(btnDel, 0.4, 0.0, 0.0, 1)
    NS.CreateBorder(btnDel, 0, 0, 0, 1)
    
    local delText = btnDel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    delText:SetPoint("CENTER"); delText:SetText(NS.L["DELETE"] or "Delete"); delText:SetTextColor(1, 1, 1)
    btnDel:SetFontString(delText)
    
    btnDel:SetScript("OnClick", function()
        if GR.DeleteCurrentTemplate then GR:DeleteCurrentTemplate(dTempTxt) end
    end)
    
    btnDel:SetScript("OnEnter", function(s) if s.bg then s.bg:SetColorTexture(0.6, 0.1, 0.1, 1) end end)
    btnDel:SetScript("OnLeave", function(s) if s.bg then s.bg:SetColorTexture(0.4, 0.0, 0.0, 1) end end)
    btnDel:SetScript("OnMouseDown", function(s) if s.bg then s.bg:SetColorTexture(0.2, 0.0, 0.0, 1) end end)
    btnDel:SetScript("OnMouseUp", function(s) if s.bg then s.bg:SetColorTexture(0.6, 0.1, 0.1, 1) end end)

    -- TEST BUTTON (UPDATED WITH EXPLICIT LIST)
    local btnTest = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnTest:SetSize(165, 22)
    btnTest:SetPoint("TOPLEFT", btnSave, "BOTTOMLEFT", 0, -5)
    btnTest:SetText("TEST ME")
    btnTest:SetScript("OnClick", function()
        if not GR.InputBody then return end
        local msg = GR.InputBody:GetText()
        if not msg or msg == "" then print("Template is empty."); return end
        
        local myName = UnitName("player")
        local myClass = UnitClass("player")
        local myGuild = GetGuildInfo("player") or "Guild"
        
        -- [[ PLACEHOLDER REPLACEMENT LOGIC (DUMB-PROOFED) ]]
        -- 1. Target Name (Variations) -> Becomes My Name for Test
        msg = msg:gsub("%[player%]", myName)
        msg = msg:gsub("%[Player%]", myName)
        msg = msg:gsub("%[PLAYER%]", myName)
        msg = msg:gsub("%[ player %]", myName)
        msg = msg:gsub("%[ Player %]", myName)
        msg = msg:gsub("%[ PLAYER %]", myName)
        
        -- 2. Own Name (Variations)
        msg = msg:gsub("%[ownname%]", myName)
        msg = msg:gsub("%[OwnName%]", myName)
        msg = msg:gsub("%[Ownname%]", myName)
        msg = msg:gsub("%[OWNNAME%]", myName)
        msg = msg:gsub("%[own name%]", myName)
        msg = msg:gsub("%[Own Name%]", myName)
        msg = msg:gsub("%[OWN NAME%]", myName)
        
        -- 3. Guild (Variations)
        msg = msg:gsub("%[guild%]", myGuild)
        msg = msg:gsub("%[Guild%]", myGuild)
        msg = msg:gsub("%[GUILD%]", myGuild)
        
        -- 4. Class (Variations)
        msg = msg:gsub("%[playerclass%]", myClass)
        msg = msg:gsub("%[PlayerClass%]", myClass)
        msg = msg:gsub("%[Playerclass%]", myClass)
        msg = msg:gsub("%[PLAYERCLASS%]", myClass)
        msg = msg:gsub("%[player class%]", myClass)
        msg = msg:gsub("%[Player Class%]", myClass)
        msg = msg:gsub("%[Player class%]", myClass)
        msg = msg:gsub("%[class%]", myClass)
        msg = msg:gsub("%[Class%]", myClass)
        msg = msg:gsub("%[CLASS%]", myClass)
        
        msg = msg:gsub("[\n\r]", " ") -- Strip newlines before sending
        SendChatMessage(msg, "WHISPER", nil, myName)
        print("GRS: Test message sent to self.")
    end)
    NS.SkinButton(btnTest)
    GR.TestBtn = btnTest -- Expose for Compact Mode Anchor
    
    -- =========================================================================
    -- NEW: POPUP BUTTON FOR WELCOME MESSAGES
    -- =========================================================================
    
    local btnWelcomePopup = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnWelcomePopup:SetSize(165, 30)
    btnWelcomePopup:SetPoint("TOPLEFT", btnTest, "BOTTOMLEFT", 0, -20)
    btnWelcomePopup:SetText(NS.L["WELCOME_MESSAGES"] or "Welcome Messages")
    
    btnWelcomePopup:SetScript("OnClick", function()
        GR:OpenWelcomePopup()
    end)
    NS.SkinButton(btnWelcomePopup)
    GR.WelcomeBtn = btnWelcomePopup -- Expose for Compact Mode Reparenting

    -- =========================================================================
    -- NEW: DEV MODE SYNC TEST BUTTON
    -- =========================================================================
    local btnDevSync = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnDevSync:SetSize(165, 22)
    btnDevSync:SetPoint("TOPLEFT", btnWelcomePopup, "BOTTOMLEFT", 0, -20)
    btnDevSync:SetText("DEV: Test Sync Receive")
    NS.SkinButton(btnDevSync)
    btnDevSync:Hide() -- Hidden by default
    
    btnDevSync:SetScript("OnClick", function()
        if GR.SimulateIncomingSync then GR:SimulateIncomingSync() end
    end)
    
    -- Update visibility based on Dev Mode toggle
    btnDevSync:SetScript("OnShow", function(self)
        if GR.devOfficerMode then self:Show() else self:Hide() end
    end)
    
    -- Hook into the Dev Toggle in UI_Main to refresh this button
    -- (This logic relies on the OnShow script above when the frame refreshes)
    parent:HookScript("OnUpdate", function()
        if GR.devOfficerMode and not btnDevSync:IsShown() then
            btnDevSync:Show()
        elseif not GR.devOfficerMode and btnDevSync:IsShown() then
            btnDevSync:Hide()
        end
    end)
end