local addonName, NS = ...
local GR = NS.GR -- Reference the Core Frame

-- =========================================================================
-- UI: HELPERS
-- =========================================================================

local function CreateBg(f, r, g, b, a)
    local t = f:CreateTexture(nil, "BACKGROUND")
    t:SetAllPoints()
    t:SetColorTexture(r, g, b, a)
    f.bg = t
end

local function CreateBorder(f, r, g, b, a)
    if f.border then f.border:Hide() end
    
    local border = f:CreateTexture(nil, "BORDER")
    border:SetPoint("TOPLEFT", -1, 1)
    border:SetPoint("BOTTOMRIGHT", 1, -1)
    border:SetColorTexture(r, g, b, a)
    f.border = border
end

local function SkinButton(btn)
    if not btn then return end
    
    btn:SetNormalTexture("")
    btn:SetHighlightTexture("")
    btn:SetPushedTexture("")
    btn:SetDisabledTexture("")
    
    if btn.Left then btn.Left:SetAlpha(0) end
    if btn.Middle then btn.Middle:SetAlpha(0) end
    if btn.Right then btn.Right:SetAlpha(0) end
    if btn.Top then btn.Top:SetAlpha(0) end
    if btn.Bottom then btn.Bottom:SetAlpha(0) end
    
    CreateBg(btn, 0.2, 0.2, 0.2, 1)
    CreateBorder(btn, 0, 0, 0, 1)
    
    btn:HookScript("OnEnter", function(s) 
        if s.bg then s.bg:SetColorTexture(0.3, 0.3, 0.3, 1) end 
    end)
    btn:HookScript("OnLeave", function(s) 
        if s.bg then s.bg:SetColorTexture(0.2, 0.2, 0.2, 1) end 
    end)
    btn:HookScript("OnMouseDown", function(s) 
        if s.bg then s.bg:SetColorTexture(0.1, 0.1, 0.1, 1) end 
    end)
    btn:HookScript("OnMouseUp", function(s) 
        if s.bg then s.bg:SetColorTexture(0.3, 0.3, 0.3, 1) end 
    end)
    
    local fs = btn:GetFontString()
    if fs then fs:SetTextColor(1, 1, 1) end
end

-- Export helpers to NS
NS.CreateBg = CreateBg
NS.CreateBorder = CreateBorder
NS.SkinButton = SkinButton

-- =========================================================================
-- UI: STRUCTURE & TABS
-- =========================================================================

function GR:UpdateResizeBounds()
    local currentScale = self:GetScale() or 1
    local maxWidth = 1980 / currentScale
    local maxHeight = 750 / currentScale -- Increased max height for safety
    self:SetResizeBounds(1280, 750, maxWidth, maxHeight) -- Updated min/max bounds
end

