--------------------------------------------------------------------------------
-- Nanami-UI: Social UI (SocialUI.lua)
-- Replaces FriendsFrame with modern rounded UI
-- Tabs: Friends/Ignore, Who, Guild, Raid
--------------------------------------------------------------------------------

SFrames = SFrames or {}
SFrames.SocialUI = {}
local SUI = SFrames.SocialUI
SFramesDB = SFramesDB or {}

--------------------------------------------------------------------------------
-- Theme (Pink Cat-Paw)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
    onlineText  = { 0.30, 1.0, 0.30 },
    offlineText = { 0.50, 0.45, 0.48 },
})

local CLASS_COLORS = {
    ["WARRIOR"]  = { 0.78, 0.61, 0.43 },
    ["MAGE"]     = { 0.41, 0.80, 0.94 },
    ["ROGUE"]    = { 1.00, 0.96, 0.41 },
    ["DRUID"]    = { 1.00, 0.49, 0.04 },
    ["HUNTER"]   = { 0.67, 0.83, 0.45 },
    ["SHAMAN"]   = { 0.14, 0.35, 1.00 },
    ["PRIEST"]   = { 1.00, 1.00, 1.00 },
    ["WARLOCK"]  = { 0.58, 0.51, 0.79 },
    ["PALADIN"]  = { 0.96, 0.55, 0.73 },
}

-- Reverse lookup: localized class name -> English key
local CLASS_NAME_TO_EN = {}
local function BuildClassReverseLookup()
    -- Try WoW global tables first
    if LOCALIZED_CLASS_NAMES_MALE then
        for en, loc in pairs(LOCALIZED_CLASS_NAMES_MALE) do
            CLASS_NAME_TO_EN[loc] = en
        end
    end
    if LOCALIZED_CLASS_NAMES_FEMALE then
        for en, loc in pairs(LOCALIZED_CLASS_NAMES_FEMALE) do
            CLASS_NAME_TO_EN[loc] = en
        end
    end
    -- Hardcoded Chinese fallback
    local zhMap = {
        ["战士"] = "WARRIOR", ["法师"] = "MAGE", ["盗贼"] = "ROGUE",
        ["德鲁伊"] = "DRUID", ["猎人"] = "HUNTER", ["萨满祭司"] = "SHAMAN",
        ["牧师"] = "PRIEST", ["术士"] = "WARLOCK", ["圣骑士"] = "PALADIN",
    }
    for loc, en in pairs(zhMap) do
        if not CLASS_NAME_TO_EN[loc] then
            CLASS_NAME_TO_EN[loc] = en
        end
    end
    -- Also add English names themselves
    for en, _ in pairs(CLASS_COLORS) do
        CLASS_NAME_TO_EN[en] = en
        CLASS_NAME_TO_EN[string.lower(en)] = en
    end
end

--------------------------------------------------------------------------------
-- Chinese -> English search-term translation for SendWho
--------------------------------------------------------------------------------
local WHO_ZH_TO_EN = {
    -- Classes
    ["战士"] = "Warrior", ["法师"] = "Mage", ["盗贼"] = "Rogue",
    ["德鲁伊"] = "Druid", ["猎人"] = "Hunter", ["萨满祭司"] = "Shaman",
    ["萨满"] = "Shaman", ["牧师"] = "Priest", ["术士"] = "Warlock",
    ["圣骑士"] = "Paladin",
    -- Races
    ["人类"] = "Human", ["矮人"] = "Dwarf", ["暗夜精灵"] = "Night Elf",
    ["侏儒"] = "Gnome", ["兽人"] = "Orc", ["巨魔"] = "Troll",
    ["亡灵"] = "Undead", ["牛头人"] = "Tauren", ["高等精灵"] = "High Elf",
    ["哥布林"] = "Goblin",
    -- Zones (Alliance)
    ["暴风城"] = "Stormwind", ["铁炉堡"] = "Ironforge", ["达纳苏斯"] = "Darnassus",
    ["艾尔文森林"] = "Elwynn Forest", ["西部荒野"] = "Westfall",
    ["丹莫罗"] = "Dun Morogh", ["洛克莫丹"] = "Loch Modan",
    ["湿地"] = "Wetlands", ["赤脊山"] = "Redridge Mountains",
    ["暮色森林"] = "Duskwood", ["荆棘谷"] = "Stranglethorn Vale",
    ["泰达希尔"] = "Teldrassil", ["黑海岸"] = "Darkshore",
    ["灰谷"] = "Ashenvale", ["石爪山脉"] = "Stonetalon Mountains",
    -- Zones (Horde)
    ["奥格瑞玛"] = "Orgrimmar", ["雷霆崖"] = "Thunder Bluff",
    ["幽暗城"] = "Undercity", ["杜隆塔尔"] = "Durotar",
    ["莫高雷"] = "Mulgore", ["贫瘠之地"] = "The Barrens",
    ["银松森林"] = "Silverpine Forest", ["提瑞斯法林地"] = "Tirisfal Glades",
    ["希尔斯布莱德丘陵"] = "Hillsbrad Foothills",
    -- Zones (Contested / High-level)
    ["塔纳利斯"] = "Tanaris", ["菲拉斯"] = "Feralas",
    ["凄凉之地"] = "Desolace", ["尘泥沼泽"] = "Dustwallow Marsh",
    ["千针石林"] = "Thousand Needles", ["辛特兰"] = "The Hinterlands",
    ["阿拉希高地"] = "Arathi Highlands", ["荒芜之地"] = "Badlands",
    ["灼热峡谷"] = "Searing Gorge", ["燃烧平原"] = "Burning Steppes",
    ["西瘟疫之地"] = "Western Plaguelands", ["东瘟疫之地"] = "Eastern Plaguelands",
    ["费伍德森林"] = "Felwood", ["冬泉谷"] = "Winterspring",
    ["安戈洛环形山"] = "Un'Goro Crater", ["希利苏斯"] = "Silithus",
    ["艾萨拉"] = "Azshara", ["诅咒之地"] = "Blasted Lands",
    ["逆风小径"] = "Deadwind Pass", ["悲伤沼泽"] = "Swamp of Sorrows",
    -- Dungeons / Raids
    ["熔火之心"] = "Molten Core", ["黑翼之巢"] = "Blackwing Lair",
    ["奥妮克希亚的巢穴"] = "Onyxia's Lair", ["祖尔格拉布"] = "Zul'Gurub",
    ["安其拉"] = "Ahn'Qiraj", ["纳克萨玛斯"] = "Naxxramas",
    ["黑石深渊"] = "Blackrock Depths", ["黑石塔"] = "Blackrock Spire",
    ["斯坦索姆"] = "Stratholme", ["通灵学院"] = "Scholomance",
    ["厄运之槌"] = "Dire Maul", ["玛拉顿"] = "Maraudon",
    ["祖尔法拉克"] = "Zul'Farrak",
}

local function TranslateWhoQuery(text)
    if not text then return "" end
    for zh, en in pairs(WHO_ZH_TO_EN) do
        text = string.gsub(text, zh, en)
    end
    return text
end

local CLASS_ICON_PATH = "Interface\\AddOns\\Nanami-UI\\img\\UI-Classes-Circles"
local CLASS_ICON_TCOORDS = {
    ["WARRIOR"]  = { 0, 0.25, 0, 0.25 },
    ["MAGE"]     = { 0.25, 0.49609375, 0, 0.25 },
    ["ROGUE"]    = { 0.49609375, 0.7421875, 0, 0.25 },
    ["DRUID"]    = { 0.7421875, 0.98828125, 0, 0.25 },
    ["HUNTER"]   = { 0, 0.25, 0.25, 0.5 },
    ["SHAMAN"]   = { 0.25, 0.49609375, 0.25, 0.5 },
    ["PRIEST"]   = { 0.49609375, 0.7421875, 0.25, 0.5 },
    ["WARLOCK"]  = { 0.7421875, 0.98828125, 0.25, 0.5 },
    ["PALADIN"]  = { 0, 0.25, 0.5, 0.75 },
}

--------------------------------------------------------------------------------
-- Layout
--------------------------------------------------------------------------------
local FRAME_W       = 380
local FRAME_H       = 455
local HEADER_H      = 30
local TAB_BAR_H     = 26
local SIDE_PAD      = 10
local CONTENT_W     = FRAME_W - SIDE_PAD * 2
local ROW_H         = 22
local BOTTOM_H      = 30
local SCROLL_STEP   = 22

--------------------------------------------------------------------------------
-- State
--------------------------------------------------------------------------------
local MainFrame     = nil
local mainTabs      = {}
local pages         = {}
local currentMainTab = 1
local initialized   = false

local friendRows    = {}
local ignoreRows    = {}
local whoRows       = {}
local guildRows     = {}
local raidSlots     = {}
local selectedFriend = nil
local selectedWho   = nil
local selectedGuild = nil
local friendSubTab  = "friends"
local guildViewMode = "player"
local guildHideOffline = false
local guildSortField = "name"
local guildSortAsc = true
local friendSearchText = ""
local guildSearchText = ""
local origShowFriendsAPI = nil

local widgetId = 0
local function NextName(p)
    widgetId = widgetId + 1
    return "SFramesSocial" .. (p or "") .. tostring(widgetId)
end

--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function GetFont()
    if SFrames and SFrames.GetFont then return SFrames:GetFont() end
    return "Fonts\\ARIALN.TTF"
end

local function SetRoundBackdrop(frame, bgColor, borderColor)
    frame:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    local bg = bgColor or T.panelBg
    local bd = borderColor or T.panelBorder
    frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
    frame:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
end

local function SetPixelBackdrop(frame, bgColor, borderColor)
    frame:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        tile = false, tileSize = 0, edgeSize = 1,
        insets = { left = 1, right = 1, top = 1, bottom = 1 },
    })
    local bg = bgColor or T.slotBg
    local bd = borderColor or T.slotBorder
    frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
    frame:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
end

local function SetRowNormal(frame)
    SetPixelBackdrop(frame, T.rowNormal, T.rowNormalBd)
end

local function AddSelHighlight(row)
    local sb = row:CreateTexture(nil, "ARTWORK")
    sb:SetTexture("Interface\\Buttons\\WHITE8X8")
    sb:SetAllPoints(row)
    sb:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.35)
    sb:Hide(); row._selBg = sb

    local sg = row:CreateTexture(nil, "ARTWORK")
    sg:SetTexture("Interface\\Buttons\\WHITE8X8")
    sg:SetWidth(4)
    sg:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
    sg:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
    sg:SetVertexColor(1, 0.65, 0.85, 1)
    sg:Hide(); row._selGlow = sg

    local st = row:CreateTexture(nil, "OVERLAY")
    st:SetTexture("Interface\\Buttons\\WHITE8X8")
    st:SetHeight(1)
    st:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
    st:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, 0)
    st:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.8)
    st:Hide(); row._selTop = st

    local sbo = row:CreateTexture(nil, "OVERLAY")
    sbo:SetTexture("Interface\\Buttons\\WHITE8X8")
    sbo:SetHeight(1)
    sbo:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
    sbo:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
    sbo:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.8)
    sbo:Hide(); row._selBot = sbo
end

local function ShowSelHighlight(row)
    if row._selBg then row._selBg:Show() end
    if row._selGlow then row._selGlow:Show() end
    if row._selTop then row._selTop:Show() end
    if row._selBot then row._selBot:Show() end
end

local function HideSelHighlight(row)
    if row._selBg then row._selBg:Hide() end
    if row._selGlow then row._selGlow:Hide() end
    if row._selTop then row._selTop:Hide() end
    if row._selBot then row._selBot:Hide() end
end

local function CreateShadow(parent)
    local s = CreateFrame("Frame", nil, parent)
    s:SetPoint("TOPLEFT", parent, "TOPLEFT", -4, 4)
    s:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 4, -4)
    s:SetFrameLevel(math.max(parent:GetFrameLevel() - 1, 0))
    s:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    s:SetBackdropColor(0, 0, 0, 0.6)
    s:SetBackdropBorderColor(0, 0, 0, 0.45)
    return s
end

local function MakeFS(parent, size, justifyH, color)
    local fs = parent:CreateFontString(nil, "OVERLAY")
    fs:SetFont(GetFont(), size or 11, "OUTLINE")
    fs:SetJustifyH(justifyH or "LEFT")
    local c = color or T.nameText
    fs:SetTextColor(c[1], c[2], c[3])
    return fs
end

