-- =========================================================================
-- FILE: Guild Roster.lua (TBC Port)
-- =========================================================================
local addonName, NS = ...
local GR = _G["GRS_MainFrame"]

print("|cff00ff00GRS:|r Guild Roster UI v24 (TBC) Loaded.")

-- TBC Safe Class Color Helper
local function GetClassColorSafe(classFile)
    if C_ClassColor and C_ClassColor.GetClassColor then
        return C_ClassColor.GetClassColor(classFile or "PRIEST")
    elseif RAID_CLASS_COLORS and RAID_CLASS_COLORS[classFile] then
        local c = RAID_CLASS_COLORS[classFile]
        -- Add WrapTextInColorCode if not present
        if c and not c.WrapTextInColorCode then
            c.WrapTextInColorCode = function(self, text)
                return string.format("|cff%02x%02x%02x%s|r", self.r*255, self.g*255, self.b*255, text)
            end
        end
        return c
    end
    return { r = 1, g = 1, b = 1, WrapTextInColorCode = function(self, t) return "|cffffffff"..t.."|r" end }
end

-- DEFINITION: restricted = true means "Only show if GR:CheckPermissions() is true"
-- NOTE: 'lkey' stores the localization key, looked up at runtime via GetColName()
-- TBC: Removed M+, Raid Progress, and Key columns (Retail-only features)
GR.COL_DEF = {
    { lkey = nil, name = "", width = 20, key = "expand" },
    { lkey = "NAME", name = "Name", width = 210, key = "name", sort = "name" },
    { lkey = "RANK", name = "Rank", width = 80, key = "rank", sort = "rankIndex" },
    { lkey = "LAST_ONLINE", name = "Last Online", width = 80, key = "offline", sort = "offline" },
    { lkey = "LVL", name = "Lvl", width = 35, key = "level", sort = "level" },
    { lkey = "CLASS", name = "Class", width = 80, key = "class", sort = "class" },
    { lkey = "ZONE", name = "Zone", width = 110, key = "zone", sort = "zone" },
    { lkey = "ACHV", name = "Achv", width = 50, key = "ach", sort = "ach" },
    { lkey = "IDENTITY", name = "Identity", width = 130, key = "identity", sort = "parent", restricted = true },
}

-- Helper to get localized column name at runtime
local function GetColName(col)
    if col.lkey and NS.L and NS.L[col.lkey] then
        return NS.L[col.lkey]
    end
    return col.name
end

-- =========================================================================
-- 1. STYLING HELPERS & VISUALS
-- =========================================================================

local function SkinButton(btn)
    if not btn then return end
    btn:SetNormalTexture("")
    btn:SetHighlightTexture("")
    btn:SetPushedTexture("")
    btn:SetDisabledTexture("")
    if btn.Left then btn.Left:SetAlpha(0) end
    if btn.Middle then btn.Middle:SetAlpha(0) end
    if btn.Right then btn.Right:SetAlpha(0) end
    if btn.Top then btn.Top:SetAlpha(0) end
    if btn.Bottom then btn.Bottom:SetAlpha(0) end
    local t = btn:CreateTexture(nil, "BACKGROUND")
    t:SetAllPoints(); t:SetColorTexture(0.2, 0.2, 0.2, 1); btn.bg = t
    local b = btn:CreateTexture(nil, "BORDER")
    b:SetPoint("TOPLEFT", -1, 1); b:SetPoint("BOTTOMRIGHT", 1, -1); b:SetColorTexture(0, 0, 0, 1); btn.border = b
    btn:HookScript("OnEnter", function(s) if s.bg then s.bg:SetColorTexture(0.3, 0.3, 0.3, 1) end end)
    btn:HookScript("OnLeave", function(s) if s.bg then s.bg:SetColorTexture(0.2, 0.2, 0.2, 1) end end)
    btn:HookScript("OnMouseDown", function(s) if s.bg then s.bg:SetColorTexture(0.1, 0.1, 0.1, 1) end end)
    btn:HookScript("OnMouseUp", function(s) if s.bg then s.bg:SetColorTexture(0.3, 0.3, 0.3, 1) end end)
    local fs = btn:GetFontString(); if fs then fs:SetTextColor(1, 1, 1) end
end