function GR:BuildStructuralUI()
    self:SetFrameStrata("DIALOG") 
    self:SetToplevel(true)
    self:EnableMouse(true)
    self:SetMovable(true)
    self:SetResizable(true)
    self:SetClampedToScreen(true)
    self:SetSize(1280, 750) -- Increased height to accommodate new header
    self:SetPoint("CENTER")

    CreateBg(self, 0, 0, 0, 0.85)
    CreateBorder(self, 0.3, 0.3, 0.3, 1)

    self:RegisterForDrag("LeftButton")
    self:SetScript("OnDragStart", self.StartMoving)
    self:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
    end)
    
    self:UpdateResizeBounds()

    local resize = CreateFrame("Button", nil, self)
    resize:SetSize(16, 16); resize:SetPoint("BOTTOMRIGHT")
    resize:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    resize:SetHighlightTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
    resize:SetScript("OnMouseDown", function() self:StartSizing("BOTTOMRIGHT") end)
    resize:SetScript("OnMouseUp", function() 
        self:StopMovingOrSizing() 
        if self.db then
            self.db.windowWidth = self:GetWidth()
            self.db.windowHeight = self:GetHeight()
        end
    end)
    
    table.insert(UISpecialFrames, self:GetName()) 
    
    -- =========================================================================
    -- AXIOM GLOBAL HEADER
    -- =========================================================================
    local axiomHeader = CreateFrame("Frame", "GRS_AxiomHeader", self)
    axiomHeader:SetHeight(30)
    axiomHeader:SetPoint("TOPLEFT", 0, 0)
    axiomHeader:SetPoint("TOPRIGHT", 0, 0)
    CreateBg(axiomHeader, 0.1, 0.1, 0.1, 1) -- Consistent dark background
    CreateBorder(axiomHeader, 0, 0, 0, 1)
    
    local axTitle = axiomHeader:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    axTitle:SetPoint("CENTER", 0, 0)
    axTitle:SetText("A.X.I.O.M. | Automated Xenial Index & Operations Matrix")
    axTitle:SetTextColor(0.4, 0.8, 1) -- Slight blue tint for branding
    
    -- Close Button (Moved to Global Header)
    local close = CreateFrame("Button", nil, axiomHeader)
    close:SetPoint("TOPRIGHT", -5, -3) 
    close:SetSize(24, 24)
    close:SetNormalFontObject("GameFontNormal")
    close:SetText("X")
    close:SetScript("OnClick", function() GR:Hide() end)
    SkinButton(close)

    -- =========================================================================
    -- SIDEBAR & CONTENT LAYOUT
    -- =========================================================================
    
    local sideBar = CreateFrame("Frame", "GRS_SideBar", self)
    sideBar:SetWidth(40)
    sideBar:SetPoint("TOPLEFT", 0, -30) -- Shifted down below Axiom Header
    sideBar:SetPoint("BOTTOMLEFT", 0, 0)
    CreateBg(sideBar, 0.15, 0.15, 0.15, 1)
    CreateBorder(sideBar, 0, 0, 0, 1)
    self.SideBar = sideBar
    
    local container = CreateFrame("Frame", "GRS_ViewContainer", self)
    container:SetPoint("TOPLEFT", sideBar, "TOPRIGHT", 0, 0)
    container:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0)
    self.ViewContainer = container

    -- Original Title Bar (Preserved as Sub-Header)
    local top = CreateFrame("Frame", nil, container)
    CreateBg(top, 0.1, 0.1, 0.1, 1)
    top:SetHeight(30); top:SetPoint("TOPLEFT"); top:SetPoint("TOPRIGHT")
    self.TitleBar = top
    
    local title = top:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("LEFT", 10, 0); title:SetText("Guild Recruiter System")
    self.MainTitle = title
    
    local ioStatus = top:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    ioStatus:SetPoint("LEFT", title, "RIGHT", 10, 0)
    self.ioStatus = ioStatus; self:UpdateRaiderIOStatus()
    
    local offStatus = top:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    offStatus:SetPoint("LEFT", ioStatus, "RIGHT", 10, 0)
    self.officerStatus = offStatus
    
    -- Tabs Generation
    self.Tabs = {}
    local tabsData = NS.Constants.TABS
    for i, data in ipairs(tabsData) do
        local tab = CreateFrame("Button", nil, sideBar)
        tab:SetSize(32, 32)
        tab:SetPoint("TOP", sideBar, "TOP", 0, -10 - ((i-1) * 40))
        
        local tex = tab:CreateTexture(nil, "ARTWORK")
        tex:SetAllPoints()
        tex:SetTexture(data.icon)
        tab.icon = tex
        
        tab:SetScript("OnEnter", function(s)
            GameTooltip:SetOwner(s, "ANCHOR_CURSOR")
            GameTooltip:SetText(data.title)
            GameTooltip:Show()
            if s.timer then s.timer:Cancel() end
            s.timer = C_Timer.NewTimer(5, function()
                GameTooltip:Hide()
            end)
            if s.bg then s.bg:SetColorTexture(0.3, 0.3, 0.3, 1) end
        end)
        
        tab:SetScript("OnLeave", function(s)
            GameTooltip:Hide()
            if s.timer then s.timer:Cancel() end
            if s.bg then 
                 if GR.activeTab == i then
                     s.bg:SetColorTexture(1, 1, 1, 0.2)
                 else
                     s.bg:SetColorTexture(0, 0, 0, 0)
                 end
            end
        end)
        
        tab:SetScript("OnClick", function()
            if data.id == 7 and GR.OpenSettings then
                GR:OpenSettings()
            else
                GR:SwitchView(data.id)
            end
        end)
        
        local selBg = tab:CreateTexture(nil, "BACKGROUND")
        selBg:SetAllPoints()
        selBg:SetColorTexture(1, 1, 1, 0.2)
        selBg:Hide()
        tab.selBg = selBg
        
        self.Tabs[i] = tab
    end