local function MakeSep(parent, y)
    local sep = parent:CreateTexture(nil, "ARTWORK")
    sep:SetTexture("Interface\\Buttons\\WHITE8X8")
    sep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
    sep:SetHeight(1)
    sep:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y)
    sep:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, y)
    return sep
end

local function MakeButton(parent, text, w, h)
    local btn = CreateFrame("Button", NextName("Btn"), parent)
    btn:SetWidth(w or 80)
    btn:SetHeight(h or 22)
    SetRoundBackdrop(btn, T.btnBg, T.btnBorder)
    local fs = MakeFS(btn, 11, "CENTER", T.btnText)
    fs:SetPoint("CENTER", 0, 0)
    fs:SetText(text or "")
    btn.text = fs
    btn:SetScript("OnEnter", function()
        SetRoundBackdrop(this, T.btnHoverBg, T.tabActiveBorder)
        this.text:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3])
    end)
    btn:SetScript("OnLeave", function()
        SetRoundBackdrop(this, T.btnBg, T.btnBorder)
        this.text:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
    end)
    return btn
end

local function MakeEditBox(parent, w, h)
    local box = CreateFrame("EditBox", NextName("Edit"), parent)
    box:SetWidth(w or 200)
    box:SetHeight(h or 22)
    box:SetFont(GetFont(), 11, "OUTLINE")
    box:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
    box:SetAutoFocus(false)
    box:SetMaxLetters(256)
    SetPixelBackdrop(box, T.inputBg, T.inputBorder)
    box:SetTextInsets(6, 6, 0, 0)
    box:SetScript("OnEscapePressed", function() this:ClearFocus() end)
    return box
end

local customPopup = nil
local function ShowCustomPopup(title, hasInput, onAccept, onCancel)
    if not customPopup then
        local f = CreateFrame("Frame", "SFramesSocialPopup", UIParent)
        f:SetWidth(280)
        f:SetHeight(120)
        f:SetPoint("CENTER", UIParent, "CENTER", 0, 80)
        f:SetFrameStrata("DIALOG")
        f:SetFrameLevel(100)
        SetRoundBackdrop(f, T.panelBg, T.panelBorder)
        CreateShadow(f)
        f:EnableMouse(true)
        f:SetMovable(true)
        f:RegisterForDrag("LeftButton")
        f:SetScript("OnDragStart", function() this:StartMoving() end)
        f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)

        local titleFS = MakeFS(f, 12, "CENTER", T.headerText)
        titleFS:SetPoint("TOP", f, "TOP", 0, -10)
        f.titleFS = titleFS

        local editBox = CreateFrame("EditBox", "SFramesSocialPopupEdit", f)
        editBox:SetWidth(230)
        editBox:SetHeight(24)
        editBox:SetPoint("TOP", titleFS, "BOTTOM", 0, -10)
        editBox:SetFont(GetFont(), 11, "OUTLINE")
        editBox:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
        editBox:SetAutoFocus(false)
        editBox:SetMaxLetters(64)
        SetRoundBackdrop(editBox, T.inputBg, T.inputBorder)
        editBox:SetTextInsets(8, 8, 4, 4)
        editBox:SetScript("OnEscapePressed", function()
            this:ClearFocus()
            f:Hide()
        end)
        f.editBox = editBox

        local acceptBtn = MakeButton(f, "确定", 100, 24)
        acceptBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOM", -4, 10)
        f.acceptBtn = acceptBtn

        local cancelBtn = MakeButton(f, "取消", 100, 24)
        cancelBtn:SetPoint("BOTTOMLEFT", f, "BOTTOM", 4, 10)
        cancelBtn:SetScript("OnClick", function()
            f:Hide()
        end)
        f.cancelBtn = cancelBtn

        f:Hide()
        customPopup = f
    end

    customPopup.titleFS:SetText(title or "")
    customPopup.editBox:SetText("")

    if hasInput then
        customPopup.editBox:Show()
        customPopup:SetHeight(120)
    else
        customPopup.editBox:Hide()
        customPopup:SetHeight(80)
    end

    customPopup.acceptBtn:SetScript("OnClick", function()
        local text = customPopup.editBox:GetText()
        customPopup:Hide()
        if onAccept then onAccept(text) end
    end)

    customPopup.editBox:SetScript("OnEnterPressed", function()
        local text = customPopup.editBox:GetText()
        customPopup:Hide()
        if onAccept then onAccept(text) end
    end)

    customPopup:Show()
    if hasInput then
        customPopup.editBox:SetFocus()
    end
end

local function GetClassEN(localizedName)
    if not localizedName then return nil end
    return CLASS_NAME_TO_EN[localizedName] or CLASS_NAME_TO_EN[string.upper(localizedName)]
end

local function GetClassColor(classEN)
    if not classEN then return T.nameText end
    local c = CLASS_COLORS[string.upper(classEN)]
    if c then return c end
    return T.nameText
end

local function CreateClassIcon(parent, size)
    local sz = size or 16
    local icon = parent:CreateTexture(nil, "OVERLAY")
    icon:SetTexture(CLASS_ICON_PATH)
    icon:SetWidth(sz)
    icon:SetHeight(sz)
    return icon
end

local function SetClassIcon(icon, classEN)
    if not classEN then
        icon:Hide()
        return
    end
    local upper = string.upper(classEN)
    local coords = CLASS_ICON_TCOORDS[upper]
    if coords then
        icon:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
        icon:SetVertexColor(1, 1, 1)
        icon:Show()
    else
        icon:Hide()
    end
end

--------------------------------------------------------------------------------
-- Scroll Frame Helper
--------------------------------------------------------------------------------
local function CreateScrollArea(parent, w, h)
    local container = CreateFrame("Frame", nil, parent)
    container:SetWidth(w)
    container:SetHeight(h)

    local scrollFrame = CreateFrame("ScrollFrame", NextName("Scroll"), container)
    scrollFrame:SetWidth(w)
    scrollFrame:SetHeight(h)
    scrollFrame:SetAllPoints(container)

    local child = CreateFrame("Frame", nil, scrollFrame)
    child:SetWidth(w)
    child:SetHeight(1)
    scrollFrame:SetScrollChild(child)

    local offset = 0
    local maxOffset = 0

    container.SetContentHeight = function(self, ch)
        child:SetHeight(ch)
        maxOffset = math.max(0, ch - h)
        if offset > maxOffset then
            offset = maxOffset
            scrollFrame:SetVerticalScroll(offset)
        end
    end

    container.Reset = function(self)
        offset = 0
        scrollFrame:SetVerticalScroll(0)
    end

    local function DoScroll(delta)
        offset = offset - delta * SCROLL_STEP
        if offset < 0 then offset = 0 end
        if offset > maxOffset then offset = maxOffset end
        scrollFrame:SetVerticalScroll(offset)
    end

    scrollFrame:EnableMouseWheel(true)
    scrollFrame:SetScript("OnMouseWheel", function()
        DoScroll(arg1 or 0)
    end)
    container:EnableMouseWheel(true)
    container:SetScript("OnMouseWheel", function()
        DoScroll(arg1 or 0)
    end)

    container.child = child
    container.scrollFrame = scrollFrame
    return container
end

local function WhoDebug(msg)
    if DEFAULT_CHAT_FRAME then
        DEFAULT_CHAT_FRAME:AddMessage("|cff00ffcc[Who调试]|r " .. msg)
    end
end

--------------------------------------------------------------------------------
-- Hide Blizzard FriendsFrame
--------------------------------------------------------------------------------
local origFriendsFrameShow
local function HideBlizzardFriends()
    if not FriendsFrame then return end
    origFriendsFrameShow = FriendsFrame.Show
    FriendsFrame:Hide()
    FriendsFrame:SetAlpha(0)
    FriendsFrame:EnableMouse(false)
    FriendsFrame:ClearAllPoints()
    FriendsFrame:SetPoint("TOPLEFT", UIParent, "BOTTOMRIGHT", 2000, 2000)
    FriendsFrame:UnregisterAllEvents()
    FriendsFrame.Show = function() end

    if UIPanelWindows then
        UIPanelWindows["FriendsFrame"] = nil
    end

    for i = table.getn(UISpecialFrames), 1, -1 do
        if UISpecialFrames[i] == "FriendsFrame" then
            table.remove(UISpecialFrames, i)
        end
    end

    if SetWhoToUI then
        local origSetWhoToUI = SetWhoToUI
        SetWhoToUI = function(flag)
            if flag ~= 1 then
                WhoDebug("拦截 SetWhoToUI(" .. tostring(flag) .. ") -> 强制为1")
            end
            origSetWhoToUI(1)
        end
    end
end

--------------------------------------------------------------------------------
-- Who query helper
--------------------------------------------------------------------------------
local whoQueryPending = false
local whoTimeoutFrame = nil

local function DoSendWho(query)
    if whoQueryPending and whoTimeoutFrame then
        WhoDebug("取消上次挂起的查询")
        whoQueryPending = false
        whoTimeoutFrame:SetScript("OnUpdate", nil)
    end

    WhoDebug("发送查询: \"" .. (query or "") .. "\"")
    if SetWhoToUI then SetWhoToUI(1) end
    whoQueryPending = true
    SendWho(query or "")
    WhoDebug("SendWho() 已调用, 等待 WHO_LIST_UPDATE...")

    if not whoTimeoutFrame then
        whoTimeoutFrame = CreateFrame("Frame", nil, UIParent)
    end
    whoTimeoutFrame.elapsed = 0
    whoTimeoutFrame:SetScript("OnUpdate", function()
        this.elapsed = (this.elapsed or 0) + (arg1 or 0.016)
        if not whoQueryPending then
            this:SetScript("OnUpdate", nil)
            return
        end
        if this.elapsed >= 6 then
            local n = GetNumWhoResults()
            WhoDebug("超时! 6秒未收到事件, 强制刷新, 当前结果=" .. tostring(n))
            whoQueryPending = false
            this:SetScript("OnUpdate", nil)
            SUI:UpdateWhoList()
        end
    end)
end