-- =========================================================================
-- 2. HELPER: OFFICER CHECK
-- =========================================================================
function GR:IsOfficer()
    if not IsInGuild() then return false end
    -- TBC: Use CanEditOfficerNote() or fallback
    if CanEditOfficerNote then return CanEditOfficerNote() end
    if C_GuildInfo and C_GuildInfo.CanEditOfficerNote then return C_GuildInfo.CanEditOfficerNote() end
    return false
end

function GR:IsGM()
    if not IsInGuild() then return false end
    return IsGuildLeader()
end

function GR:CheckPermissions()
    if GR.devOfficerMode then return true end
    if not IsInGuild() then return false end
    if CanEditOfficerNote then return CanEditOfficerNote() end
    if C_GuildInfo and C_GuildInfo.CanEditOfficerNote then return C_GuildInfo.CanEditOfficerNote() end
    return false
end

-- =========================================================================
-- 3. ROSTER LIST GENERATION
-- =========================================================================

function GR:CreateRosterHeader(parent)
    local hFrame = CreateFrame("Frame", nil, parent)
    hFrame:SetHeight(GR.HEADER_HEIGHT)
    hFrame:SetPoint("TOPLEFT", 0, 0); hFrame:SetPoint("TOPRIGHT", 0, 0)
    local xOffset = 0
    for _, col in ipairs(GR.COL_DEF) do
        local hideAdvanced = GR.db and GR.db.disableAdvancedRoster and col.advanced
        if (not col.restricted or GR.hasPermission) and not hideAdvanced then
            local btn = CreateFrame("Button", nil, hFrame)
            btn:SetHeight(GR.HEADER_HEIGHT); btn:SetWidth(col.width); btn:SetPoint("LEFT", xOffset, 0)
            local text = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            text:SetPoint("LEFT", 5, 0); text:SetText(GetColName(col))
            if col.sort then
                btn:SetScript("OnClick", function() GR:SortRoster(col.sort) end)
                btn:SetScript("OnEnter", function() text:SetTextColor(1, 1, 1) end)
                btn:SetScript("OnLeave", function() text:SetTextColor(1, 0.82, 0) end)
            end
            local sep = btn:CreateTexture(nil, "ARTWORK"); sep:SetColorTexture(0.3, 0.3, 0.3, 0.5)
            sep:SetWidth(1); sep:SetHeight(15); sep:SetPoint("RIGHT", 0, 0)
            xOffset = xOffset + col.width
        end
    end
end

function GR:CreateRosterRow(parent)
    local row = CreateFrame("Button", nil, parent)
    row:SetHeight(GR.ROW_HEIGHT)
    local totalWidth = 0
    for _, col in ipairs(GR.COL_DEF) do if not col.restricted or GR.hasPermission then totalWidth = totalWidth + col.width end end
    row:SetWidth(math.max(totalWidth, 1280))
    local hl = row:CreateTexture(nil, "HIGHLIGHT"); hl:SetAllPoints(); hl:SetColorTexture(1, 1, 1, 0.1)
    row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    
    -- HOOK: Interaction Scripts
    row:SetScript("OnClick", function(self, button)
        if button == "RightButton" and self.data then GR:OpenContextMenu(self, self.data)
        elseif button == "LeftButton" and self.data then GR:SelectMember(self.data) end
    end)
    
    -- HOOK: Social Tooltip
    row:SetScript("OnEnter", function(self)
        if self.data and GR.ShowSocialTooltip and GR.db and GR.db.socialEnabled then
            GR:ShowSocialTooltip(self.data)
        end
    end)
    
    row:SetScript("OnLeave", function(self)
        if GR.SocialTooltip then GR.SocialTooltip:Hide() end
    end)

    row.cols = {}
    local xOffset = 0
    for _, def in ipairs(GR.COL_DEF) do
        local hideAdvanced = GR.db and GR.db.disableAdvancedRoster and def.advanced
        if (not def.restricted or GR.hasPermission) and not hideAdvanced then
            local f = CreateFrame("Frame", nil, row)
            f:SetHeight(GR.ROW_HEIGHT); f:SetWidth(def.width); f:SetPoint("LEFT", xOffset, 0)
            if def.key == "expand" then
                local expBtn = CreateFrame("Button", nil, f)
                expBtn:SetSize(16, 16); expBtn:SetPoint("CENTER", 0, 0)
                expBtn:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
                expBtn:GetNormalTexture():SetDesaturated(true) 
                expBtn:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down"); expBtn:GetPushedTexture():SetDesaturated(true)
                expBtn:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight"); expBtn:GetHighlightTexture():SetDesaturated(true)
                expBtn:Hide()
                row.expBtn = expBtn
                expBtn:SetScript("OnClick", function() if row.data then GR.ExpandedMains[row.data.dbKey] = not GR.ExpandedMains[row.data.dbKey]; GR:BuildViewList() end end)
            else
                local t = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
                t:SetPoint("LEFT", 5, 0); t:SetWidth(def.width - 10); t:SetJustifyH("LEFT"); t:SetWordWrap(false)
                row.cols[def.key] = t; f.text = t
            end
            xOffset = xOffset + def.width
        end
    end
    local status = row:CreateTexture(nil, "OVERLAY"); status:SetSize(12, 12); status:SetTexture("Interface\\COMMON\\Indicator-Gray")
    status:SetPoint("LEFT", row, "LEFT", 220, 0); row.statusDot = status
    return row