end

function GR:SwitchView(id)
    self.activeTab = id
    for i, tab in ipairs(self.Tabs) do
        if i == id then
            tab:SetAlpha(1.0)
            tab.selBg:Show()
        else
            tab:SetAlpha(0.6)
            tab.selBg:Hide()
        end
    end
    for viewId, frame in pairs(self.views) do
        if viewId == id then
            frame:Show()
        else
            frame:Hide()
        end
    end
    self.MainTitle:SetText(NS.Constants.TABS[id].title)
end

function GR:BuildModuleContainers()
    for i = 3, 6 do
        local f = CreateFrame("Frame", nil, self.ViewContainer)
        f:SetAllPoints()
        f:Hide()
        self.views[i] = f
    end
end

-- =========================================================================
-- UI: HOME VIEW
-- =========================================================================

function GR:BuildHomeView()
    local parent = CreateFrame("Frame", "GRS_HomeView", self.ViewContainer)
    parent:SetPoint("TOPLEFT", 0, -30) 
    parent:SetPoint("BOTTOMRIGHT", 0, 0)
    self.views[1] = parent

    local outerScroll = CreateFrame("ScrollFrame", "GRS_OuterScroll", parent, "UIPanelScrollFrameTemplate")
    outerScroll:SetPoint("TOPLEFT", 0, 0)
    outerScroll:SetPoint("BOTTOMRIGHT", -28, 25) 
    
    if outerScroll.ScrollBar then outerScroll.ScrollBar:Hide(); outerScroll.ScrollBar:SetWidth(0) end
    
    local content = CreateFrame("Frame", "GRS_ScrollChild", outerScroll)
    outerScroll:SetScrollChild(content)
    
    local left = CreateFrame("Frame", nil, content)
    left:SetWidth(320); left:SetPoint("TOPLEFT"); left:SetPoint("BOTTOMLEFT")
    CreateBg(left, 0.1, 0.1, 0.1, 0.5); CreateBorder(left, 0.2, 0.2, 0.2, 1)
    self:BuildLeftPane(left)

    local rightPane = CreateFrame("Frame", "GRS_RightPane", content)
    rightPane:SetWidth(500)
    rightPane:SetPoint("TOPRIGHT"); rightPane:SetPoint("BOTTOMRIGHT")
    CreateBg(rightPane, 0.05, 0.05, 0.05, 0.8)
    CreateBorder(rightPane, 0.2, 0.2, 0.2, 1)
    self:BuildWhisperPane(rightPane)

    local center = CreateFrame("Frame", "GRS_CenterPane", content)
    center:SetPoint("TOPLEFT", left, "TOPRIGHT")
    center:SetPoint("BOTTOMRIGHT", rightPane, "BOTTOMLEFT")
    self:BuildCenterPane(center)
    
    if self.BuildFilterUI then self:BuildFilterUI() end
    
    parent:SetScript("OnSizeChanged", function(self, w, h)
        local scrollW = w - 52
        local scrollH = h - 45
        local leftPaneW = 320
        local rightPaneW = 500 
        local minCenterW = 760 
        local requiredMinW = leftPaneW + minCenterW + rightPaneW
        local finalContentW = math.max(scrollW, requiredMinW)
        local finalH = math.max(scrollH, 1)
        
        content:SetWidth(finalContentW)
        content:SetHeight(finalH)
        
        local listAvailable = finalH - 115
        if listAvailable < 20 then listAvailable = 20 end

        local whisperAvailable = finalH - 50
        if whisperAvailable < 20 then whisperAvailable = 20 end

        if GR.UpdateResultsLayout then GR:UpdateResultsLayout(listAvailable) end
        if GR.UpdateWhisperLayout then GR:UpdateWhisperLayout(whisperAvailable) end
    end)
    
    GR.UpdateHomeLayout = function()
        local w, h = parent:GetSize()
        parent:GetScript("OnSizeChanged")(parent, w, h)
    end
