-- =========================================================================
-- FILE: UI_Home_Results.lua (TBC)
-- PURPOSE: Center pane - search results, whisper grid, scroll handling
-- =========================================================================
local addonName, NS = ...
local GR = NS.GR
local CreateBg = NS.CreateBg
local CreateBorder = NS.CreateBorder
local SkinButton = NS.SkinButton

-- =========================================================================
-- UI: CENTER PANE (RESULTS)
-- =========================================================================

function GR:BuildCenterPane(parent)
    self.ResultsHeader = parent:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    self.ResultsHeader:SetPoint("TOPLEFT", 15, -15); self.ResultsHeader:SetText("Unguilded Players - Current Filter: Default (All)")
    
    local sBtn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    sBtn:SetSize(140, 30); sBtn:SetPoint("TOPRIGHT", -15, -10); sBtn:SetText("Search Players")
    self.SearchBtn = sBtn 
    sBtn:SetScript("OnClick", function() self:StartSearch() end)
    SkinButton(sBtn) 
    
    local rListBtn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    rListBtn:SetSize(100, 30)
    rListBtn:SetPoint("RIGHT", sBtn, "LEFT", -5, 0)
    rListBtn:SetText("Reset List")
    rListBtn:SetScript("OnClick", function()
        GR.foundPlayers = {}
        GR:UpdateScrollFrame()
        if GR.StatusText then GR.StatusText:SetText("List Discarded.") end
    end)
    SkinButton(rListBtn)

    -- Status text positioned below buttons (1:1 Retail Parity)
    self.StatusText = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    self.StatusText:SetPoint("TOPRIGHT", sBtn, "BOTTOMRIGHT", 0, -5)
    self.StatusText:SetText("Ready.")

    local headF = CreateFrame("Frame", nil, parent); headF:SetSize(750, 20); headF:SetPoint("TOPLEFT", 21, -45) 

    local selectAll = CreateFrame("CheckButton", nil, headF, "UICheckButtonTemplate")
    selectAll:SetSize(20, 20)
    selectAll:SetPoint("LEFT", 0, 0)
    selectAll:SetScript("OnClick", function(self)
        local state = self:GetChecked()
        for _, p in ipairs(GR.foundPlayers) do
            p.selected = state
        end
        GR:UpdateScrollFrame()
    end)
    
    local headers = {
        {text="Name", key="name", width=110}, 
        {text="Race", key="race", width=90},  
        {text="Class", key="fullClass", width=100},
        {text="Level", key="level", width=40}, 
        {text="Zone", key="zone", width=110}
    }
    
    local lastH = nil
    for i, data in ipairs(headers) do
        local btn = CreateFrame("Button", nil, headF)
        btn:SetSize(data.width, 20)
        
        if i==1 then 
            btn:SetPoint("LEFT", 30, 0) 
        else 
            btn:SetPoint("LEFT", lastH, "RIGHT", 0, 0) 
        end
        
        local txt = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        txt:SetPoint("LEFT", 5, 0)
        txt:SetText(data.text)
        btn:SetFontString(txt)
        
        -- Add border to header button (1:1 Retail Parity)
        local border = btn:CreateTexture(nil, "BORDER")
        border:SetAllPoints()
        border:SetColorTexture(0.3, 0.3, 0.3, 0.8)
        local highlight = btn:CreateTexture(nil, "ARTWORK")
        highlight:SetPoint("TOPLEFT", 1, -1)
        highlight:SetPoint("BOTTOMRIGHT", -1, 1)
        highlight:SetColorTexture(0.15, 0.15, 0.15, 0.9)
        
        btn:SetScript("OnEnter", function(s) 
            s:GetFontString():SetTextColor(1, 1, 1) 
        end)
        btn:SetScript("OnLeave", function(s) 
            s:GetFontString():SetTextColor(1, 0.82, 0) 
        end)
        
        btn:SetScript("OnClick", function() 
            GR:SortPlayers(data.key) 
        end)
        
        lastH = btn
    end
    
    local bot = CreateFrame("Frame", nil, parent); bot:SetHeight(40)
    bot:SetPoint("BOTTOMLEFT", 15, 5)
    bot:SetPoint("BOTTOMRIGHT", -15, 5)
    
    local slL = bot:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); slL:SetPoint("LEFT", 0, 0); slL:SetText("Msg Delay: 0.8s")
    self.MsgDelayLabel = slL
    local sl = CreateFrame("Slider", nil, bot, "OptionsSliderTemplate")
    sl:SetPoint("LEFT", slL, "RIGHT", 10, 0); sl:SetWidth(150); sl:SetMinMaxValues(0.5, 1.0); sl:SetValue(0.8); sl:SetValueStep(0.1); sl:SetObeyStepOnDrag(true)
    sl:SetScript("OnValueChanged", function(s,v) v=math.floor(v*10)/10; self.db.scanDelay=v; slL:SetText("Msg Delay: "..v.."s") end)
    self.MsgDelaySlider = sl
    
    local snd = CreateFrame("Button", nil, bot, "UIPanelButtonTemplate"); snd:SetSize(120, 30); snd:SetPoint("RIGHT", -10, 0); snd:SetText("Send Batch")
    snd:SetScript("OnClick", function()
        local mode = self.db.sendMode or "MESSAGE"
        if mode == "MESSAGE" then
            self:SendMessageSequence()
        else
            -- INVITE or BOTH mode: invite first player in list
            if #self.foundPlayers == 0 then
                print("|cffff0000[A.X.I.O.M.]|r No players in list."); return
            end
            local p = self.foundPlayers[1]
            local justName = Ambiguate(p.name, "none")
            GuildInvite(justName)
            self:AddToBlacklist(p.name)
            self:AddToRecent(p.name, mode == "BOTH" and "Invited + Msg" or "Invited")
            
            -- If BOTH mode, also send message
            if mode == "BOTH" and self.InputBody then
                local msg = self.InputBody:GetText()
                if msg and msg ~= "" then
                    local myName = UnitName("player")
                    local gName = GetGuildInfo("player") or "Guild"
                    local pClass = p.fullClass or "Hero"
                    
                    msg = msg:gsub("%b[]", function(captured)
                        local inner = captured:sub(2, -2)
                        local clean = inner:lower():gsub("%s+", "")
                        if clean == "player" then return justName end
                        if clean == "ownname" then return myName end
                        if clean == "guild" then return gName end
                        if clean == "playerclass" or clean == "class" then return pClass end
                        return captured
                    end)
                    
                    self.recruitedPlayers[justName] = true
                    SendChatMessage(msg, "WHISPER", nil, p.name)
                end
            end
            
            -- Remove from list
            table.remove(self.foundPlayers, 1)
            self:UpdateScrollFrame()
        end
    end)
    SkinButton(snd)
    self.SendBatchBtn = snd 

    local btnBL = CreateFrame("Button", nil, bot, "UIPanelButtonTemplate")
    btnBL:SetSize(130, 30)
    btnBL:SetPoint("RIGHT", snd, "LEFT", -10, 0)
    btnBL:SetText("Blacklist Selected")
    btnBL:SetScript("OnClick", function() self:ManualBlacklist() end)
    SkinButton(btnBL)

    -- =========================================================================
    -- SEND MODE DROPDOWN (1:1 Copy of Retail)
    -- =========================================================================
    local SEND_MODES = {
        { key = "MESSAGE", label = "Message Only" },
        { key = "INVITE",  label = "Guild Invite Only" },
        { key = "BOTH",    label = "Message + G-Invite" },
    }
    
    -- Initialize default if not set
    if not self.db.sendMode then self.db.sendMode = "MESSAGE" end
    
    -- Get label for current mode
    local function GetModeLabel(modeKey)
        for _, m in ipairs(SEND_MODES) do
            if m.key == modeKey then return m.label end
        end
        return SEND_MODES[1].label
    end
    
    local dMode = CreateFrame("Button", nil, bot)
    CreateBg(dMode, 0.2, 0.2, 0.2, 1)
    dMode:SetSize(150, 30)
    dMode:SetPoint("RIGHT", btnBL, "LEFT", -10, 0)
    
    local dModeTxt = dMode:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    dModeTxt:SetPoint("LEFT", 8, 0)
    dModeTxt:SetText(GetModeLabel(self.db.sendMode))
    
    local dModeArrow = dMode:CreateTexture(nil, "ARTWORK")
    dModeArrow:SetTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up")
    dModeArrow:SetSize(20, 20)
    dModeArrow:SetPoint("RIGHT", -2, 0)
    
    -- Dropdown list (opens UPWARD like Retail)
    local dModeList = CreateFrame("Frame", nil, dMode)
    CreateBg(dModeList, 0.1, 0.1, 0.1, 1)
    CreateBorder(dModeList, 0.5, 0.5, 0.5, 1)
    dModeList:SetSize(150, 70)
    dModeList:SetPoint("BOTTOMLEFT", dMode, "TOPLEFT", 0, 2)
    dModeList:SetFrameStrata("TOOLTIP") -- Above FULLSCREEN_DIALOG to clear compact buttons
    dModeList:SetFrameLevel(200)
    dModeList:Hide()
    
    local ly = -5
    for _, mode in ipairs(SEND_MODES) do
        local b = CreateFrame("Button", nil, dModeList)
        b:SetSize(140, 20)
        b:SetPoint("TOPLEFT", 5, ly)
        
        -- Hover glow background
        local glow = b:CreateTexture(nil, "BACKGROUND")
        glow:SetAllPoints()
        glow:SetColorTexture(0.2, 0.8, 0.3, 0.15)
        glow:Hide()
        
        local t = b:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        t:SetPoint("LEFT")
        t:SetText(mode.label)
        b:SetScript("OnClick", function()
            self.db.sendMode = mode.key
            dModeTxt:SetText(mode.label)
            dModeList:Hide()
            self:UpdateSendModeUI()
        end)
        b:SetScript("OnEnter", function(s) t:SetTextColor(1, 1, 1); glow:Show() end)
        b:SetScript("OnLeave", function(s) t:SetTextColor(1, 0.82, 0); glow:Hide() end)
        ly = ly - 20
    end
    dModeList:SetHeight(math.abs(ly) + 5)
    
    dMode:SetScript("OnClick", function()
        dModeList:SetShown(not dModeList:IsShown())
    end)
    
    -- Expose for Compact Mode repositioning
    self.SendModeBtn = dMode
    self.SendModeList = dModeList
    self.SendModeOriginalParent = bot

    -- =========================================================================
    -- LAYERING FIX: Separate Backdrop from Scroll Logic (1:1 Retail Parity)
    -- =========================================================================
    
    -- 1. Create the Backdrop "Slab" first
    local listBackdrop = CreateFrame("Frame", nil, parent)
    listBackdrop:SetPoint("TOPLEFT", headF, "BOTTOMLEFT", 0, -5)
    listBackdrop:SetPoint("BOTTOMRIGHT", bot, "TOPRIGHT", -5, 5)
    listBackdrop:SetFrameLevel(parent:GetFrameLevel() + 1)
    listBackdrop:SetClipsChildren(true)
    
    CreateBg(listBackdrop, 0, 0, 0, 0.8) 
    self.listBackdrop = listBackdrop

    -- 2. Create the Logical ScrollFrame
    self.scroll = CreateFrame("ScrollFrame", "GRS_S", parent, "FauxScrollFrameTemplate")
    parent.scrollFrame = self.scroll
    parent.scroll = self.scroll 
    local scrollFrame = self.scroll 
    
    -- Anchor scrollframe to the backdrop
    scrollFrame:SetAllPoints(listBackdrop)
    scrollFrame:SetClipsChildren(true)
    
    -- Ensure scroll logic sits ABOVE the backdrop
    scrollFrame:SetFrameLevel(listBackdrop:GetFrameLevel() + 5)
    
    -- ScrollBar Styling (matches Raid Roster design)
    local scrollBar = _G["GRS_SScrollBar"]
    if scrollBar then
        NS.StyleScrollBar(scrollBar)
        scrollBar:ClearAllPoints()
        scrollBar:SetPoint("TOPRIGHT", listBackdrop, "TOPRIGHT", 0, -2)
        scrollBar:SetPoint("BOTTOMRIGHT", listBackdrop, "BOTTOMRIGHT", 0, 2)
        scrollBar:Show()
    end

    scrollFrame:SetScript("OnVerticalScroll", function(s, offset)
        FauxScrollFrame_OnVerticalScroll(s, offset, 20, function() self:UpdateScrollFrame() end)
    end)
    
    scrollFrame:EnableMouseWheel(true)
    
    parent:SetScript("OnSizeChanged", function(frame, w, h)
        local sFrame = scrollFrame or frame.scrollFrame or frame.scroll
        if not sFrame then return end

        -- FIXED WIDTH: Always use 760px to match header widths
        local FIXED_WIDTH = 760
        
        bot:ClearAllPoints()
        bot:SetPoint("BOTTOMLEFT", 15, 5)
        bot:SetWidth(FIXED_WIDTH)
        
        -- Update Backdrop with fixed width
        if self.listBackdrop then
            self.listBackdrop:ClearAllPoints()
            self.listBackdrop:SetPoint("TOPLEFT", headF, "BOTTOMLEFT", 0, -5)
            self.listBackdrop:SetPoint("BOTTOMLEFT", bot, "TOPLEFT", 0, 5)
            self.listBackdrop:SetWidth(FIXED_WIDTH)
        end
        
        -- Snap Scroll to Backdrop
        sFrame:ClearAllPoints()
        sFrame:SetAllPoints(self.listBackdrop)
    end)
    
    self.rows = {}
    
    function GR:UpdateResultsLayout(height)
        if not height then height = self.scroll:GetHeight() end
        local numRows = math.floor(height / 20) - 2 -- Subtract 2 to prevent bottom row bleed (Retail parity)
        if numRows < 1 then numRows = 1 end
        self.visibleRows = numRows 
        
        for i = 1, numRows do
            if not self.rows[i] then
                local r = CreateFrame("Button", nil, parent)
                -- Force rows to sit above the backdrop (ScrollFrame level + 1)
                r:SetFrameLevel(self.scroll:GetFrameLevel() + 1)
                
                r:SetHeight(20); 
                r:SetPoint("LEFT", 41, 0); 
                r:SetPoint("RIGHT", -25, 0)
                if i==1 then r:SetPoint("TOP", self.scroll, "TOP") else r:SetPoint("TOP", self.rows[i-1], "BOTTOM") end
                
                r.c = CreateFrame("CheckButton", nil, r, "UICheckButtonTemplate"); r.c:SetSize(20,20); r.c:SetPoint("LEFT", -20, 0)
                r.c:SetScript("OnClick", function(b) if r.d then r.d.selected=b:GetChecked() end end)
                
                r.n = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.n:SetPoint("LEFT",10,0); r.n:SetWidth(110); r.n:SetJustifyH("LEFT")
                r.ra = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.ra:SetPoint("LEFT",r.n,"RIGHT"); r.ra:SetWidth(90); r.ra:SetJustifyH("LEFT")
                r.cl = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.cl:SetPoint("LEFT",r.ra,"RIGHT"); r.cl:SetWidth(100); r.cl:SetJustifyH("LEFT")
                r.lvl = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.lvl:SetPoint("LEFT",r.cl,"RIGHT"); r.lvl:SetWidth(40); r.lvl:SetJustifyH("LEFT")
                r.z = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.z:SetPoint("LEFT",r.lvl,"RIGHT"); r.z:SetWidth(110); r.z:SetJustifyH("LEFT")
                
                -- ROW CLICK HANDLER: In invite modes, clicking row triggers GuildInvite
                r:SetScript("OnClick", function(self)
                    if not self.d then return end
                    local mode = GR.db.sendMode or "MESSAGE"
                    if mode == "INVITE" or mode == "BOTH" then
                        local justName = Ambiguate(self.d.name, "none")
                        GuildInvite(justName)
                        GR:AddToBlacklist(self.d.name)
                        GR:AddToRecent(self.d.name, mode == "BOTH" and "Invited + Msg" or "Invited")
                        
                        -- If BOTH mode, also send message
                        if mode == "BOTH" and GR.InputBody then
                            local msg = GR.InputBody:GetText()
                            if msg and msg ~= "" then
                                local safeName = justName
                                local myName = UnitName("player")
                                local gName = GetGuildInfo("player") or "Guild"
                                local pClass = self.d.fullClass or "Hero"
                                
                                msg = msg:gsub("%b[]", function(captured)
                                    local inner = captured:sub(2, -2)
                                    local clean = inner:lower():gsub("%s+", "")
                                    if clean == "player" then return safeName end
                                    if clean == "ownname" then return myName end
                                    if clean == "guild" then return gName end
                                    if clean == "playerclass" or clean == "class" then return pClass end
                                    return captured
                                end)
                                
                                -- Add to recruitedPlayers BEFORE sending so whisper pane tracks it
                                GR.recruitedPlayers[justName] = true
                                SendChatMessage(msg, "WHISPER", nil, self.d.name)
                            end
                        end
                        
                        -- Remove from list after invite
                        for k, v in ipairs(GR.foundPlayers) do
                            if v.name == self.d.name then
                                table.remove(GR.foundPlayers, k)
                                break
                            end
                        end
                        GR:UpdateScrollFrame()
                    end
                end)
                
                -- Highlight on hover for invite mode
                r:SetScript("OnEnter", function(self)
                    local mode = GR.db.sendMode or "MESSAGE"
                    if mode == "INVITE" or mode == "BOTH" then
                        self.n:SetTextColor(0, 1, 0.5)
                    end
                end)
                r:SetScript("OnLeave", function(self)
                    self.n:SetTextColor(1, 1, 1)
                end)
                
                self.rows[i] = r
            end
        end
        self:UpdateScrollFrame() 
    end