--------------------------------------------------------------------------------
-- Tab 1: Friends / Ignore
--------------------------------------------------------------------------------
local function BuildFriendsPage(page)
    local subTabFrame = CreateFrame("Frame", nil, page)
    subTabFrame:SetHeight(16)
    subTabFrame:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    subTabFrame:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, 0)

    local friendsSubBtn = CreateFrame("Button", NextName("SubTab"), subTabFrame)
    friendsSubBtn:SetWidth(50)
    friendsSubBtn:SetHeight(16)
    friendsSubBtn:SetPoint("LEFT", subTabFrame, "LEFT", 0, 0)
    local fsBtnText = MakeFS(friendsSubBtn, 10, "CENTER", T.tabActiveText)
    fsBtnText:SetPoint("CENTER", 0, 0)
    fsBtnText:SetText("好友")
    friendsSubBtn.text = fsBtnText
    friendsSubBtn:SetScript("OnEnter", function()
        if friendSubTab ~= "friends" then
            this.text:SetTextColor(1, 1, 1)
        end
    end)
    friendsSubBtn:SetScript("OnLeave", function()
        if friendSubTab ~= "friends" then
            this.text:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
        end
    end)
    page.friendsSubBtn = friendsSubBtn

    local ignoreSubBtn = CreateFrame("Button", NextName("SubTab"), subTabFrame)
    ignoreSubBtn:SetWidth(50)
    ignoreSubBtn:SetHeight(16)
    ignoreSubBtn:SetPoint("LEFT", friendsSubBtn, "RIGHT", 6, 0)
    local igBtnText = MakeFS(ignoreSubBtn, 10, "CENTER", T.tabText)
    igBtnText:SetPoint("CENTER", 0, 0)
    igBtnText:SetText("屏蔽")
    ignoreSubBtn.text = igBtnText
    ignoreSubBtn:SetScript("OnEnter", function()
        if friendSubTab ~= "ignore" then
            this.text:SetTextColor(1, 1, 1)
        end
    end)
    ignoreSubBtn:SetScript("OnLeave", function()
        if friendSubTab ~= "ignore" then
            this.text:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
        end
    end)
    page.ignoreSubBtn = ignoreSubBtn

    -- Underline indicator for active sub-tab
    local subIndicator = friendsSubBtn:CreateTexture(nil, "OVERLAY")
    subIndicator:SetHeight(2)
    subIndicator:SetPoint("BOTTOMLEFT", friendsSubBtn, "BOTTOMLEFT", 4, 0)
    subIndicator:SetPoint("BOTTOMRIGHT", friendsSubBtn, "BOTTOMRIGHT", -4, 0)
    subIndicator:SetTexture(1, 1, 1, 1)
    subIndicator:SetVertexColor(T.tabActiveBorder[1], T.tabActiveBorder[2], T.tabActiveBorder[3], 0.8)
    page.subIndicator = subIndicator

    -- Friend search bar
    local friendSearchBar = CreateFrame("Frame", nil, page)
    friendSearchBar:SetHeight(20)
    friendSearchBar:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -18)
    friendSearchBar:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, -18)
    page.friendSearchBar = friendSearchBar

    local searchLabel = MakeFS(friendSearchBar, 9, "LEFT", T.dimText)
    searchLabel:SetPoint("LEFT", friendSearchBar, "LEFT", 2, 0)
    searchLabel:SetText("搜索:")

    local friendSearchBox = MakeEditBox(friendSearchBar, CONTENT_W - 36, 18)
    friendSearchBox:SetPoint("LEFT", searchLabel, "RIGHT", 4, 0)
    friendSearchBox:SetScript("OnTextChanged", function()
        friendSearchText = this:GetText() or ""
        SUI:UpdateFriendsList()
    end)
    friendSearchBox:SetScript("OnEnterPressed", function() this:ClearFocus() end)
    friendSearchBox:SetScript("OnEscapePressed", function()
        this:SetText("")
        this:ClearFocus()
    end)
    page.friendSearchBox = friendSearchBox

    -- Friends list
    local friendsArea = CreateFrame("Frame", nil, page)
    friendsArea:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -40)
    friendsArea:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, BOTTOM_H)
    page.friendsArea = friendsArea

    local fScroll = CreateScrollArea(friendsArea, CONTENT_W, FRAME_H - HEADER_H - TAB_BAR_H - 40 - BOTTOM_H - 16)
    fScroll:SetPoint("TOPLEFT", friendsArea, "TOPLEFT", 0, 0)
    page.fScroll = fScroll

    local MAX_FRIEND_ROWS = 50
    for idx = 1, MAX_FRIEND_ROWS do
        local rowIdx = idx
        local row = CreateFrame("Button", NextName("FR"), fScroll.child)
        row:SetWidth(CONTENT_W - 4)
        row:SetHeight(ROW_H)
        row:SetPoint("TOPLEFT", fScroll.child, "TOPLEFT", 2, -((rowIdx - 1) * ROW_H))
        row.rowIndex = rowIdx
        SetRowNormal(row)

        local classIcon = CreateClassIcon(row, 16)
        classIcon:SetPoint("LEFT", row, "LEFT", 4, 0)
        row.classIcon = classIcon

        local nameFS = MakeFS(row, 11, "LEFT", T.nameText)
        nameFS:SetPoint("LEFT", classIcon, "RIGHT", 4, 0)
        nameFS:SetWidth(120)
        row.nameFS = nameFS

        local infoFS = MakeFS(row, 9, "LEFT", T.dimText)
        infoFS:SetPoint("LEFT", nameFS, "RIGHT", 4, 0)
        infoFS:SetPoint("RIGHT", row, "RIGHT", -4, 0)
        row.infoFS = infoFS

        AddSelHighlight(row)
        row:RegisterForClicks("LeftButtonUp", "RightButtonUp")

        row:SetScript("OnEnter", function()
            SetPixelBackdrop(this, T.slotHover, T.slotSelected)
        end)
        row:SetScript("OnLeave", function()
            if selectedFriend and selectedFriend == this.friendDataIndex then
                SetPixelBackdrop(this, T.tabActiveBg, T.tabActiveBorder)
            else
                SetRowNormal(this)
            end
        end)
        row:SetScript("OnClick", function()
            selectedFriend = this.friendDataIndex
            if arg1 == "RightButton" and this.friendDataIndex then
                SUI:ShowFriendRowMenu(this)
                return
            end
            SUI:UpdateFriendsList()
        end)
        row:Hide()
        friendRows[idx] = row
    end

    -- Ignore list
    local ignoreArea = CreateFrame("Frame", nil, page)
    ignoreArea:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -20)
    ignoreArea:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, BOTTOM_H)
    ignoreArea:Hide()
    page.ignoreArea = ignoreArea

    local iScroll = CreateScrollArea(ignoreArea, CONTENT_W, FRAME_H - HEADER_H - TAB_BAR_H - 20 - BOTTOM_H - 16)
    iScroll:SetPoint("TOPLEFT", ignoreArea, "TOPLEFT", 0, 0)
    page.iScroll = iScroll

    local MAX_IGNORE_ROWS = 50
    for idx = 1, MAX_IGNORE_ROWS do
        local rowIdx = idx
        local row = CreateFrame("Button", NextName("IR"), iScroll.child)
        row:SetWidth(CONTENT_W - 4)
        row:SetHeight(20)
        row:SetPoint("TOPLEFT", iScroll.child, "TOPLEFT", 2, -((rowIdx - 1) * 20))
        row.rowIndex = rowIdx
        SetRowNormal(row)

        local nameFS = MakeFS(row, 11, "LEFT", T.nameText)
        nameFS:SetPoint("LEFT", row, "LEFT", 8, 0)
        row.nameFS = nameFS

        row:SetScript("OnEnter", function()
            SetPixelBackdrop(this, T.slotHover, T.slotSelected)
        end)
        row:SetScript("OnLeave", function()
            SetRowNormal(this)
        end)
        row:Hide()
        ignoreRows[idx] = row
    end

    -- Bottom buttons
    local btnBar = CreateFrame("Frame", nil, page)
    btnBar:SetHeight(BOTTOM_H)
    btnBar:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, 0)
    btnBar:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.btnBar = btnBar

    local addBtn = MakeButton(btnBar, "添加好友", 80, 22)
    addBtn:SetPoint("BOTTOMLEFT", btnBar, "BOTTOMLEFT", 0, 2)
    addBtn:SetScript("OnClick", function()
        if friendSubTab == "friends" then
            ShowCustomPopup("添加好友", true, function(name)
                if name and name ~= "" then AddFriend(name) end
            end)
        else
            ShowCustomPopup("添加屏蔽", true, function(name)
                if name and name ~= "" then AddIgnore(name) end
            end)
        end
    end)

    local removeBtn = MakeButton(btnBar, "删除好友", 80, 22)
    removeBtn:SetPoint("LEFT", addBtn, "RIGHT", 4, 0)
    removeBtn:SetScript("OnClick", function()
        if selectedFriend then RemoveFriend(selectedFriend) end
    end)

    local msgBtn = MakeButton(btnBar, "发送信息", 80, 22)
    msgBtn:SetPoint("LEFT", removeBtn, "RIGHT", 4, 0)
    msgBtn:SetScript("OnClick", function()
        if selectedFriend then
            local name = GetFriendInfo(selectedFriend)
            if name then ChatFrame_SendTell(name) end
        end
    end)

    local inviteBtn = MakeButton(btnBar, "组队邀请", 80, 22)
    inviteBtn:SetPoint("LEFT", msgBtn, "RIGHT", 4, 0)
    inviteBtn:SetScript("OnClick", function()
        if selectedFriend then
            local name = GetFriendInfo(selectedFriend)
            if name then InviteByName(name) end
        end
    end)

    page.addBtn = addBtn
    page.removeBtn = removeBtn
    page.msgBtn = msgBtn
    page.inviteBtn = inviteBtn

    friendsSubBtn:SetScript("OnClick", function()
        friendSubTab = "friends"
        SUI:UpdateFriendsPage()
    end)
    ignoreSubBtn:SetScript("OnClick", function()
        friendSubTab = "ignore"
        SUI:UpdateFriendsPage()
    end)
end

function SUI:UpdateFriendsPage()
    local page = pages[1]
    if not page then return end

    if friendSubTab == "friends" then
        page.friendsArea:Show()
        page.friendSearchBar:Show()
        page.ignoreArea:Hide()
        page.friendsSubBtn.text:SetTextColor(T.tabActiveText[1], T.tabActiveText[2], T.tabActiveText[3])
        page.ignoreSubBtn.text:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
        page.subIndicator:ClearAllPoints()
        page.subIndicator:SetPoint("BOTTOMLEFT", page.friendsSubBtn, "BOTTOMLEFT", 4, 0)
        page.subIndicator:SetPoint("BOTTOMRIGHT", page.friendsSubBtn, "BOTTOMRIGHT", -4, 0)
        page.subIndicator:Show()
        page.addBtn.text:SetText("添加好友")
        page.removeBtn.text:SetText("删除好友")
        self:UpdateFriendsList()
    else
        page.friendsArea:Hide()
        page.friendSearchBar:Hide()
        page.ignoreArea:Show()
        page.ignoreSubBtn.text:SetTextColor(T.tabActiveText[1], T.tabActiveText[2], T.tabActiveText[3])
        page.friendsSubBtn.text:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
        page.subIndicator:ClearAllPoints()
        page.subIndicator:SetPoint("BOTTOMLEFT", page.ignoreSubBtn, "BOTTOMLEFT", 4, 0)
        page.subIndicator:SetPoint("BOTTOMRIGHT", page.ignoreSubBtn, "BOTTOMRIGHT", -4, 0)
        page.subIndicator:Show()
        page.addBtn.text:SetText("添加屏蔽")
        page.removeBtn.text:SetText("取消屏蔽")
        self:UpdateIgnoreList()
    end
end

function SUI:UpdateFriendsList()
    local numFriends = GetNumFriends()
    local totalH = 0
    local rowIdx = 0
    local searchLower = string.lower(friendSearchText or "")
    local hasSearch = searchLower ~= ""

    for i = 1, numFriends do
        local name, level, class, area, connected, status = GetFriendInfo(i)
        if name then
            local match = true
            if hasSearch then
                match = string.find(string.lower(name), searchLower, 1, true)
                if not match and class then
                    match = string.find(string.lower(class), searchLower, 1, true)
                end
                if not match and area then
                    match = string.find(string.lower(area), searchLower, 1, true)
                end
            end

            if match then
                rowIdx = rowIdx + 1
                if rowIdx > 50 then break end
                local row = friendRows[rowIdx]
                if row then
                    row.nameFS:SetText(name)
                    row.friendDataIndex = i
                    local classEN = GetClassEN(class)

                    if connected then
                        local cc = classEN and GetClassColor(classEN) or T.onlineText
                        row.nameFS:SetTextColor(cc[1], cc[2], cc[3])
                        local info = ""
                        if level and level > 0 then info = "等级" .. level end
                        if class then info = info .. " " .. class end
                        if area then info = info .. " - " .. area end
                        row.infoFS:SetText(info)
                        row.infoFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
                    else
                        row.nameFS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
                        row.infoFS:SetText("离线")
                        row.infoFS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
                    end

                    SetClassIcon(row.classIcon, classEN)

                    if selectedFriend == i then
                        SetPixelBackdrop(row, T.tabActiveBg, T.tabActiveBorder)
                        ShowSelHighlight(row)
                        row.nameFS:SetTextColor(1, 1, 1)
                    else
                        SetRowNormal(row)
                        HideSelHighlight(row)
                    end

                    row:Show()
                    totalH = totalH + ROW_H
                end
            end
        end
    end

    for i = rowIdx + 1, 50 do
        if friendRows[i] then friendRows[i]:Hide() end
    end

    if pages[1] and pages[1].fScroll then
        pages[1].fScroll:SetContentHeight(totalH + 4)
    end
end

function SUI:UpdateIgnoreList()
    local numIgnores = GetNumIgnores()
    local totalH = 0
    for i = 1, 50 do
        local row = ignoreRows[i]
        if not row then break end
        if i <= numIgnores then
            local name = GetIgnoreName(i)
            if name then
                row.nameFS:SetText(name)
                row:Show()
                totalH = totalH + 20
            else
                row:Hide()
            end
        else
            row:Hide()
        end
    end
    if pages[1] and pages[1].iScroll then
        pages[1].iScroll:SetContentHeight(totalH + 4)
    end
end