end

-- =========================================================================
-- UI: PANES (Left, Center, Whisper)
-- =========================================================================

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("SEARCH FILTERS", "GameFontNormal", 0); y = y - 30
    
    local dFilt = CreateFrame("Button", nil, parent); 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("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); CreateBg(dFiltList, 0.1, 0.1, 0.1, 1); 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-OptionsButton")
    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)
    
    y = y - 40; AddText("Level Range", "GameFontHighlightSmall", 0)
    local minB = CreateFrame("EditBox", nil, parent, "InputBoxTemplate"); minB:SetSize(40, 20); minB:SetPoint("TOPLEFT", 15, y - 20); minB:SetNumeric(true); minB:SetText(self.db.minLevel)
    minB:SetAutoFocus(false)
    minB:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    minB:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
    minB:SetScript("OnTextChanged", function(e) self.db.minLevel = tonumber(e:GetText()) or 70 end)
    
    local maxB = CreateFrame("EditBox", nil, parent, "InputBoxTemplate"); maxB:SetSize(40, 20); maxB:SetPoint("LEFT", minB, "RIGHT", 15, 0); maxB:SetNumeric(true); maxB:SetText(self.db.maxLevel)
    maxB:SetAutoFocus(false)
    maxB:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    maxB:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
    maxB:SetScript("OnTextChanged", function(e) self.db.maxLevel = tonumber(e:GetText()) or 80 end)
    
    y = y - 50; AddText("Scan Scope", "GameFontHighlightSmall", 0); y = y - 20
    local function MkRemBtn(text, mode, x)
        local b = CreateFrame("Button", nil, parent); 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
    self.btnRetail = MkRemBtn("Retail", "OFF", 0); self.btnAllow = MkRemBtn("All", "ALLOW", 85); self.btnRemix = MkRemBtn("Remix", "ONLY", 170)
    
    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)
        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
    end
    self:UpdateRemixVisuals()
    
    y = y - 40; AddText("MESSAGE TEMPLATES", "GameFontNormal", 0); y = y - 30
    local dTemp = CreateFrame("Button", nil, parent); 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); CreateBg(dTempList, 0.1, 0.1, 0.1, 1); 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("New")
    btnNew:SetScript("OnClick", function() GR.InputName:SetText(""); GR.InputBody:SetText(""); GR.InputName:SetFocus() end)
    SkinButton(btnNew)

    y = y - 30
    local nameIn = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
    nameIn:SetSize(250, 20); nameIn:SetPoint("TOPLEFT", 15, y); nameIn:SetAutoFocus(false)
    nameIn:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    self.InputName = nameIn
    local ph = nameIn:CreateFontString(nil, "ARTWORK", "GameFontDisable"); ph:SetPoint("LEFT", 5, 0); ph:SetText("Template Name...")
    
    nameIn:SetScript("OnTextChanged", function(self) 
        if self: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)
    
    y = y - 30
    local bodyF = CreateFrame("Frame", nil, parent); CreateBg(bodyF, 0.1, 0.1, 0.1, 0.5); bodyF:SetSize(290, 100); bodyF:SetPoint("TOPLEFT", 15, y)
    local scrollM = CreateFrame("ScrollFrame", nil, bodyF, "UIPanelScrollFrameTemplate"); scrollM:SetPoint("TOPLEFT", 5, -5); scrollM:SetPoint("BOTTOMRIGHT", -25, 5)
    local editM = CreateFrame("EditBox", nil, scrollM); editM:SetSize(265, 100); editM:SetMultiLine(true); editM:SetFontObject(ChatFontNormal); 
    editM:SetMaxLetters(255); 
    editM:SetAutoFocus(false) 
    editM:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    editM:SetText(self.db.templates[self.db.messageTemplate] or ""); scrollM:SetScrollChild(editM); self.InputBody = editM

    local charCount = bodyF:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    charCount:SetPoint("BOTTOMRIGHT", -30, 2)
    local function UpdateCharCount()
        local len = string.len(editM:GetText() or "")
        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)
    editM:SetScript("OnEditFocusGained", function(self) UpdateCharCount() end) 
    UpdateCharCount() 
    
    y = y - 110
    local btnSave = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate"); btnSave:SetSize(80, 22); btnSave:SetPoint("TOPLEFT", 15, y); btnSave:SetText("Save")
    btnSave:SetScript("OnClick", function()
        local n, b = self.InputName:GetText(), self.InputBody:GetText()
        if n ~= "" and b ~= "" then
            self.db.templates[n] = b; self.db.messageTemplate = n; dTempTxt:SetText(n); print("Saved Template: " .. n)
        end
    end)
    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("")
    CreateBg(btnHelp, 0.4, 0.0, 0.0, 1)
    CreateBorder(btnHelp, 0, 0, 0, 1)
    
    local hText = btnHelp:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    hText:SetPoint("CENTER"); hText:SetText("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)

    -- 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)
        
        SendChatMessage(msg, "WHISPER", nil, myName)
        print("GRS: Test message sent to self.")
    end)
    SkinButton(btnTest)
