-------------------------------------------------------------------------------
-- HelwenLFG
-- Premade Group Finder enhancements for EllesmereUI:
-- spec icons, leader Mythic+ score, and decline-signup bypass.
-------------------------------------------------------------------------------
local ADDON = ...

local issecretvalue = issecretvalue or function() return false end

local DUNGEONS_CATEGORY = GROUP_FINDER_CATEGORY_ID_DUNGEONS or 2

local _ffd = setmetatable({}, { __mode = "k" })
local function GetFFD(frame)
    local d = _ffd[frame]
    if not d then d = {}; _ffd[frame] = d end
    return d
end

local function FontPath()
    return (EllesmereUI and EllesmereUI.GetFontPath and EllesmereUI.GetFontPath("blizzardSkin"))
        or STANDARD_TEXT_FONT
end

local function FeatureOn(key)
    local db = _G.HelwenLFGDB or _G.BoringLFGFilterDB
    local ff = db and db.features
    return not (ff and ff[key] == false)
end

local function IsRestricted()
    return InCombatLockdown() and true or false
end

local ROLE_RANK = { TANK = 1, HEALER = 2, DAMAGER = 3, NONE = 4 }

local _previewMembers = {}
local function PreviewRoleSort(a, b)
    return (ROLE_RANK[a.assignedRole] or 9) < (ROLE_RANK[b.assignedRole] or 9)
end

local _specIcon
local function BuildSpecIcons()
    if _specIcon then return end
    _specIcon = {}
    local num = (GetNumClasses and GetNumClasses()) or 13
    for cid = 1, num do
        local file
        if C_CreatureInfo and C_CreatureInfo.GetClassInfo then
            local ci = C_CreatureInfo.GetClassInfo(cid)
            file = ci and ci.classFile
        end
        if file and GetNumSpecializationsForClassID and GetSpecializationInfoForClassID then
            _specIcon[file] = {}
            for s = 1, (GetNumSpecializationsForClassID(cid) or 0) do
                local _, name, _, icon = GetSpecializationInfoForClassID(cid, s)
                if name and icon then _specIcon[file][name] = icon end
            end
        end
    end
end

local function SpecIconFor(classFile, specName)
    BuildSpecIcons()
    return classFile and specName and _specIcon[classFile] and _specIcon[classFile][specName] or nil
end