end

-- =========================================================================
-- UI: WHISPER PANE
-- =========================================================================

function GR:BuildWhisperPane(parent)
    local expandedF = CreateFrame("Frame", nil, parent)
    expandedF:SetAllPoints()
    self.WhisperExpandedFrame = expandedF
    
    local gridH = expandedF:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    gridH:SetPoint("TOPLEFT", 15, -15)
    gridH:SetText("ACTIVE WHISPERS")
    
    local pText = expandedF:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    pText:SetPoint("LEFT", gridH, "RIGHT", 30, 0) 
    pText:SetText("Page 1 / 1")
    self.pageText = pText
    
    local prevBtn = CreateFrame("Button", nil, expandedF)
    prevBtn:SetSize(20, 20)
    prevBtn:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Up")
    prevBtn:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
    prevBtn:SetPoint("RIGHT", pText, "LEFT", -5, 0)
    prevBtn:SetScript("OnClick", function() 
        if GR.currentPage > 1 then 
            GR.currentPage = GR.currentPage - 1
            GR:RenderWhisperGrid() 
        end 
    end)
    self.prevBtn = prevBtn
    
    local nextBtn = CreateFrame("Button", nil, expandedF)
    nextBtn:SetSize(20, 20)
    nextBtn:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Up")
    nextBtn:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
    nextBtn:SetPoint("LEFT", pText, "RIGHT", 5, 0)
    nextBtn:SetScript("OnClick", function() 
        if GR.nextBtn and GR.nextBtn:IsEnabled() then 
            GR.currentPage = GR.currentPage + 1
            GR:RenderWhisperGrid() 
        end 
    end)
    self.nextBtn = nextBtn

    local pruneBtn = CreateFrame("Button", nil, expandedF, "UIPanelButtonTemplate")
    pruneBtn:SetSize(100, 20)
    pruneBtn:SetPoint("LEFT", nextBtn, "RIGHT", 20, 0)
    pruneBtn:SetText("Prune Inactive")
    pruneBtn:SetScript("OnClick", function() self:PruneInactiveChats() end)
    SkinButton(pruneBtn)
    
    local rOnly = CreateFrame("CheckButton", nil, expandedF, "UICheckButtonTemplate")
    rOnly:SetSize(24, 24)
    rOnly:SetPoint("LEFT", pruneBtn, "RIGHT", 10, 0)
    rOnly.text = rOnly:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    rOnly.text:SetPoint("LEFT", rOnly, "RIGHT", 0, 0)
    rOnly.text:SetText("Replies only")
    rOnly:SetScript("OnClick", function(self)
        if GR.db then GR.db.showRepliesOnly = self:GetChecked() end
        GR.currentPage = 1
        GR:RenderWhisperGrid()
    end)
    if GR.db and GR.db.showRepliesOnly then rOnly:SetChecked(true) end
    self.chkReplies = rOnly
    
    self.chatFrames = {}
    local gridBase = CreateFrame("Frame", nil, expandedF)
    gridBase:SetPoint("TOPLEFT", gridH, "BOTTOMLEFT", 0, -10)
    gridBase:SetPoint("BOTTOMRIGHT", -15, 10)
    self.WhisperGridBase = gridBase
    
    function GR:UpdateWhisperLayout(totalHeight)
        self.chatPageSize = 6
        local chatH = 115
        local gap = 10
        
        for i = 1, self.chatPageSize do
            if not self.chatFrames[i] then
                local cf = CreateFrame("Frame", nil, self.WhisperGridBase)
                CreateBg(cf, 0, 0, 0, 0.6); CreateBorder(cf, 0.4, 0.4, 0.4, 1)
                cf:SetHeight(chatH); cf:SetPoint("LEFT"); cf:SetPoint("RIGHT")
                
                cf:Hide() 
                
                cf.headerBtn = CreateFrame("Button", nil, cf); cf.headerBtn:SetPoint("TOPLEFT", 5, -5); cf.headerBtn:SetSize(200, 15)
                cf.headerBtn.text = cf.headerBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall"); cf.headerBtn.text:SetPoint("LEFT"); cf.headerBtn.text:SetText("Inactive")
                cf.headerBtn:RegisterForClicks("AnyUp") 
                cf.headerBtn:SetScript("OnClick", function(s, button)
                    if not s.playerName then return end
                    if button == "LeftButton" then ChatFrame_SendTell(s.playerName)
                    elseif button == "RightButton" then
                        -- TBC: Use UIDropDownMenu instead of MenuUtil
                        local menu = CreateFrame("Frame", "GRS_ChatContextMenu", UIParent, "UIDropDownMenuTemplate")
                        UIDropDownMenu_Initialize(menu, function(self, level)
                            local info = UIDropDownMenu_CreateInfo()
                            info.text = s.playerName
                            info.isTitle = true
                            info.notCheckable = true
                            UIDropDownMenu_AddButton(info, level)
                            
                            info = UIDropDownMenu_CreateInfo()
                            info.text = "Invite to guild"
                            info.notCheckable = true
                            info.func = function() GuildInvite(s.playerName) end
                            UIDropDownMenu_AddButton(info, level)
                        end, "MENU")
                        ToggleDropDownMenu(1, nil, menu, "cursor", 0, 0)
                    end
                end)
                
                local closeChat = CreateFrame("Button", nil, cf, "UIPanelCloseButton"); closeChat:SetSize(20, 20); closeChat:SetPoint("TOPRIGHT", -2, -2)
                closeChat:SetScript("OnClick", function()
                    if cf.headerBtn.playerName then
                        for k, v in ipairs(GR.activeChats) do
                            if v.name == cf.headerBtn.playerName then
                                table.remove(GR.activeChats, k)
                                break
                            end
                        end
                        GR:RenderWhisperGrid() 
                    end
                end)
                
                cf.scroll = CreateFrame("ScrollFrame", nil, cf, "UIPanelScrollFrameTemplate"); cf.scroll:SetPoint("TOPLEFT", 5, -20); cf.scroll:SetPoint("BOTTOMRIGHT", -25, 5)
                if NS.StyleScrollBar then NS.StyleScrollBar(cf.scroll) end
                cf.text = CreateFrame("EditBox", nil, cf.scroll); cf.text:SetMultiLine(true); cf.text:SetFontObject(ChatFontSmall); cf.text:SetWidth(430); cf.text:SetAutoFocus(false); cf.text:EnableMouse(false)
                cf.scroll:SetScrollChild(cf.text)
                self.chatFrames[i] = cf
            end
            
            if i==1 then 
                self.chatFrames[i]:SetPoint("TOP") 
            else 
                self.chatFrames[i]:SetPoint("TOP", self.chatFrames[i-1], "BOTTOM", 0, -gap) 
            end
        end
        
        for i = self.chatPageSize + 1, #self.chatFrames do
            if self.chatFrames[i] then self.chatFrames[i]:Hide() end
        end
        
        self:RenderWhisperGrid()
    end