function SUI:ShowFriendRowMenu(row)
    local di = row.friendDataIndex
    if not di then return end
    local name, level, class, area, connected, status = GetFriendInfo(di)
    if not name then return end

    if not SUI.friendDropDown then
        SUI.friendDropDown = CreateFrame("Frame", "SFramesSocialFriendDD", UIParent, "UIDropDownMenuTemplate")
        SUI.friendDropDown.displayMode = "MENU"
        SUI.friendDropDown.initialize = function()
            local fd = SUI.friendDropDown
            local fName = fd.friendName
            local fOnline = fd.friendOnline
            if not fName then return end

            local info = {}
            info.text = fName
            info.isTitle = 1
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)

            if fOnline then
                info = {}
                info.text = "密语"
                info.notCheckable = 1
                info.func = function() ChatFrame_SendTell(fName) end
                UIDropDownMenu_AddButton(info)

                info = {}
                info.text = "邀请组队"
                info.notCheckable = 1
                info.func = function() InviteByName(fName) end
                UIDropDownMenu_AddButton(info)

                info = {}
                info.text = "观察"
                info.notCheckable = 1
                info.func = function()
                    if TargetByName then TargetByName(fName) end
                    if UnitExists("target") and UnitName("target") == fName then
                        InspectUnit("target")
                    end
                end
                UIDropDownMenu_AddButton(info)
            end

            info = {}
            info.text = "删除好友"
            info.notCheckable = 1
            info.func = function()
                local idx = fd.friendIdx
                if idx then RemoveFriend(idx) end
            end
            UIDropDownMenu_AddButton(info)

            info = {}
            info.text = "取消"
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)
        end
    end

    SUI.friendDropDown.friendName = name
    SUI.friendDropDown.friendOnline = connected
    SUI.friendDropDown.friendIdx = di
    ToggleDropDownMenu(1, nil, SUI.friendDropDown, "cursor")
end

--------------------------------------------------------------------------------
-- Tab 2: Who
--------------------------------------------------------------------------------
local function BuildWhoPage(page)
    local searchBar = CreateFrame("Frame", nil, page)
    searchBar:SetHeight(26)
    searchBar:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    searchBar:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, 0)

    local editBox = MakeEditBox(searchBar, CONTENT_W - 110, 22)
    editBox:SetPoint("LEFT", searchBar, "LEFT", 0, 0)

    local placeholder = editBox:CreateFontString(nil, "ARTWORK")
    placeholder:SetFont(GetFont(), 10, "OUTLINE")
    placeholder:SetPoint("LEFT", editBox, "LEFT", 6, 0)
    placeholder:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3], 0.6)
    placeholder:SetText("名称/等级/职业/种族/区域")
    editBox.placeholder = placeholder

    editBox:SetScript("OnTextChanged", function()
        local text = this:GetText()
        if text and text ~= "" then
            this.placeholder:Hide()
        else
            this.placeholder:Show()
        end
    end)
    editBox:SetScript("OnEditFocusGained", function()
        if this:GetText() == "" then this.placeholder:Show() end
    end)
    editBox:SetScript("OnEditFocusLost", function()
        if this:GetText() == "" then this.placeholder:Show() end
    end)

    editBox:SetScript("OnEnterPressed", function()
        local text = this:GetText() or ""
        SUI:ClearWhoList()
        DoSendWho(text)
        this:ClearFocus()
    end)
    page.editBox = editBox

    local clearBtn = MakeButton(searchBar, "X", 28, 22)
    clearBtn:SetPoint("LEFT", editBox, "RIGHT", 2, 0)
    clearBtn:SetScript("OnClick", function()
        if page.editBox then
            page.editBox:SetText("")
            page.editBox:SetFocus()
        end
    end)

    local searchBtn = MakeButton(searchBar, "搜索", 64, 22)
    searchBtn:SetPoint("LEFT", clearBtn, "RIGHT", 2, 0)
    searchBtn:SetScript("OnClick", function()
        local text = page.editBox:GetText() or ""
        SUI:ClearWhoList()
        DoSendWho(text)
    end)

    -- Column headers
    local colFrame = CreateFrame("Frame", nil, page)
    colFrame:SetHeight(18)
    colFrame:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -28)
    colFrame:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, -28)

    local cols = { { "名称", 0.30 }, { "等级", 0.12 }, { "职业", 0.18 }, { "区域", 0.40 } }
    local cx = 0
    for _, col in ipairs(cols) do
        local fs = MakeFS(colFrame, 10, "LEFT", T.colHeader)
        fs:SetPoint("TOPLEFT", colFrame, "TOPLEFT", cx, 0)
        fs:SetWidth(CONTENT_W * col[2])
        fs:SetText(col[1])
        cx = cx + CONTENT_W * col[2]
    end
    MakeSep(page, -46)

    -- Results scroll (leave 16px above btnBar for totalFS)
    local listArea = CreateFrame("Frame", nil, page)
    listArea:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -48)
    listArea:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, BOTTOM_H + 18)

    local scrollH = FRAME_H - HEADER_H - TAB_BAR_H - 48 - BOTTOM_H - 18
    local wScroll = CreateScrollArea(listArea, CONTENT_W, scrollH)
    wScroll:SetPoint("TOPLEFT", listArea, "TOPLEFT", 0, 0)
    page.wScroll = wScroll

    local MAX_WHO_ROWS = 50
    for idx = 1, MAX_WHO_ROWS do
        local rowIdx = idx
        local row = CreateFrame("Button", NextName("WR"), wScroll.child)
        row:SetWidth(CONTENT_W - 4)
        row:SetHeight(20)
        row:SetPoint("TOPLEFT", wScroll.child, "TOPLEFT", 2, -((rowIdx - 1) * 20))
        row.rowIndex = rowIdx
        SetRowNormal(row)

        local nameFS = MakeFS(row, 10, "LEFT", T.nameText)
        nameFS:SetPoint("LEFT", row, "LEFT", 4, 0)
        nameFS:SetWidth(CONTENT_W * 0.30 - 8)
        row.nameFS = nameFS

        local lvlFS = MakeFS(row, 10, "LEFT", T.bodyText)
        lvlFS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.30, 0)
        lvlFS:SetWidth(CONTENT_W * 0.12)
        row.lvlFS = lvlFS

        local classFS = MakeFS(row, 10, "LEFT", T.bodyText)
        classFS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.42, 0)
        classFS:SetWidth(CONTENT_W * 0.18)
        row.classFS = classFS

        local zoneFS = MakeFS(row, 10, "LEFT", T.dimText)
        zoneFS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.60, 0)
        zoneFS:SetPoint("RIGHT", row, "RIGHT", -4, 0)
        row.zoneFS = zoneFS

        AddSelHighlight(row)
        row:SetScript("OnEnter", function()
            SetPixelBackdrop(this, T.slotHover, T.slotSelected)
        end)
        row:SetScript("OnLeave", function()
            if selectedWho == this.rowIndex then
                SetPixelBackdrop(this, T.tabActiveBg, T.tabActiveBorder)
            else
                SetRowNormal(this)
            end
        end)
        row:SetScript("OnClick", function()
            selectedWho = this.rowIndex
            SUI:UpdateWhoList()
        end)
        row:Hide()
        whoRows[idx] = row
    end

    -- Totals
    local totalFS = MakeFS(page, 10, "LEFT", T.dimText)
    totalFS:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, BOTTOM_H + 2)
    page.totalFS = totalFS

    -- Bottom buttons
    local btnBar = CreateFrame("Frame", nil, page)
    btnBar:SetHeight(BOTTOM_H)
    btnBar:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, 0)
    btnBar:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)

    local invBtn = MakeButton(btnBar, "组队邀请", 80, 22)
    invBtn:SetPoint("BOTTOMLEFT", btnBar, "BOTTOMLEFT", 0, 2)
    invBtn:SetScript("OnClick", function()
        if selectedWho then
            local name = GetWhoInfo(selectedWho)
            if name then InviteByName(name) end
        end
    end)

    local addFriendBtn = MakeButton(btnBar, "添加好友", 80, 22)
    addFriendBtn:SetPoint("LEFT", invBtn, "RIGHT", 4, 0)
    addFriendBtn:SetScript("OnClick", function()
        if selectedWho then
            local name = GetWhoInfo(selectedWho)
            if name then AddFriend(name) end
        end
    end)

    local whisperBtn = MakeButton(btnBar, "密语", 80, 22)
    whisperBtn:SetPoint("LEFT", addFriendBtn, "RIGHT", 4, 0)
    whisperBtn:SetScript("OnClick", function()
        if selectedWho then
            local name = GetWhoInfo(selectedWho)
            if name then ChatFrame_SendTell(name) end
        end
    end)
end

function SUI:ClearWhoList()
    selectedWho = nil
    for i = 1, 50 do
        local row = whoRows[i]
        if not row then break end
        row.nameFS:SetText("")
        row.lvlFS:SetText("")
        row.classFS:SetText("")
        row.zoneFS:SetText("")
        SetRowNormal(row)
        HideSelHighlight(row)
        row:Hide()
    end
    if pages[2] and pages[2].wScroll then
        pages[2].wScroll:SetContentHeight(4)
    end
    if pages[2] and pages[2].totalFS then
        pages[2].totalFS:SetText("搜索中...")
    end
end

function SUI:UpdateWhoList()
    local numWho, totalCount = GetNumWhoResults()
    local totalH = 0
    for i = 1, 50 do
        local row = whoRows[i]
        if not row then break end
        if i <= numWho then
            local name, guild, level, race, class, zone, classEN = GetWhoInfo(i)
            if name then
                if not classEN or classEN == "" then
                    classEN = GetClassEN(class)
                end
                local cc = GetClassColor(classEN)
                row.nameFS:SetText(name)
                row.nameFS:SetTextColor(cc[1], cc[2], cc[3])
                row.lvlFS:SetText(level or "")
                row.classFS:SetText(class or "")
                row.classFS:SetTextColor(cc[1], cc[2], cc[3])
                row.zoneFS:SetText(zone or "")

                if selectedWho == i then
                    SetPixelBackdrop(row, T.tabActiveBg, T.tabActiveBorder)
                    ShowSelHighlight(row)
                    row.nameFS:SetTextColor(1, 1, 1)
                else
                    SetRowNormal(row)
                    HideSelHighlight(row)
                end
                row:Show()
                totalH = totalH + 20
            else
                row:Hide()
            end
        else
            row:Hide()
        end
    end
    if pages[2] and pages[2].wScroll then
        pages[2].wScroll:SetContentHeight(totalH + 4)
    end
    if pages[2] and pages[2].totalFS then
        pages[2].totalFS:SetText((numWho or 0) .. " 个结果 (共 " .. (totalCount or 0) .. " 人)")
    end
end

--------------------------------------------------------------------------------
-- Tab 3: Guild
--------------------------------------------------------------------------------
local motdPopup = nil
local function ShowMotdPopup(motdStr)
    if not motdPopup then
        local f = CreateFrame("Frame", "SFramesSocialMotdPopup", UIParent)
        f:SetWidth(360)
        f:SetHeight(220)
        f:SetPoint("CENTER", UIParent, "CENTER", 0, 60)
        f:SetFrameStrata("DIALOG")
        f:SetFrameLevel(120)
        SetRoundBackdrop(f, T.panelBg, T.panelBorder)
        CreateShadow(f)
        f:EnableMouse(true)
        f:SetMovable(true)
        f:RegisterForDrag("LeftButton")
        f:SetScript("OnDragStart", function() this:StartMoving() end)
        f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)

        local titleFS = MakeFS(f, 12, "CENTER", T.gold)
        titleFS:SetPoint("TOP", f, "TOP", 0, -10)
        titleFS:SetText("公会公告")
        f.titleFS = titleFS

        MakeSep(f, -26)

        local scrollFrame = CreateFrame("ScrollFrame", NextName("MotdScroll"), f)
        scrollFrame:SetPoint("TOPLEFT", f, "TOPLEFT", 12, -30)
        scrollFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12, 40)

        local child = CreateFrame("Frame", nil, scrollFrame)
        child:SetWidth(336 - 24)
        child:SetHeight(1)
        scrollFrame:SetScrollChild(child)

        local bodyFS = child:CreateFontString(nil, "OVERLAY")
        bodyFS:SetFont(GetFont(), 11, "OUTLINE")
        bodyFS:SetJustifyH("LEFT")
        bodyFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
        bodyFS:SetPoint("TOPLEFT", child, "TOPLEFT", 0, 0)
        bodyFS:SetWidth(336 - 24)
        f.bodyFS = bodyFS

        local scrollOffset = 0
        local scrollMax = 0

        f.UpdateScroll = function()
            local textH = bodyFS:GetHeight() or 0
            local viewH = scrollFrame:GetHeight() or 1
            scrollMax = math.max(0, textH - viewH)
            if scrollOffset > scrollMax then scrollOffset = scrollMax end
            scrollFrame:SetVerticalScroll(scrollOffset)
            child:SetHeight(math.max(textH, viewH))
        end

        scrollFrame:EnableMouseWheel(true)
        scrollFrame:SetScript("OnMouseWheel", function()
            scrollOffset = scrollOffset - (arg1 or 0) * 18
            if scrollOffset < 0 then scrollOffset = 0 end
            if scrollOffset > scrollMax then scrollOffset = scrollMax end
            scrollFrame:SetVerticalScroll(scrollOffset)
        end)

        local closeBtn = MakeButton(f, "关闭", 100, 24)
        closeBtn:SetPoint("BOTTOM", f, "BOTTOM", 0, 10)
        closeBtn:SetScript("OnClick", function() f:Hide() end)

        f:Hide()
        motdPopup = f
    end

    motdPopup.bodyFS:SetText(motdStr or "无公告")
    motdPopup:Show()
    motdPopup.UpdateScroll()