local PREVIEW_BACKDROP = {
    edgeFile = "Interface\\Buttons\\WHITE8X8",
    edgeSize = 1,
    insets = { left = 0, right = 0, top = 0, bottom = 0 },
}
local function MakePreviewSlot(parent, size)
    local f = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    f:SetSize(size, size)
    local bg = f:CreateTexture(nil, "BACKGROUND")
    bg:SetColorTexture(0.05, 0.05, 0.05, 0.85)
    bg:SetPoint("TOPLEFT", 1, -1)
    bg:SetPoint("BOTTOMRIGHT", -1, 1)
    local icon = f:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("TOPLEFT", 1, -1)
    icon:SetPoint("BOTTOMRIGHT", -1, 1)
    local edges
    if f.SetBackdrop then
        f:SetBackdrop(PREVIEW_BACKDROP)
        f:SetBackdropBorderColor(0, 0, 0, 1)
        edges = {}
    else
        edges = {}
        local function edge()
            local t = f:CreateTexture(nil, "OVERLAY")
            t:SetColorTexture(0, 0, 0, 1)
            edges[#edges + 1] = t
            return t
        end
        local top, bot, left, right = edge(), edge(), edge(), edge()
        top:SetPoint("TOPLEFT");    top:SetPoint("TOPRIGHT");    top:SetHeight(1)
        bot:SetPoint("BOTTOMLEFT"); bot:SetPoint("BOTTOMRIGHT"); bot:SetHeight(1)
        left:SetPoint("TOPLEFT");   left:SetPoint("BOTTOMLEFT");  left:SetWidth(1)
        right:SetPoint("TOPRIGHT"); right:SetPoint("BOTTOMRIGHT"); right:SetWidth(1)
    end
    return { frame = f, icon = icon, bg = bg, edges = edges }
end

local function ClearGroupPreview(entry, restoreDisplay)
    local d = GetFFD(entry)
    if d.previewSlots then
        for _, slot in ipairs(d.previewSlots) do slot.frame:Hide() end
    end
    if d.previewRow then d.previewRow:Hide() end
    if d.leaderIO then d.leaderIO:Hide() end
    if restoreDisplay and entry and entry.DataDisplay then entry.DataDisplay:SetAlpha(1) end
end

local function UpdateGroupPreview(entry)
    if not entry or entry:IsForbidden() then return end
    if IsRestricted() then return end

    local showSpecs = FeatureOn("specIcons")
    local showLeaderIO = FeatureOn("leaderIO")
    if not showSpecs and not showLeaderIO then
        ClearGroupPreview(entry, true)
        return
    end

    if not (C_LFGList and C_LFGList.GetSearchResultInfo) then
        ClearGroupPreview(entry, true)
        return
    end
    local resultID = entry.resultID
    if not resultID then
        ClearGroupPreview(entry, true)
        return
    end

    local info = C_LFGList.GetSearchResultInfo(resultID)
    if not info then
        ClearGroupPreview(entry, true)
        return
    end



    if issecretvalue(info.activityIDs) or issecretvalue(info.numMembers)
       or issecretvalue(info.leaderOverallDungeonScore) then
        ClearGroupPreview(entry, true)
        return
    end

    local actID = info.activityIDs and info.activityIDs[1]
    local act = actID and C_LFGList.GetActivityInfoTable and C_LFGList.GetActivityInfoTable(actID)
    local isDungeon = act and (act.isMythicPlusActivity or act.categoryID == DUNGEONS_CATEGORY)
    if not (isDungeon and not entry.isApplication and not info.isDelisted) then
        ClearGroupPreview(entry, true)
        return
    end

    local d = GetFFD(entry)
    local dd = entry.DataDisplay


    if showSpecs then
        if not d.previewRow then
            d.previewRow = CreateFrame("Frame", nil, entry)
            d.previewRow:SetFrameLevel((entry:GetFrameLevel() or 1) + 5)
            d.previewSlots = {}
        end
        local members = _previewMembers
        wipe(members)
        if C_LFGList.GetSearchResultPlayerInfo then
            for i = 1, (info.numMembers or 0) do
                local p = C_LFGList.GetSearchResultPlayerInfo(resultID, i)
                if p then members[#members + 1] = p end
            end
        end
        table.sort(members, PreviewRoleSort)

        local maxSlots = (act and act.maxNumPlayers) or 5
        if maxSlots < 1 then maxSlots = 5 end
        local size, gap = 18, 2
        if dd then dd:SetAlpha(0) end
        d.previewRow:SetSize(maxSlots * size + (maxSlots - 1) * gap, size)
        d.previewRow:ClearAllPoints()
        d.previewRow:SetPoint("RIGHT", entry, "RIGHT", -10, -4)
        d.previewRow:Show()

        for slot = 1, maxSlots do
            local s = d.previewSlots[slot]
            if not s then
                s = MakePreviewSlot(d.previewRow, size)
                d.previewSlots[slot] = s
            end
            s.frame:ClearAllPoints()
            s.frame:SetSize(size, size)
            s.frame:SetPoint("LEFT", d.previewRow, "LEFT", (slot - 1) * (size + gap), 0)
            local p = members[slot]
            if p then
                local icon = SpecIconFor(p.classFilename, p.specName)
                if icon then
                    s.icon:SetTexture(icon)
                    s.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
                elseif GetTexCoordsForRoleSmallCircle and p.assignedRole and p.assignedRole ~= "NONE" then
                    s.icon:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES")
                    s.icon:SetTexCoord(GetTexCoordsForRoleSmallCircle(p.assignedRole))
                else
                    s.icon:SetTexture(nil)
                end
                s.icon:SetAlpha(1)
                s.bg:SetAlpha(0.85)
                if s.frame.SetBackdropBorderColor then
                    s.frame:SetBackdropBorderColor(0, 0, 0, 1)
                else
                    for _, ed in ipairs(s.edges) do ed:SetAlpha(1) end
                end
            else
                s.icon:SetTexture(nil)
                s.bg:SetAlpha(0.22)
                if s.frame.SetBackdropBorderColor then
                    s.frame:SetBackdropBorderColor(0, 0, 0, 0.22)
                else
                    for _, ed in ipairs(s.edges) do ed:SetAlpha(0.22) end
                end
            end
            s.frame:Show()
        end
        for slot = maxSlots + 1, #d.previewSlots do
            d.previewSlots[slot].frame:Hide()
        end
    else

        if dd then dd:SetAlpha(1) end
        if d.previewSlots then
            for _, slot in ipairs(d.previewSlots) do slot.frame:Hide() end
        end
        if d.previewRow then d.previewRow:Hide() end
    end


    if not showLeaderIO then
        if d.leaderIO then d.leaderIO:Hide() end
        return
    end
    if not d.leaderIO then
        d.leaderIO = entry:CreateFontString(nil, "OVERLAY")
        d.leaderIO:SetFont(FontPath() or STANDARD_TEXT_FONT, 10, "OUTLINE")
    end
    local score = info.leaderOverallDungeonScore
    if score and score > 0 then
        local r, g, b = 1, 0.82, 0
        if C_ChallengeMode and C_ChallengeMode.GetDungeonScoreRarityColor then
            local col = C_ChallengeMode.GetDungeonScoreRarityColor(score)
            if col then r, g, b = col.r or r, col.g or g, col.b or b end
        end
        d.leaderIO:SetTextColor(r, g, b)
        d.leaderIO:ClearAllPoints()
        if d.previewRow and d.previewRow:IsShown() then
            d.leaderIO:SetPoint("BOTTOMRIGHT", d.previewRow, "TOPRIGHT", 0, 1)
        else
            d.leaderIO:SetPoint("TOPRIGHT", entry, "TOPRIGHT", -10, -5)
        end
        d.leaderIO:SetText(tostring(score))
        d.leaderIO:Show()
    else
        d.leaderIO:Hide()
    end
end

local ROLE_ICON_KEYS = { "RoleIcon1", "RoleIcon2", "RoleIcon3" }
local function ApplicantSpecIcon(member)
    if IsRestricted() or not FeatureOn("specIcons") then return end
    if not member then return end
    local applicant = member.GetParent and member:GetParent()
    local appID = applicant and applicant.applicantID
    local idx = member.memberIdx
    if not (appID and idx and C_LFGList and C_LFGList.GetApplicantMemberInfo) then return end
    local _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, specID = C_LFGList.GetApplicantMemberInfo(appID, idx)
    if not specID or specID == 0 or not GetSpecializationInfoByID then return end
    local _, _, _, icon = GetSpecializationInfoByID(specID)
    if not icon then return end
    for i = 1, #ROLE_ICON_KEYS do
        local ri = member[ROLE_ICON_KEYS[i]]
        if ri and ri.IsShown and ri:IsShown() then
            local nt = ri.GetNormalTexture and ri:GetNormalTexture()
            if nt and nt.SetTexture then nt:SetTexture(icon); nt:SetTexCoord(0.08, 0.92, 0.08, 0.92) end
            local ht = ri.GetHighlightTexture and ri:GetHighlightTexture()
            if ht and ht.SetTexture then ht:SetTexture(icon); ht:SetTexCoord(0.08, 0.92, 0.08, 0.92) end
        end
    end
end

local function InstallDeclineBypass()
    local f = _G.LFGListFrame
    if not f or f._hlfgDeclineBypass then return end
    f._hlfgDeclineBypass = true
    f.declines = setmetatable({}, { __newindex = function() end })
end

local function InstallHooks()
    local f = _G.LFGListFrame
    if not f or f._hlfgGroupFinderHooked then return end
    f._hlfgGroupFinderHooked = true


    if _G.LFGListSearchEntry_Update then
        hooksecurefunc("LFGListSearchEntry_Update", function(entry)
            if not entry or entry:IsForbidden() then return end
            pcall(UpdateGroupPreview, entry)
        end)
    end


    if _G.LFGListApplicationViewer_UpdateRoleIcons then
        hooksecurefunc("LFGListApplicationViewer_UpdateRoleIcons", function(member)
            pcall(ApplicantSpecIcon, member)
        end)
    end

    InstallDeclineBypass()
end

local boot = CreateFrame("Frame")
boot:RegisterEvent("PLAYER_LOGIN")
boot:RegisterEvent("ADDON_LOADED")
boot:SetScript("OnEvent", function(_, event, name)
    if event == "PLAYER_LOGIN" then
        InstallHooks()
        InstallDeclineBypass()
    elseif event == "ADDON_LOADED" and name == "Blizzard_GroupFinder" then
        InstallHooks()
        InstallDeclineBypass()
    end
end)