end

function GR:BuildHelpPopup()
    local f = CreateFrame("Frame", "GRS_HelpPopup", UIParent)
    f:SetSize(400, 300)
    f:SetPoint("CENTER")
    f:SetFrameStrata("FULLSCREEN_DIALOG")
    CreateBg(f, 0, 0, 0, 0.95)
    CreateBorder(f, 0.5, 0.5, 0.5, 1)
    
    local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    close:SetPoint("TOPRIGHT", -5, -5)
    
    local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -15)
    title:SetText("Template Help")
    
    local content = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    content:SetPoint("TOPLEFT", 20, -50)
    content:SetPoint("BOTTOMRIGHT", -20, 20)
    content:SetJustifyH("LEFT")
    content:SetJustifyV("TOP")
    
    local helpText = 
        "Extra functionalities for the message template:\n\n" ..
        "|cff00ffff[player]|r - Scans and mentions the players name.\n" ..
        "|cff00ffff[ownname]|r - Scans and mentions YOUR name.\n" ..
        "|cff00ffff[Guild]|r - Scans and mentions your guilds name.\n" ..
        "|cff00ffff[playerclass]|r - Scans and mentions players class.\n"
    
    content:SetText(helpText)
    
    GR.HelpFrame = f
end

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)

    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="Max Key", key="maxKey", width=50}, 
        {text="Zone", key="zone", width=110}, 
        {text="Score", key="rio", width=70},  
        {text="Heroic", key="heroic", width=70}, 
        {text="Mythic", key="mythic", width=70}  
    }
    
    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", 0, 0)
        txt:SetText(data.text)
        btn:SetFontString(txt)
        
        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")
    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)
    
    local snd = CreateFrame("Button", nil, bot, "UIPanelButtonTemplate"); snd:SetSize(120, 30); snd:SetPoint("RIGHT", -10, 0); snd:SetText("Send Batch")
    snd:SetScript("OnClick", function() self:SendMessageSequence() end)
    SkinButton(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)

    self.StatusText = bot:CreateFontString(nil,"OVERLAY","GameFontNormal"); 
    self.StatusText:SetPoint("RIGHT", btnBL, "LEFT", -20, 0); 
    self.StatusText:SetText("Ready.")

    self.scroll = CreateFrame("ScrollFrame", "GRS_S", parent, "FauxScrollFrameTemplate")
    parent.scrollFrame = self.scroll
    parent.scroll = self.scroll 
    local scrollFrame = self.scroll 
    
    scrollFrame:SetPoint("TOPLEFT", headF, "BOTTOMLEFT", 0, -5)
    scrollFrame:SetPoint("BOTTOMRIGHT", bot, "TOPRIGHT", -5, 5)
    
    CreateBg(scrollFrame, 0, 0, 0, 0.8) 
    
    local scrollBar = _G["GRS_SScrollBar"]
    if scrollBar then
        CreateBg(scrollBar, 0.1, 0.1, 0.1, 0.8) 
    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

        local availableW = w - 30 
        local MAX_WIDTH = 760 
        
        if availableW > MAX_WIDTH then
            bot:ClearAllPoints()
            bot:SetPoint("BOTTOMLEFT", 15, 5)
            bot:SetWidth(MAX_WIDTH)
            
            sFrame:ClearAllPoints()
            sFrame:SetPoint("TOPLEFT", headF, "BOTTOMLEFT", 0, -5)
            sFrame:SetPoint("BOTTOMLEFT", bot, "TOPLEFT", 0, 5)
            sFrame:SetWidth(MAX_WIDTH)
        else
            bot:ClearAllPoints()
            bot:SetPoint("BOTTOMLEFT", 15, 5)
            bot:SetPoint("BOTTOMRIGHT", -15, 5)
            
            sFrame:ClearAllPoints()
            sFrame:SetPoint("TOPLEFT", headF, "BOTTOMLEFT", 0, -5)
            sFrame:SetPoint("BOTTOMRIGHT", bot, "TOPRIGHT", -5, 5)
        end
    end)
    
    self.rows = {}
    
    function GR:UpdateResultsLayout(height)
        if not height then height = self.scroll:GetHeight() end
        local numRows = math.floor(height / 20)
        self.visibleRows = numRows 
        
        for i = 1, numRows do
            if not self.rows[i] then
                local r = CreateFrame("Button", nil, parent)
                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.mk = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.mk:SetPoint("LEFT",r.lvl,"RIGHT"); r.mk:SetWidth(50); r.mk:SetJustifyH("LEFT") 

                r.z = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.z:SetPoint("LEFT",r.mk,"RIGHT"); r.z:SetWidth(110); r.z:SetJustifyH("LEFT")
                r.s = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.s:SetPoint("LEFT",r.z,"RIGHT"); r.s:SetWidth(70); r.s:SetJustifyH("LEFT")
                r.h = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.h:SetPoint("LEFT",r.s,"RIGHT"); r.h:SetWidth(70); r.h:SetJustifyH("LEFT")
                r.m = r:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall"); r.m:SetPoint("LEFT",r.h,"RIGHT"); r.m:SetWidth(70); r.m:SetJustifyH("LEFT")
                self.rows[i] = r
            end
        end
        self:UpdateScrollFrame() 
    end