end

local function BuildGuildPage(page)
    local motdFrame = CreateFrame("Button", NextName("MotdBtn"), page)
    motdFrame:SetHeight(36)
    motdFrame:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    motdFrame:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, 0)
    SetPixelBackdrop(motdFrame, { 0.08, 0.04, 0.06, 0.8 }, T.slotBorder)

    local motdLabel = MakeFS(motdFrame, 9, "LEFT", T.dimText)
    motdLabel:SetPoint("TOPLEFT", motdFrame, "TOPLEFT", 6, -2)
    motdLabel:SetText("公会公告: (点击查看完整)")

    local motdText = MakeFS(motdFrame, 10, "LEFT", T.bodyText)
    motdText:SetPoint("TOPLEFT", motdLabel, "BOTTOMLEFT", 0, -2)
    motdText:SetPoint("RIGHT", motdFrame, "RIGHT", -6, 0)
    page.motdText = motdText

    motdFrame:SetScript("OnClick", function()
        local motd = GetGuildRosterMOTD and GetGuildRosterMOTD() or ""
        ShowMotdPopup(motd ~= "" and motd or "无公告")
    end)
    motdFrame:SetScript("OnEnter", function()
        SetPixelBackdrop(this, { 0.12, 0.06, 0.09, 0.9 }, T.tabActiveBorder)
    end)
    motdFrame:SetScript("OnLeave", function()
        SetPixelBackdrop(this, { 0.08, 0.04, 0.06, 0.8 }, T.slotBorder)
    end)

    -- Toolbar row: search + filter buttons
    local toolBar = CreateFrame("Frame", nil, page)
    toolBar:SetHeight(18)
    toolBar:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -38)
    toolBar:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, -38)

    local gSearchLabel = MakeFS(toolBar, 9, "LEFT", T.dimText)
    gSearchLabel:SetPoint("LEFT", toolBar, "LEFT", 0, 0)
    gSearchLabel:SetText("搜索:")

    local gSearchBox = MakeEditBox(toolBar, 110, 16)
    gSearchBox:SetPoint("LEFT", gSearchLabel, "RIGHT", 2, 0)
    gSearchBox:SetFont(GetFont(), 9, "OUTLINE")
    gSearchBox:SetScript("OnTextChanged", function()
        guildSearchText = this:GetText() or ""
        SUI:UpdateGuildList()
    end)
    gSearchBox:SetScript("OnEnterPressed", function() this:ClearFocus() end)
    gSearchBox:SetScript("OnEscapePressed", function()
        this:SetText("")
        this:ClearFocus()
    end)
    page.gSearchBox = gSearchBox

    local offlineToggle = MakeButton(toolBar, guildHideOffline and "显示离线" or "隐藏离线", 56, 16)
    offlineToggle:SetPoint("TOPRIGHT", toolBar, "TOPRIGHT", 0, 0)
    offlineToggle.text:SetFont(GetFont(), 9, "OUTLINE")
    offlineToggle:SetScript("OnClick", function()
        guildHideOffline = not guildHideOffline
        SFramesDB.guildHideOffline = guildHideOffline
        if guildHideOffline then
            this.text:SetText("显示离线")
        else
            this.text:SetText("隐藏离线")
        end
        SUI:UpdateGuildList()
    end)
    page.offlineToggle = offlineToggle

    local viewToggle = MakeButton(toolBar, "切换视图", 56, 16)
    viewToggle:SetPoint("RIGHT", offlineToggle, "LEFT", -4, 0)
    viewToggle.text:SetFont(GetFont(), 9, "OUTLINE")
    viewToggle:SetScript("OnClick", function()
        if guildViewMode == "player" then guildViewMode = "guild" else guildViewMode = "player" end
        SUI:UpdateGuildList()
    end)

    -- Column headers
    local colFrame = CreateFrame("Frame", nil, page)
    colFrame:SetHeight(18)
    colFrame:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -56)
    colFrame:SetPoint("TOPRIGHT", page, "TOPRIGHT", 0, -56)
    page.guildColFrame = colFrame
    page.guildColBtns = {}

    MakeSep(page, -74)

    local listArea = CreateFrame("Frame", nil, page)
    listArea:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -76)
    listArea:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, BOTTOM_H + 14)

    local gScroll = CreateScrollArea(listArea, CONTENT_W, FRAME_H - HEADER_H - TAB_BAR_H - 76 - BOTTOM_H - 14 - 16)
    gScroll:SetPoint("TOPLEFT", listArea, "TOPLEFT", 0, 0)
    page.gScroll = gScroll

    local MAX_GUILD_ROWS = 100
    for idx = 1, MAX_GUILD_ROWS do
        local rowIdx = idx
        local row = CreateFrame("Button", NextName("GR"), gScroll.child)
        row:SetWidth(CONTENT_W - 4)
        row:SetHeight(18)
        row:SetPoint("TOPLEFT", gScroll.child, "TOPLEFT", 2, -((rowIdx - 1) * 18))
        row.rowIndex = rowIdx
        SetRowNormal(row)

        local classIcon = CreateClassIcon(row, 14)
        classIcon:SetPoint("LEFT", row, "LEFT", 2, 0)
        row.classIcon = classIcon

        local nameFS = MakeFS(row, 10, "LEFT", T.nameText)
        nameFS:SetPoint("LEFT", classIcon, "RIGHT", 3, 0)
        nameFS:SetWidth(CONTENT_W * 0.24 - 20)
        row.nameFS = nameFS

        local lvlFS = MakeFS(row, 10, "CENTER", T.bodyText)
        lvlFS:SetWidth(30)
        row.lvlFS = lvlFS

        local col3FS = MakeFS(row, 10, "LEFT", T.dimText)
        col3FS:SetWidth(100)
        row.col3FS = col3FS

        local col4FS = MakeFS(row, 9, "LEFT", T.dimText)
        row.col4FS = col4FS

        local col5FS = MakeFS(row, 9, "LEFT", T.dimText)
        row.col5FS = col5FS

        AddSelHighlight(row)
        row:RegisterForClicks("LeftButtonUp", "RightButtonUp")

        row:SetScript("OnEnter", function()
            SetPixelBackdrop(this, T.slotHover, T.slotSelected)
        end)
        row:SetScript("OnLeave", function()
            if selectedGuild and selectedGuild == this.guildDataIndex then
                SetPixelBackdrop(this, T.tabActiveBg, T.tabActiveBorder)
            else
                SetRowNormal(this)
            end
        end)
        row:SetScript("OnClick", function()
            selectedGuild = this.guildDataIndex
            if arg1 == "RightButton" and this.guildDataIndex then
                SUI:ShowGuildRowMenu(this)
            else
                SUI:UpdateGuildList()
            end
        end)
        row:Hide()
        guildRows[idx] = row
    end

    local totalFS = MakeFS(page, 9, "LEFT", T.dimText)
    totalFS:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 2, BOTTOM_H + 2)
    page.guildTotalFS = totalFS

    local btnBar = CreateFrame("Frame", nil, page)
    btnBar:SetHeight(BOTTOM_H)
    btnBar:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, 0)
    btnBar:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)

    local invBtn = MakeButton(btnBar, "组队邀请", 80, 22)
    invBtn:SetPoint("BOTTOMLEFT", btnBar, "BOTTOMLEFT", 0, 2)
    invBtn:SetScript("OnClick", function()
        if selectedGuild then
            local name, rank, rankIndex, level, class, zone, note, officerNote, online = GetGuildRosterInfo(selectedGuild)
            if name and online then InviteByName(name) end
        end
    end)

    local msgBtn = MakeButton(btnBar, "发送信息", 80, 22)
    msgBtn:SetPoint("LEFT", invBtn, "RIGHT", 4, 0)
    msgBtn:SetScript("OnClick", function()
        if selectedGuild then
            local name = GetGuildRosterInfo(selectedGuild)
            if name then ChatFrame_SendTell(name) end
        end
    end)

    local gInviteBtn = MakeButton(btnBar, "邀请入会", 80, 22)
    gInviteBtn:SetPoint("LEFT", msgBtn, "RIGHT", 4, 0)
    gInviteBtn:SetScript("OnClick", function()
        ShowCustomPopup("邀请玩家加入公会", true, function(name)
            if name and name ~= "" then
                if GuildInvite then
                    GuildInvite(name)
                elseif GuildInviteByName then
                    GuildInviteByName(name)
                else
                    SendChatMessage(".ginvite " .. name, "GUILD")
                end
            end
        end)
    end)
    page.gInviteBtn = gInviteBtn

    local gLeaveBtn = MakeButton(btnBar, "退出公会", 80, 22)
    gLeaveBtn:SetPoint("LEFT", gInviteBtn, "RIGHT", 4, 0)
    gLeaveBtn:SetScript("OnClick", function()
        StaticPopup_Show("CONFIRM_GUILD_LEAVE", GetGuildInfo("player"))
    end)
    page.gLeaveBtn = gLeaveBtn
end

function SUI:ShowGuildRowMenu(row)
    local di = row.guildDataIndex
    if not di then return end
    local name, rank, rankIndex, level, class, zone, note, officerNote, online = GetGuildRosterInfo(di)
    if not name then return end
    local isSelf = (name == UnitName("player"))

    if not SUI.guildDropDown then
        SUI.guildDropDown = CreateFrame("Frame", "SFramesSocialGuildDD", UIParent, "UIDropDownMenuTemplate")
        SUI.guildDropDown.displayMode = "MENU"
        SUI.guildDropDown.initialize = function()
            local gd = SUI.guildDropDown
            local gName = gd.memberName
            local gOnline = gd.memberOnline
            local gSelf = gd.memberIsSelf
            if not gName then return end

            local info = {}
            info.text = gName
            info.isTitle = 1
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)

            if not gSelf then
                if gOnline then
                    info = {}
                    info.text = "密语"
                    info.notCheckable = 1
                    info.func = function() ChatFrame_SendTell(gName) end
                    UIDropDownMenu_AddButton(info)

                    info = {}
                    info.text = "邀请组队"
                    info.notCheckable = 1
                    info.func = function() InviteByName(gName) end
                    UIDropDownMenu_AddButton(info)

                    info = {}
                    info.text = "观察"
                    info.notCheckable = 1
                    info.func = function()
                        if TargetByName then TargetByName(gName) end
                        if UnitExists("target") and UnitName("target") == gName then
                            InspectUnit("target")
                        end
                    end
                    UIDropDownMenu_AddButton(info)
                end

                if CanGuildRemove and CanGuildRemove() then
                    info = {}
                    info.text = "移出公会"
                    info.notCheckable = 1
                    info.func = function() GuildUninvite(gName) end
                    UIDropDownMenu_AddButton(info)
                end

                if CanGuildPromote and CanGuildPromote() then
                    info = {}
                    info.text = "晋升"
                    info.notCheckable = 1
                    info.func = function() GuildPromote(gName) end
                    UIDropDownMenu_AddButton(info)
                end

                if CanGuildDemote and CanGuildDemote() then
                    info = {}
                    info.text = "降级"
                    info.notCheckable = 1
                    info.func = function() GuildDemote(gName) end
                    UIDropDownMenu_AddButton(info)
                end
            else
                info = {}
                info.text = "退出公会"
                info.notCheckable = 1
                info.func = function()
                    StaticPopup_Show("CONFIRM_GUILD_LEAVE", GetGuildInfo("player"))
                end
                UIDropDownMenu_AddButton(info)
            end

            info = {}
            info.text = "取消"
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)
        end
    end

    SUI.guildDropDown.memberName = name
    SUI.guildDropDown.memberOnline = online
    SUI.guildDropDown.memberIsSelf = isSelf
    ToggleDropDownMenu(1, nil, SUI.guildDropDown, "cursor")
end

local GUILD_COL_SORT_MAP = {
    ["名称"] = "name",
    ["等级"] = "level",
    ["职业"] = "class",
    ["职位"] = "rank",
    ["区域"] = "zone",
    ["备注"] = "note",
    ["状态"] = "status",
}