end

function GR:UpdateRosterList()
    local mainContainer = GR.rosterListContainer or _G["GRS_RosterListFrame"]
    if not mainContainer then return end
    local currentPermission = GR:CheckPermissions()
    GR.hasPermission = currentPermission
    if GR.lastPermissionState ~= currentPermission then
        if GR.RosterSplitMain then GR.RosterSplitMain:Hide(); GR.RosterSplitMain = nil; GR.RosterRows = {}; GR.Rows = {}; if GR.RosterContent then GR.RosterContent:Hide(); GR.RosterContent = nil end end
        if GR.RightPaneTop then GR.RightPaneTop:Hide(); GR.RightPaneTop = nil end
        if GR.RightPaneBottom then GR.RightPaneBottom:Hide(); GR.RightPaneBottom = nil end
        GR.lastPermissionState = currentPermission
    end

    -- [[ DYNAMIC WIDTH CALCULATION: STRICT MODE ]]
    local rosterWidth = 0
    for _, col in ipairs(GR.COL_DEF) do
        local hideAdvanced = GR.db and GR.db.disableAdvancedRoster and col.advanced
        if (not col.restricted or GR.hasPermission) and not hideAdvanced then
            rosterWidth = rosterWidth + col.width
        end
    end

    if not GR.RosterSplitMain then
        GR.RosterSplitMain = CreateFrame("Frame", nil, mainContainer)
        GR.RosterSplitMain:SetPoint("TOPLEFT", 0, 0); GR.RosterSplitMain:SetPoint("BOTTOMLEFT", 0, 0)
        GR.RosterSplitMain:SetWidth(rosterWidth) 

        GR.RightPaneTop = CreateFrame("Frame", nil, mainContainer, "BackdropTemplate")
        GR.RightPaneTop:SetPoint("TOPRIGHT", 0, 0)
        GR.RightPaneTop:SetPoint("TOPLEFT", GR.RosterSplitMain, "TOPRIGHT", 5, 0)
        GR.RightPaneTop:SetHeight(GR.TOP_PANE_HEIGHT)
        GR.RightPaneTop:SetBackdrop({ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 16, insets = { left = 4, right = 4, top = 4, bottom = 4 } })
        GR.RightPaneTop:SetBackdropColor(0, 0, 0, 0.9)

        GR.RightPaneBottom = CreateFrame("Frame", nil, mainContainer, "BackdropTemplate")
        GR.RightPaneBottom:SetPoint("TOPLEFT", GR.RightPaneTop, "BOTTOMLEFT", 0, 0); GR.RightPaneBottom:SetPoint("BOTTOMRIGHT", 0, 0)
        GR.RightPaneBottom:SetBackdrop({ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 16, insets = { left = 4, right = 4, top = 4, bottom = 4 } })
        GR.RightPaneBottom:SetBackdropColor(0, 0, 0, 0.9)

        GR:CreateRosterHeader(GR.RosterSplitMain)
        local sf = CreateFrame("ScrollFrame", "GRS_RosterScrollDirect", GR.RosterSplitMain, "UIPanelScrollFrameTemplate")
        sf:SetPoint("TOPLEFT", GR.RosterSplitMain, "TOPLEFT", 0, -GR.HEADER_HEIGHT - 5)
        sf:SetPoint("BOTTOMRIGHT", GR.RosterSplitMain, "BOTTOMRIGHT", -26, 5) 
        if NS.StyleScrollBar then NS.StyleScrollBar(sf) end
        local content = CreateFrame("Frame", nil, sf)
        content:SetHeight(500); content:SetWidth(sf:GetWidth()); sf:SetScrollChild(content)
        sf:SetScript("OnSizeChanged", function(self, w, h) content:SetWidth(w) end)
        GR.RosterContent = content; GR.RosterScrollFrame = sf
        GR:BuildRightSide(GR.RightPaneTop, GR.RightPaneBottom)
        GR.RosterRows = {}
    end
    
    if GR.RosterSplitMain then GR.RosterSplitMain:SetWidth(rosterWidth) end
    if #GR.ViewList == 0 and #GR.RosterData == 0 then GR:FetchGuildData() end

    local data = GR.ViewList; local content = GR.RosterContent
    for _, r in ipairs(GR.Rows) do r:Hide() end
    for i, d in ipairs(data) do
        local r = GR.Rows[i]
        if not r then r = GR:CreateRosterRow(content); GR.Rows[i] = r end
        r:SetPoint("TOPLEFT", 0, -((i - 1) * GR.ROW_HEIGHT)); r:SetWidth(content:GetWidth()); r:Show(); r.data = d
        local indent = d.isChild and 15 or 0
        r.cols["name"]:SetPoint("LEFT", 5 + indent, 0)
        -- TBC: Use safe class color helper
        local cColor = GetClassColorSafe((d.class or "PRIEST"):upper())
        r.cols["name"]:SetText(cColor:WrapTextInColorCode(d.name))
        r.cols["rank"]:SetText(d.rank); r.cols["level"]:SetText(d.level); r.cols["class"]:SetText(cColor:WrapTextInColorCode(d.class))
        r.cols["zone"]:SetText(d.zone); r.cols["offline"]:SetText(d.timeText); r.cols["ach"]:SetText(d.ach or "-")
        if r.cols["identity"] then r.cols["identity"]:SetText(d.identity or "") end
        r.statusDot:Show(); r.statusDot:SetTexture("Interface\\COMMON\\Indicator-Gray")
        if d.online then r.statusDot:SetVertexColor(d.isChild and 1 or 0, d.isChild and 0.6 or 1, 0)
        else r.statusDot:SetVertexColor(d.isOnlineOnAlt and 1 or 0.5, d.isOnlineOnAlt and 0.5 or 0.5, d.isOnlineOnAlt and 0 or 0.5) end
        if d.hasChildren then
            r.expBtn:Show(); r.expBtn:SetNormalTexture(d.isExpanded and "Interface\\Buttons\\UI-MinusButton-Up" or "Interface\\Buttons\\UI-PlusButton-Up")
            local nt = r.expBtn:GetNormalTexture(); if nt then nt:SetDesaturated(true) end
        else r.expBtn:Hide() end
    end
    content:SetHeight(#data * GR.ROW_HEIGHT)
end

-- =========================================================================
-- 4. MAIN BUILD FUNCTION
-- =========================================================================
function GR:BuildRosterView(parent)
    if not parent then return end
    if parent.isRosterBuilt then return end
    parent.isRosterBuilt = true

    for _, region in ipairs({parent:GetRegions()}) do
        if region:GetObjectType() == "FontString" then region:Hide() end
    end
    
    local gmotdLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    gmotdLabel:SetPoint("TOPLEFT", 20, -50)
    gmotdLabel:SetPoint("TOPRIGHT", -20, -50)
    gmotdLabel:SetJustifyH("CENTER")
    gmotdLabel:SetTextColor(0, 1, 0, 1)
    
    gmotdLabel:SetText(GetGuildRosterMOTD() or NS.L and NS.L["NO_MOTD"] or "No Message of the Day")
    parent.gmotdLabel = gmotdLabel
    
    local motdFrame = CreateFrame("Frame", nil, parent)
    motdFrame:RegisterEvent("GUILD_MOTD")
    motdFrame:RegisterEvent("GUILD_ROSTER_UPDATE")
    
    local function UpdateMOTD()
        local msg = GetGuildRosterMOTD()
        if msg and msg ~= "" then
            gmotdLabel:SetText(msg)
        else
            gmotdLabel:SetText(NS.L and NS.L["NO_MOTD"] or "No Message of the Day")
        end
    end

    motdFrame:SetScript("OnEvent", function() 
        if parent:IsVisible() then UpdateMOTD() end 
    end)
    
    parent:HookScript("OnShow", function()
        -- TBC: Use GuildRoster() instead of C_GuildInfo.GuildRoster()
        if GuildRoster then GuildRoster() elseif C_GuildInfo and C_GuildInfo.GuildRoster then C_GuildInfo.GuildRoster() end
        UpdateMOTD()
    end)

    local listContainer = CreateFrame("Frame", "GRS_RosterListFrame", parent, "BackdropTemplate")
    listContainer:SetPoint("TOPLEFT", 10, -80)
    listContainer:SetPoint("BOTTOMRIGHT", -10, 50)
    listContainer:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    listContainer:SetBackdropColor(0, 0, 0, 0.8)
    GR.rosterListContainer = listContainer

    local btnHeight = 22
    local btnWidth = 120

    local btnInvite = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnInvite:SetSize(btnWidth, btnHeight)
    btnInvite:SetPoint("BOTTOMLEFT", 10, 10)
    btnInvite:SetText(NS.L and NS.L["INVITE_MEMBER"] or "Invite Member")
    SkinButton(btnInvite)
    if CanGuildInvite() then btnInvite:Enable() else btnInvite:Disable() end
    btnInvite:SetScript("OnClick", function() StaticPopup_Show("ADD_GUILDMEMBER") end)

    local btnSettings = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btnSettings:SetSize(btnWidth, btnHeight)
    btnSettings:SetPoint("LEFT", btnInvite, "RIGHT", 10, 0)
    btnSettings:SetText(NS.L and NS.L["GUILD_SETTINGS"] or "Guild Settings")
    SkinButton(btnSettings)
    if GR:IsGM() then btnSettings:Show() else btnSettings:Hide() end
    btnSettings:SetScript("OnClick", function() 
        if not InCombatLockdown() then
            -- TBC: GuildControlUI may not exist, use fallback
            if GuildControlUI_LoadUI then GuildControlUI_LoadUI() end
            if GuildControlUI then GuildControlUI:Show() end
        end
    end)

    -- TBC: Skip Recruitment button (requires Blizzard_Communities which doesn't exist in TBC)
    
    local chkReplace = CreateFrame("CheckButton", nil, parent, "ChatConfigCheckButtonTemplate")
    chkReplace:SetPoint("BOTTOMRIGHT", -200, 10)
    chkReplace.Text:SetText(NS.L and NS.L["REPLACE_NATIVE_FRAME"] or "Replace Native Frame")
    if GRS_DB and GRS_DB.ReplaceGuildFrame then chkReplace:SetChecked(true) else chkReplace:SetChecked(false) end
    
    chkReplace:SetScript("OnClick", function(self)
        if not GRS_DB then GRS_DB = {} end
        GRS_DB.ReplaceGuildFrame = self:GetChecked()
        print("|cff00ff00GRS:|r Native Frame Replacement: " .. (GRS_DB.ReplaceGuildFrame and "ON" or "OFF"))
    end)
    
    if not GR.hookedToggle then
        -- TBC: Hook ToggleGuildFrame for native frame replacement
        if ToggleGuildFrame then
            hooksecurefunc("ToggleGuildFrame", function()
                if GRS_DB and GRS_DB.ReplaceGuildFrame then
                    if GuildFrame and GuildFrame:IsShown() then GuildFrame:Hide() end
                    if GR:IsShown() then GR:Hide() else GR:Show() end
                end
            end)
        end
        GR.hookedToggle = true
    end
    
    if GR.UpdateRosterList then GR:UpdateRosterList() end
    
    print("|cff00ff00GRS:|r Roster UI Built.")
end

local loader = CreateFrame("Frame")
loader:RegisterEvent("PLAYER_ENTERING_WORLD")
loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function(self, event, arg1)
    if GR and GR.views and GR.views[5] then
        GR:BuildRosterView(GR.views[5])
        self:UnregisterAllEvents() 
    end
end)