end

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() 
        local maxP = math.ceil(#GR.activeChats/(GR.chatPageSize or 4))
        if maxP < 1 then maxP=1 end
        if GR.currentPage < maxP 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)
    
    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" and MenuUtil then
                        MenuUtil.CreateContextMenu(s, function(owner, root)
                            root:CreateTitle(s.playerName); root:CreateButton("Invite", function() C_PartyInfo.InviteUnit(s.playerName) end)
                        end)
                    end
                end)
                
                local closeChat = CreateFrame("Button", nil, cf, "UIPanelCloseButton"); closeChat:SetSize(20, 20); closeChat:SetPoint("TOPRIGHT", -2, -2)
                closeChat:SetScript("OnClick", function()
                    local idx = (GR.currentPage - 1) * self.chatPageSize + i
                    if GR.activeChats[idx] then table.remove(GR.activeChats, idx); GR:RenderWhisperGrid() end
                end)
                
                cf.scroll = CreateFrame("ScrollFrame", nil, cf, "UIPanelScrollFrameTemplate"); cf.scroll:SetPoint("TOPLEFT", 5, -20); cf.scroll:SetPoint("BOTTOMRIGHT", -25, 5)
                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

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.mk:SetText(p.maxKey); 
                
                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)
                
                local sc = p.rio or 0; local col = "|cffffffff"
                if sc>=2500 then col="|cffff8000" 
                elseif sc>=2000 then col="|cffa335ee" 
                elseif sc>=1000 then col="|cff0070dd" 
                end
                r.s:SetText(col..sc.."|r")
                
                local function Col(str)
                    if not str or str == "-" then return "|cff808080"..str.."|r" end
                    local k = tonumber(str:match("^(%d+)")) or 0
                    if k >= 8 then return "|cffff8000"..str.."|r"
                    elseif k >= 4 then return "|cffa335ee"..str.."|r"
                    else return "|cff00ff00"..str.."|r" end
                end
                
                r.h:SetText(Col(p.heroic)); r.m:SetText(Col(p.mythic))
                r.c:SetChecked(p.selected)
            else 
                r:Hide() 
            end
        end
    end
end