local function RebuildGuildColHeaders()
    local colFrame = pages[3] and pages[3].guildColFrame
    if not colFrame then return end
    local old = pages[3].guildColBtns
    if old then
        for _, btn in ipairs(old) do btn:Hide() end
    end
    pages[3].guildColBtns = {}
    local headers
    if guildViewMode == "player" then
        headers = { { "名称", 0, 0.24 }, { "等级", 0.24, 0.08 }, { "职业", 0.32, 0.14 }, { "职位", 0.46, 0.18 }, { "区域", 0.64, 0.36 } }
    else
        headers = { { "名称", 0, 0.30 }, { "职位", 0.30, 0.20 }, { "备注", 0.50, 0.30 }, { "状态", 0.80, 0.20 } }
    end
    for _, h in ipairs(headers) do
        local sortKey = GUILD_COL_SORT_MAP[h[1]]
        local btn = CreateFrame("Button", nil, colFrame)
        btn:SetHeight(18)
        btn:SetPoint("TOPLEFT", colFrame, "TOPLEFT", CONTENT_W * h[2], 0)
        btn:SetWidth(CONTENT_W * h[3])
        local fs = MakeFS(btn, 10, "LEFT", T.colHeader)
        fs:SetPoint("LEFT", btn, "LEFT", 0, 0)
        local arrow = ""
        if sortKey and guildSortField == sortKey then
            arrow = guildSortAsc and " ▲" or " ▼"
        end
        fs:SetText(h[1] .. arrow)
        btn.headerFS = fs
        if sortKey then
            btn.sortKey = sortKey
            btn:SetScript("OnClick", function()
                if guildSortField == this.sortKey then
                    guildSortAsc = not guildSortAsc
                else
                    guildSortField = this.sortKey
                    guildSortAsc = true
                end
                SUI:UpdateGuildList()
            end)
            btn:SetScript("OnEnter", function()
                this.headerFS:SetTextColor(1, 1, 1)
            end)
            btn:SetScript("OnLeave", function()
                this.headerFS:SetTextColor(T.colHeader[1], T.colHeader[2], T.colHeader[3])
            end)
        end
        table.insert(pages[3].guildColBtns, btn)
    end
end

function SUI:UpdateGuildList()
    if not IsInGuild() then
        for i = 1, 100 do
            if guildRows[i] then guildRows[i]:Hide() end
        end
        if pages[3] and pages[3].motdText then
            pages[3].motdText:SetText("你没有加入公会")
        end
        if pages[3] and pages[3].gInviteBtn then pages[3].gInviteBtn:Hide() end
        if pages[3] and pages[3].gLeaveBtn then pages[3].gLeaveBtn:Hide() end
        if pages[3] and pages[3].guildTotalFS then pages[3].guildTotalFS:SetText("") end
        return
    end

    if SetGuildRosterShowOffline then
        SetGuildRosterShowOffline(not guildHideOffline)
    end
    GuildRoster()
    local motd = GetGuildRosterMOTD and GetGuildRosterMOTD() or ""
    if pages[3] and pages[3].motdText then
        pages[3].motdText:SetText(motd ~= "" and motd or "无公告")
    end

    RebuildGuildColHeaders()

    local numTotal, numOnlineRet = GetNumGuildMembers()
    numTotal = numTotal or 0
    local totalOnline = 0
    local members = {}

    local searchLower = string.lower(guildSearchText or "")
    local hasSearch = searchLower ~= ""

    for i = 1, numTotal do
        local name, rank, rankIndex, level, class, zone, note, officerNote, online, status, classEN = GetGuildRosterInfo(i)
        if name then
            if online then totalOnline = totalOnline + 1 end
            local match = true
            if hasSearch then
                match = string.find(string.lower(name), searchLower, 1, true)
                if not match and class then
                    match = string.find(string.lower(class), searchLower, 1, true)
                end
                if not match and rank then
                    match = string.find(string.lower(rank), searchLower, 1, true)
                end
                if not match and zone then
                    match = string.find(string.lower(zone), searchLower, 1, true)
                end
            end
            if match then
                if not classEN or classEN == "" then
                    classEN = GetClassEN(class)
                end
                table.insert(members, {
                    idx = i, name = name, rank = rank, rankIndex = rankIndex or 99,
                    level = level or 0, class = class or "", classEN = classEN,
                    zone = zone or "", note = note or "", officerNote = officerNote or "",
                    online = online, status = status,
                })
            end
        end
    end

    local sf = guildSortField
    local asc = guildSortAsc
    table.sort(members, function(a, b)
        local va, vb
        if sf == "name" then
            va, vb = (a.name or ""), (b.name or "")
        elseif sf == "level" then
            va, vb = (a.level or 0), (b.level or 0)
        elseif sf == "class" then
            va, vb = (a.class or ""), (b.class or "")
        elseif sf == "rank" then
            va, vb = (a.rankIndex or 99), (b.rankIndex or 99)
        elseif sf == "zone" then
            va, vb = (a.zone or ""), (b.zone or "")
        elseif sf == "note" then
            va, vb = (a.note or ""), (b.note or "")
        elseif sf == "status" then
            local sa = a.online and 0 or 1
            local sb = b.online and 0 or 1
            va, vb = sa, sb
        else
            va, vb = (a.name or ""), (b.name or "")
        end
        if va == vb then
            return (a.name or "") < (b.name or "")
        end
        if asc then return va < vb else return va > vb end
    end)

    local totalH = 0
    local rowIdx = 0

    for mi = 1, table.getn(members) do
        local m = members[mi]
        rowIdx = rowIdx + 1
        if rowIdx > 100 then break end
        local row = guildRows[rowIdx]
        if row then
            local cc = GetClassColor(m.classEN)
            SetClassIcon(row.classIcon, m.classEN)
            row.nameFS:SetText(m.name)
            row.guildDataIndex = m.idx

            if m.online then
                row.nameFS:SetTextColor(cc[1], cc[2], cc[3])
            else
                row.nameFS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
            end

            if guildViewMode == "player" then
                row.lvlFS:SetText(m.level > 0 and m.level or "")
                row.lvlFS:ClearAllPoints()
                row.lvlFS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.24, 0)

                row.col3FS:SetText(m.class)
                row.col3FS:ClearAllPoints()
                row.col3FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.32, 0)
                row.col3FS:SetWidth(CONTENT_W * 0.14)
                row.col3FS:SetTextColor(cc[1], cc[2], cc[3])

                row.col4FS:SetText(m.rank or "")
                row.col4FS:ClearAllPoints()
                row.col4FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.46, 0)
                row.col4FS:SetWidth(CONTENT_W * 0.18)
                row.col4FS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])

                row.col5FS:SetText(m.online and m.zone or "离线")
                row.col5FS:ClearAllPoints()
                row.col5FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.64, 0)
                row.col5FS:SetPoint("RIGHT", row, "RIGHT", -4, 0)
                if m.online then
                    row.col5FS:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
                else
                    row.col5FS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
                end
                row.col5FS:Show()
            else
                row.lvlFS:SetText("")
                row.col3FS:SetText(m.rank or "")
                row.col3FS:ClearAllPoints()
                row.col3FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.30, 0)
                row.col3FS:SetWidth(CONTENT_W * 0.20)
                row.col3FS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])

                row.col4FS:SetText(m.note)
                row.col4FS:ClearAllPoints()
                row.col4FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.50, 0)
                row.col4FS:SetWidth(CONTENT_W * 0.30)
                row.col4FS:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])

                row.col5FS:SetText(m.online and "在线" or "离线")
                row.col5FS:ClearAllPoints()
                row.col5FS:SetPoint("LEFT", row, "LEFT", CONTENT_W * 0.80, 0)
                row.col5FS:SetPoint("RIGHT", row, "RIGHT", -4, 0)
                if m.online then
                    row.col5FS:SetTextColor(T.onlineText[1], T.onlineText[2], T.onlineText[3])
                else
                    row.col5FS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
                end
                row.col5FS:Show()
            end

            if selectedGuild == m.idx then
                SetPixelBackdrop(row, T.tabActiveBg, T.tabActiveBorder)
                ShowSelHighlight(row)
                row.nameFS:SetTextColor(1, 1, 1)
            else
                SetRowNormal(row)
                HideSelHighlight(row)
            end
            row:Show()
            totalH = totalH + 18
        end
    end

    for i = rowIdx + 1, 100 do
        if guildRows[i] then
            guildRows[i]:Hide()
        end
    end

    if pages[3] and pages[3].gScroll then
        pages[3].gScroll:SetContentHeight(totalH + 4)
    end
    if pages[3] and pages[3].guildTotalFS then
        local shownCount = table.getn(members)
        if guildHideOffline then
            pages[3].guildTotalFS:SetText(totalOnline .. " 人在线 (共 " .. numTotal .. " 名成员)")
        else
            pages[3].guildTotalFS:SetText("共 " .. numTotal .. " 名成员, " .. totalOnline .. " 人在线")
        end
    end

    -- Show/hide guild invite button based on permission
    if pages[3] and pages[3].gInviteBtn then
        local canInvite = CanGuildInvite and CanGuildInvite()
        if canInvite then
            pages[3].gInviteBtn:Show()
        else
            pages[3].gInviteBtn:Hide()
        end
    end
end

--------------------------------------------------------------------------------
-- Tab 4: Raid
--------------------------------------------------------------------------------
local RAID_COLS = 5
local RAID_GROUPS = 8
local RAID_SLOT_W = 68
local RAID_SLOT_H = 24
local RAID_GROUP_PAD = 3
local RAID_GROUP_LABEL_H = 14
local RAID_ICON_SIZE = 14

local dragSlot = nil
local dragHighlight = nil