end

-- =========================================================================
-- UI: SCROLL FRAME UPDATE
-- =========================================================================

function GR:UpdateScrollFrame()
    local d = self.foundPlayers
    local rowCount = self.visibleRows or 15
    if rowCount > #self.rows then rowCount = #self.rows end
    
    FauxScrollFrame_Update(self.scroll, #d, rowCount, 20)
    
    local scrollBar = _G["GRS_SScrollBar"]
    if scrollBar then
        scrollBar:Show() 
        
        local _, maxVal = scrollBar:GetMinMaxValues()
        local thumb = scrollBar:GetThumbTexture()
        
        if not maxVal or maxVal == 0 then
             if thumb then thumb:Hide() end
             
             local up = _G["GRS_SScrollBarScrollUpButton"]
             local down = _G["GRS_SScrollBarScrollDownButton"]
             if up then up:Disable() end
             if down then down:Disable() end
        else
             if thumb then thumb:Show() end
             
             local up = _G["GRS_SScrollBarScrollUpButton"]
             local down = _G["GRS_SScrollBarScrollDownButton"]
             if up then up:Enable() end
             if down then down:Enable() end
        end
    end
    
    local offset = FauxScrollFrame_GetOffset(self.scroll)
    
    for i=1, #self.rows do
        local r = self.rows[i]
        if i > rowCount then
            r:Hide()
        else
            local idx = offset + i
            if idx <= #d then
                local p = d[idx]
                r.d = p; r:Show()
                r.n:SetText(p.name); 
                r.lvl:SetText(p.level);
                r.z:SetText(p.zone); r.ra:SetText(p.race or "Unknown")
                local cc = C_ClassColor.GetClassColor(p.classFile or "PRIEST")
                r.cl:SetText(cc and cc:WrapTextInColorCode(p.fullClass) or p.fullClass)
                r.c:SetChecked(p.selected)
                
                -- Hide/show checkbox based on mode
                local mode = self.db.sendMode or "MESSAGE"
                if mode == "INVITE" or mode == "BOTH" then
                    r.c:Hide()
                else
                    r.c:Show()
                end
            else 
                r:Hide() 
            end
        end
    end
end

-- =========================================================================
-- UI: UPDATE SEND MODE (Button text, slider visibility, checkbox refresh)
-- =========================================================================

function GR:UpdateSendModeUI()
    local mode = self.db.sendMode or "MESSAGE"
    
    -- Update button text
    if self.SendBatchBtn then
        if mode == "MESSAGE" then
            self.SendBatchBtn:SetText("Send Batch")
        else
            self.SendBatchBtn:SetText("Invite to Guild")
        end
        self.SendBatchBtn:Enable()
    end
    
    -- Update compact mode button text (1:1 parity)
    if self.CompactBatchBtn then
        if mode == "MESSAGE" then
            self.CompactBatchBtn:SetText("Send Batch")
        else
            self.CompactBatchBtn:SetText("Invite to Guild")
        end
    end
    
    -- Show/hide message delay slider based on mode
    if self.MsgDelaySlider then
        if mode == "MESSAGE" then
            self.MsgDelaySlider:Show()
            if self.MsgDelayLabel then self.MsgDelayLabel:Show() end
        else
            self.MsgDelaySlider:Hide()
            if self.MsgDelayLabel then self.MsgDelayLabel:Hide() end
        end
    end
    
    -- Refresh scroll frame to update checkbox visibility
    self:UpdateScrollFrame()
end