local function BuildRaidPage(page)
    local infoFS = MakeFS(page, 10, "LEFT", T.dimText)
    infoFS:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    page.raidInfoFS = infoFS

    local rScroll = CreateScrollArea(page, CONTENT_W, FRAME_H - HEADER_H - TAB_BAR_H - 20 - BOTTOM_H - 16)
    rScroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, -18)
    page.rScroll = rScroll

    local groupH = RAID_GROUP_LABEL_H + RAID_SLOT_H
    for gIdx = 1, RAID_GROUPS do
        local g = gIdx
        local groupFrame = CreateFrame("Frame", nil, rScroll.child)
        groupFrame:SetWidth(CONTENT_W)
        groupFrame:SetHeight(groupH)
        local gy = -((g - 1) * (groupH + RAID_GROUP_PAD))
        groupFrame:SetPoint("TOPLEFT", rScroll.child, "TOPLEFT", 0, gy)
        SetRoundBackdrop(groupFrame, T.raidGroup, T.raidGroupBorder)

        groupFrame.groupIndex = g
        groupFrame:EnableMouse(true)
        groupFrame:SetScript("OnMouseUp", function()
            if not dragSlot then return end
            if dragHighlight then
                dragHighlight:Hide()
                dragHighlight:SetScript("OnUpdate", nil)
            end
            local tg = this.groupIndex
            if tg and dragSlot.raidIndex and tg ~= dragSlot.group then
                SetRaidSubgroup(dragSlot.raidIndex, tg)
            end
            dragSlot = nil
        end)

        local groupLabel = MakeFS(groupFrame, 9, "LEFT", T.gold)
        groupLabel:SetPoint("TOPLEFT", groupFrame, "TOPLEFT", 4, -1)
        groupLabel:SetText("小组 " .. g)

        for sIdx = 1, RAID_COLS do
            local s = sIdx
            local slot = CreateFrame("Button", NextName("RS"), groupFrame)
            slot:SetWidth(RAID_SLOT_W)
            slot:SetHeight(RAID_SLOT_H)
            local sx = 2 + (s - 1) * (RAID_SLOT_W + 2)
            slot:SetPoint("BOTTOMLEFT", groupFrame, "BOTTOMLEFT", sx, 1)
            SetPixelBackdrop(slot, T.raidSlotEmpty, T.slotBorder)
            slot.group = g
            slot.slotIndex = s

            local classIcon = CreateClassIcon(slot, RAID_ICON_SIZE)
            classIcon:SetPoint("LEFT", slot, "LEFT", 2, 0)
            slot.classIcon = classIcon

            local nameFS = MakeFS(slot, 8, "LEFT", T.nameText)
            nameFS:SetPoint("LEFT", classIcon, "RIGHT", 2, 0)
            nameFS:SetPoint("RIGHT", slot, "RIGHT", -2, 0)
            slot.nameFS = nameFS

            slot.infoFS = nil

            slot:RegisterForClicks("LeftButtonUp", "RightButtonUp")
            slot:RegisterForDrag("LeftButton")

            slot:SetScript("OnClick", function()
                if not this.raidIndex then return end
                if arg1 == "RightButton" then
                    SUI:ShowRaidSlotMenu(this)
                end
            end)

            slot:SetScript("OnDragStart", function()
                if not this.raidIndex then return end
                if not (IsRaidLeader() or IsRaidOfficer()) then return end
                dragSlot = this
                if not dragHighlight then
                    dragHighlight = CreateFrame("Frame", "SFramesRaidDragHL", UIParent)
                    dragHighlight:SetWidth(RAID_SLOT_W)
                    dragHighlight:SetHeight(RAID_SLOT_H)
                    dragHighlight:SetFrameStrata("TOOLTIP")
                    SetPixelBackdrop(dragHighlight, T.tabActiveBg, T.tabActiveBorder)
                    local dfs = MakeFS(dragHighlight, 8, "CENTER", T.tabActiveText)
                    dfs:SetPoint("CENTER", 0, 0)
                    dragHighlight.text = dfs
                end
                dragHighlight.text:SetText(this.playerName or "")
                local cc = GetClassColor(this.playerClass)
                dragHighlight.text:SetTextColor(cc[1], cc[2], cc[3])
                dragHighlight:Show()
                dragHighlight:SetScript("OnUpdate", function()
                    local cx, cy = GetCursorPosition()
                    local s = UIParent:GetEffectiveScale()
                    dragHighlight:ClearAllPoints()
                    dragHighlight:SetPoint("CENTER", UIParent, "BOTTOMLEFT", cx / s, cy / s)
                end)
            end)

            slot:SetScript("OnDragStop", function()
                if not dragSlot then return end
                if dragHighlight then
                    dragHighlight:Hide()
                    dragHighlight:SetScript("OnUpdate", nil)
                end
                local cx, cy = GetCursorPosition()
                local es = UIParent:GetEffectiveScale()
                cx = cx / es
                cy = cy / es
                local targetSlot = nil
                local targetGroup = nil
                for tg = 1, RAID_GROUPS do
                    for ts = 1, RAID_COLS do
                        local tSlot = raidSlots[tg] and raidSlots[tg][ts]
                        if tSlot and tSlot:IsVisible() then
                            local left = tSlot:GetLeft()
                            local right = tSlot:GetRight()
                            local top = tSlot:GetTop()
                            local bottom = tSlot:GetBottom()
                            if left and cx >= left and cx <= right and cy >= bottom and cy <= top then
                                targetSlot = tSlot
                                targetGroup = tg
                                break
                            end
                        end
                    end
                    if targetGroup then break end
                end
                if not targetGroup then
                    for tg = 1, RAID_GROUPS do
                        local gf = raidSlots[tg] and raidSlots[tg][1] and raidSlots[tg][1]:GetParent()
                        if gf and gf:IsVisible() then
                            local left = gf:GetLeft()
                            local right = gf:GetRight()
                            local top = gf:GetTop()
                            local bottom = gf:GetBottom()
                            if left and cx >= left and cx <= right and cy >= bottom and cy <= top then
                                targetGroup = tg
                                break
                            end
                        end
                    end
                end
                if targetGroup and dragSlot.raidIndex and targetGroup ~= dragSlot.group then
                    if targetSlot and targetSlot.raidIndex and SwapRaidMembers then
                        SwapRaidMembers(dragSlot.raidIndex, targetSlot.raidIndex)
                    else
                        SetRaidSubgroup(dragSlot.raidIndex, targetGroup)
                    end
                end
                dragSlot = nil
            end)

            slot:SetScript("OnEnter", function()
                if dragSlot and dragSlot ~= this then
                    SetPixelBackdrop(this, { 0.30, 0.18, 0.26, 0.9 }, T.tabActiveBorder)
                else
                    SetPixelBackdrop(this, T.slotHover, T.slotSelected)
                end
                if this.playerName and not dragSlot then
                    GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
                    GameTooltip:AddLine(this.playerName, 1, 1, 1)
                    if this.playerClass then
                        local cc = GetClassColor(this.playerClass)
                        GameTooltip:AddLine(this.playerClass, cc[1], cc[2], cc[3])
                    end
                    if this.playerLevel then
                        GameTooltip:AddLine("等级 " .. this.playerLevel, T.bodyText[1], T.bodyText[2], T.bodyText[3])
                    end
                    if IsRaidLeader() or IsRaidOfficer() then
                        GameTooltip:AddLine("左键拖拽: 移动到其他小组", T.dimText[1], T.dimText[2], T.dimText[3])
                    end
                    GameTooltip:AddLine("右键: 团队操作菜单", T.dimText[1], T.dimText[2], T.dimText[3])
                    GameTooltip:Show()
                end
            end)
            slot:SetScript("OnLeave", function()
                if this.raidIndex then
                    SetPixelBackdrop(this, T.slotBg, T.slotBorder)
                else
                    SetPixelBackdrop(this, T.raidSlotEmpty, T.slotBorder)
                end
                GameTooltip:Hide()
            end)

            if not raidSlots[g] then raidSlots[g] = {} end
            raidSlots[g][s] = slot
        end
    end

    rScroll:SetContentHeight(RAID_GROUPS * (groupH + RAID_GROUP_PAD))

    local btnBar = CreateFrame("Frame", nil, page)
    btnBar:SetHeight(BOTTOM_H)
    btnBar:SetPoint("BOTTOMLEFT", page, "BOTTOMLEFT", 0, 0)
    btnBar:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)

    local convertBtn = MakeButton(btnBar, "转换团队", 80, 22)
    convertBtn:SetPoint("BOTTOMLEFT", btnBar, "BOTTOMLEFT", 0, 2)
    convertBtn:SetScript("OnClick", function()
        if GetNumRaidMembers() == 0 and GetNumPartyMembers() > 0 then
            ConvertToRaid()
        end
    end)

    local leaveBtn = MakeButton(btnBar, "离开团队", 80, 22)
    leaveBtn:SetPoint("LEFT", convertBtn, "RIGHT", 4, 0)
    leaveBtn:SetScript("OnClick", function()
        if GetNumRaidMembers() > 0 then LeaveParty() end
    end)

    local readyBtn = MakeButton(btnBar, "就位确认", 80, 22)
    readyBtn:SetPoint("LEFT", leaveBtn, "RIGHT", 4, 0)
    readyBtn:SetScript("OnClick", function()
        if DoReadyCheck then DoReadyCheck() end
    end)
end

function SUI:GetRaidUnitByName(targetName)
    for i = 1, 40 do
        local u = "raid" .. i
        if UnitExists(u) and UnitName(u) == targetName then
            return u
        end
    end
    return nil
end

function SUI:ShowRaidSlotMenu(slot)
    if not slot.raidIndex then return end
    local pName = slot.playerName
    if not pName then return end

    if not SUI.raidDropDown then
        SUI.raidDropDown = CreateFrame("Frame", "SFramesSocialRaidDD", UIParent, "UIDropDownMenuTemplate")
        SUI.raidDropDown.displayMode = "MENU"
        SUI.raidDropDown.initialize = function()
            local rd = SUI.raidDropDown
            local name = rd.slotName
            local rIdx = rd.slotRaidIndex
            local grp = rd.slotGroup
            local rnk = rd.slotRank
            local isSelf = rd.slotIsSelf
            if not name or not rIdx then return end

            local isLeader = IsRaidLeader()
            local isOfficer = IsRaidOfficer()
            local info

            info = {}
            info.text = name
            info.isTitle = 1
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)

            if not isSelf then
                info = {}
                info.text = "密语"
                info.notCheckable = 1
                info.func = function() ChatFrame_SendTell(name) end
                UIDropDownMenu_AddButton(info)

                info = {}
                info.text = "观察"
                info.notCheckable = 1
                info.func = function()
                    local u = SUI:GetRaidUnitByName(name)
                    if u then InspectUnit(u) end
                end
                UIDropDownMenu_AddButton(info)
            end

            if isLeader or isOfficer then
                for gIdx = 1, RAID_GROUPS do
                    if gIdx ~= grp then
                        info = {}
                        info.text = "移至小组 " .. gIdx
                        info.notCheckable = 1
                        local tg = gIdx
                        info.func = function() SetRaidSubgroup(rIdx, tg) end
                        UIDropDownMenu_AddButton(info)
                    end
                end
            end

            if isLeader and not isSelf then
                if rnk and rnk < 1 then
                    info = {}
                    info.text = "提升为助理"
                    info.notCheckable = 1
                    info.func = function() PromoteToAssistant(name) end
                    UIDropDownMenu_AddButton(info)
                elseif rnk and rnk == 1 then
                    info = {}
                    info.text = "取消助理"
                    info.notCheckable = 1
                    info.func = function() DemoteAssistant(name) end
                    UIDropDownMenu_AddButton(info)
                end

                info = {}
                info.text = "转让队长"
                info.notCheckable = 1
                info.func = function()
                    StaticPopupDialogs["SFRAMES_PROMOTE_LEADER"] = {
                        text = "确定要将队长转让给 " .. name .. " 吗？",
                        button1 = "确定",
                        button2 = "取消",
                        OnAccept = function() PromoteToLeader(name) end,
                        timeout = 0,
                        whileDead = true,
                        hideOnEscape = true,
                    }
                    StaticPopup_Show("SFRAMES_PROMOTE_LEADER")
                end
                UIDropDownMenu_AddButton(info)
            end

            if (isLeader or isOfficer) and not isSelf then
                info = {}
                info.text = "移出团队"
                info.notCheckable = 1
                info.func = function() UninviteByName(name) end
                UIDropDownMenu_AddButton(info)
            end

            info = {}
            info.text = "取消"
            info.notCheckable = 1
            UIDropDownMenu_AddButton(info)
        end
    end

    SUI.raidDropDown.slotName = pName
    SUI.raidDropDown.slotRaidIndex = slot.raidIndex
    SUI.raidDropDown.slotGroup = slot.group
    SUI.raidDropDown.slotRank = slot.playerRank
    SUI.raidDropDown.slotIsSelf = (pName == UnitName("player"))
    ToggleDropDownMenu(1, nil, SUI.raidDropDown, "cursor")
end

local function CancelRaidDrag()
    if dragSlot then
        dragSlot = nil
        if dragHighlight then
            dragHighlight:Hide()
            dragHighlight:SetScript("OnUpdate", nil)
        end
    end
end

function SUI:UpdateRaidPage()
    local page = pages[4]
    if not page then return end

    local numRaid = GetNumRaidMembers()

    -- Clear all slots
    for g = 1, RAID_GROUPS do
        for s = 1, RAID_COLS do
            local slot = raidSlots[g] and raidSlots[g][s]
            if slot then
                slot.raidIndex = nil
                slot.playerName = nil
                slot.playerClass = nil
                slot.playerRank = nil
                slot.playerLevel = nil
                slot.nameFS:SetText("")
                slot.classIcon:Hide()
                SetPixelBackdrop(slot, T.raidSlotEmpty, T.slotBorder)
            end
        end
    end

    if numRaid == 0 then
        page.raidInfoFS:SetText("你不在团队中")
        return
    end

    page.raidInfoFS:SetText("团队成员: " .. numRaid .. "/40")

    local groupCount = {}
    for g = 1, RAID_GROUPS do groupCount[g] = 0 end

    for i = 1, numRaid do
        local name, rank, subgroup, level, class, fileName, zone, online, isDead, role, isML = GetRaidRosterInfo(i)
        if name and subgroup then
            local g = subgroup
            groupCount[g] = (groupCount[g] or 0) + 1
            local s = groupCount[g]
            if s <= RAID_COLS then
                local slot = raidSlots[g] and raidSlots[g][s]
                if slot then
                    slot.raidIndex = i
                    slot.playerName = name
                    slot.playerClass = fileName
                    slot.playerRank = rank
                    slot.playerLevel = level

                    local shortName = name
                    if string.len(name) > 7 then
                        shortName = string.sub(name, 1, 6) .. ".."
                    end
                    local prefix = ""
                    if rank and rank == 2 then prefix = "*"
                    elseif rank and rank == 1 then prefix = "+" end
                    slot.nameFS:SetText(prefix .. shortName)

                    local cc = GetClassColor(fileName)
                    slot.nameFS:SetTextColor(cc[1], cc[2], cc[3])
                    SetClassIcon(slot.classIcon, fileName)

                    if online then
                        SetPixelBackdrop(slot, T.slotBg, T.slotBorder)
                        if isDead then
                            slot.nameFS:SetTextColor(0.5, 0.2, 0.2)
                        end
                    else
                        SetPixelBackdrop(slot, { 0.06, 0.03, 0.05, 0.7 }, { 0.20, 0.15, 0.18, 0.6 })
                        slot.nameFS:SetTextColor(T.offlineText[1], T.offlineText[2], T.offlineText[3])
                    end
                end
            end
        end
    end
end

--------------------------------------------------------------------------------
-- Main Tab Switching
--------------------------------------------------------------------------------
local TAB_NAMES = { "好友", "查询", "工会", "团队" }

local function ShowPage(tabIdx)
    currentMainTab = tabIdx
    for i = 1, 4 do
        local p = pages[i]
        if p then
            if i == tabIdx then p:Show() else p:Hide() end
        end
        local btn = mainTabs[i]
        if btn then
            if i == tabIdx then
                SetRoundBackdrop(btn, T.tabActiveBg, T.tabActiveBorder)
                btn.text:SetTextColor(T.tabActiveText[1], T.tabActiveText[2], T.tabActiveText[3])
            else
                SetRoundBackdrop(btn, T.tabBg, T.tabBorder)
                btn.text:SetTextColor(T.tabText[1], T.tabText[2], T.tabText[3])
            end
        end
    end

    if tabIdx == 1 then
        if origShowFriendsAPI then origShowFriendsAPI() end
        SUI:UpdateFriendsPage()
    elseif tabIdx == 2 then
        if SetWhoToUI then SetWhoToUI(1) end
        SUI:UpdateWhoList()
    elseif tabIdx == 3 then
        SUI:UpdateGuildList()
    elseif tabIdx == 4 then
        SUI:UpdateRaidPage()
    end

    if MainFrame and MainFrame.titleFS then
        local titles = { "好友名单", "查询玩家", "公会", "团队" }
        if tabIdx == 3 and IsInGuild() then
            local guildName = GetGuildInfo("player")
            if guildName and guildName ~= "" then
                MainFrame.titleFS:SetText("< " .. guildName .. " >")
            else
                MainFrame.titleFS:SetText(titles[tabIdx])
            end
        else
            MainFrame.titleFS:SetText(titles[tabIdx] or "社交")
        end
    end
end

--------------------------------------------------------------------------------
-- Build Main Frame
--------------------------------------------------------------------------------
local function BuildMainFrame()
    if MainFrame then return end

    local f = CreateFrame("Frame", "SFramesSocialUI", UIParent)
    f:SetWidth(FRAME_W)
    f:SetHeight(FRAME_H)
    f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    f:SetFrameStrata("HIGH")
    f:SetFrameLevel(10)
    SetRoundBackdrop(f)
    CreateShadow(f)

    f:EnableMouse(true)
    f:SetMovable(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", function() this:StartMoving() end)
    f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
    f:SetScript("OnHide", function() CancelRaidDrag() end)

    -- Header
    local header = CreateFrame("Frame", nil, f)
    header:SetHeight(HEADER_H)
    header:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0)
    header:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 0)

    local titleIco = SFrames:CreateIcon(header, "friends", 14)
    titleIco:SetDrawLayer("OVERLAY")
    titleIco:SetPoint("CENTER", header, "CENTER", -40, 0)
    titleIco:SetVertexColor(T.gold[1], T.gold[2], T.gold[3])

    local title = MakeFS(header, 13, "CENTER", T.gold)
    title:SetPoint("LEFT", titleIco, "RIGHT", 4, 0)
    title:SetText("好友名单")
    f.titleFS = title

    local closeBtn = CreateFrame("Button", NextName("Close"), header)
    closeBtn:SetWidth(20)
    closeBtn:SetHeight(20)
    closeBtn:SetPoint("RIGHT", header, "RIGHT", -6, 0)
    closeBtn:SetScript("OnClick", function() f:Hide() end)
    local closeIco = SFrames:CreateIcon(closeBtn, "close", 12)
    closeIco:SetDrawLayer("OVERLAY")
    closeIco:SetPoint("CENTER", closeBtn, "CENTER", 0, 0)
    closeIco:SetVertexColor(1, 0.7, 0.7)
    closeBtn:SetScript("OnEnter", function()
        closeIco:SetVertexColor(1, 0.4, 0.5)
    end)
    closeBtn:SetScript("OnLeave", function()
        closeIco:SetVertexColor(1, 0.7, 0.7)
    end)

    -- Main tabs (use this.tabIndex to avoid closure issues)
    local tabContainer = CreateFrame("Frame", nil, f)
    tabContainer:SetHeight(TAB_BAR_H)
    tabContainer:SetPoint("TOPLEFT", f, "TOPLEFT", SIDE_PAD, -(HEADER_H + 2))
    tabContainer:SetPoint("TOPRIGHT", f, "TOPRIGHT", -SIDE_PAD, -(HEADER_H + 2))

    local tabW = math.floor((CONTENT_W - 6) / 4)
    for idx = 1, 4 do
        local tabIdx = idx
        local btn = CreateFrame("Button", NextName("MTab"), tabContainer)
        btn:SetWidth(tabW)
        btn:SetHeight(TAB_BAR_H - 2)
        btn.tabIndex = tabIdx
        if tabIdx == 1 then
            btn:SetPoint("LEFT", tabContainer, "LEFT", 0, 0)
        else
            btn:SetPoint("LEFT", mainTabs[tabIdx - 1], "RIGHT", 2, 0)
        end
        SetRoundBackdrop(btn, T.tabBg, T.tabBorder)
        local text = MakeFS(btn, 10, "CENTER", T.tabText)
        text:SetPoint("CENTER", 0, 0)
        text:SetText(TAB_NAMES[tabIdx])
        btn.text = text
        btn:SetScript("OnClick", function()
            ShowPage(this.tabIndex)
        end)
        mainTabs[tabIdx] = btn
    end

    -- Content pages
    local contentTop = -(HEADER_H + TAB_BAR_H + 6)
    for i = 1, 4 do
        local page = CreateFrame("Frame", nil, f)
        page:SetPoint("TOPLEFT", f, "TOPLEFT", SIDE_PAD, contentTop)
        page:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -SIDE_PAD, SIDE_PAD)
        page:Hide()
        pages[i] = page
    end

    BuildFriendsPage(pages[1])
    BuildWhoPage(pages[2])
    BuildGuildPage(pages[3])
    BuildRaidPage(pages[4])

    table.insert(UISpecialFrames, "SFramesSocialUI")

    local scale = SFramesDB.socialScale or 1
    if scale ~= 1 then f:SetScale(scale) end

    MainFrame = f
    f:Hide()
end

--------------------------------------------------------------------------------
-- Public API
--------------------------------------------------------------------------------
function SUI:Toggle(tabIdx)
    if not initialized then return end
    if not MainFrame then BuildMainFrame() end

    if MainFrame:IsShown() then
        if tabIdx and tabIdx ~= currentMainTab then
            ShowPage(tabIdx)
        else
            MainFrame:Hide()
        end
    else
        MainFrame:Show()
        ShowPage(tabIdx or 1)
    end
end

function SUI:Show(tabIdx)
    if not initialized then return end
    if not MainFrame then BuildMainFrame() end
    MainFrame:Show()
    ShowPage(tabIdx or 1)
end

function SUI:Hide()
    if MainFrame then MainFrame:Hide() end
end

function SUI:IsShown()
    return MainFrame and MainFrame:IsShown()
end

--------------------------------------------------------------------------------
-- Initialize
--------------------------------------------------------------------------------
function SUI:Initialize()
    if initialized then return end
    initialized = true

    guildHideOffline = SFramesDB.guildHideOffline or false

    BuildClassReverseLookup()
    HideBlizzardFriends()
    BuildMainFrame()

    local ef = CreateFrame("Frame", nil, UIParent)
    ef:RegisterEvent("FRIENDLIST_UPDATE")
    ef:RegisterEvent("IGNORELIST_UPDATE")
    ef:RegisterEvent("WHO_LIST_UPDATE")
    ef:RegisterEvent("GUILD_ROSTER_UPDATE")
    ef:RegisterEvent("RAID_ROSTER_UPDATE")
    ef:RegisterEvent("PARTY_MEMBERS_CHANGED")
    ef:SetScript("OnEvent", function()
        if event == "WHO_LIST_UPDATE" then
            local n, t = GetNumWhoResults()
            WhoDebug("收到 WHO_LIST_UPDATE! 结果=" .. tostring(n) .. " 总计=" .. tostring(t) .. " pending=" .. tostring(whoQueryPending))
            local wasPending = whoQueryPending
            whoQueryPending = false
            if SetWhoToUI then SetWhoToUI(1) end
            if FriendsFrame and FriendsFrame:IsShown() then
                FriendsFrame:Hide()
            end
            if MainFrame and MainFrame:IsShown() and currentMainTab == 2 then
                WhoDebug("更新列表显示")
                SUI:UpdateWhoList()
            else
                WhoDebug("自动打开查询页显示结果")
                if not MainFrame then BuildMainFrame() end
                if MainFrame then
                    MainFrame:Show()
                    ShowPage(2)
                    SUI:UpdateWhoList()
                end
            end
            return
        end
        if not MainFrame or not MainFrame:IsShown() then return end
        if event == "FRIENDLIST_UPDATE" or event == "IGNORELIST_UPDATE" then
            if currentMainTab == 1 then SUI:UpdateFriendsPage() end
        elseif event == "GUILD_ROSTER_UPDATE" then
            if currentMainTab == 3 then SUI:UpdateGuildList() end
        elseif event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED" then
            if currentMainTab == 4 then SUI:UpdateRaidPage() end
        end
    end)
end

--------------------------------------------------------------------------------
-- Hook ToggleFriendsFrame
--------------------------------------------------------------------------------
local origToggleFriendsFrame = ToggleFriendsFrame
ToggleFriendsFrame = function(tab)
    if SFramesDB and SFramesDB.enableSocial == false then
        if origToggleFriendsFrame then
            if origFriendsFrameShow and FriendsFrame then
                FriendsFrame.Show = origFriendsFrameShow
            end
            origToggleFriendsFrame(tab)
        end
        return
    end
    SUI:Toggle(tab or 1)
end

if ShowFriends then
    origShowFriendsAPI = ShowFriends
    local origShowFriends = ShowFriends
    ShowFriends = function()
        if SFramesDB and SFramesDB.enableSocial == false then
            if origShowFriends then origShowFriends() end
            return
        end
        SUI:Show(1)
    end
end

--------------------------------------------------------------------------------
-- Hook SetItemRef: shift-click player name -> WHO query in our panel
--------------------------------------------------------------------------------
do
    local origSetItemRef_SUI = SetItemRef
    SetItemRef = function(link, text, button)
        if link and IsShiftKeyDown and IsShiftKeyDown() then
            local playerName = nil
            if string.sub(link, 1, 7) == "player:" then
                playerName = string.sub(link, 8)
                local colonPos = string.find(playerName, ":")
                if colonPos then playerName = string.sub(playerName, 1, colonPos - 1) end
            end
            if playerName and playerName ~= "" then
                if SFramesDB and SFramesDB.enableSocial ~= false and initialized then
                    WhoDebug("Shift点击玩家: " .. playerName .. ", 发起WHO查询")
                    local query = "n-\"" .. playerName .. "\""
                    if not MainFrame then BuildMainFrame() end
                    if MainFrame then
                        MainFrame:Show()
                        ShowPage(2)
                    end
                    if pages[2] and pages[2].editBox then
                        pages[2].editBox:SetText(playerName)
                        if pages[2].editBox.placeholder then
                            pages[2].editBox.placeholder:Hide()
                        end
                    end
                    SUI:ClearWhoList()
                    DoSendWho(query)
                    return
                end
            end
        end
        if origSetItemRef_SUI then
            origSetItemRef_SUI(link, text, button)
        end
    end
end

--------------------------------------------------------------------------------
-- Bootstrap
--------------------------------------------------------------------------------
local bootstrap = CreateFrame("Frame", nil, UIParent)
bootstrap:RegisterEvent("PLAYER_LOGIN")
bootstrap:SetScript("OnEvent", function()
    if event == "PLAYER_LOGIN" then
        if SFramesDB.enableSocial == nil then
            SFramesDB.enableSocial = true
        end
        if SFramesDB.enableSocial ~= false then
            SUI:Initialize()
        end
    end
end)
