local addonName = "WDD"

-- Display mode: "minimal" (1 line everywhere), "mixed" (1 line world + full LFG), "verbose" (full everywhere)
-- Declared at file scope so all hook closures share the same upvalue and see live changes.
local wddMode = "mixed"

-- Error logging: catches errors from hook callbacks before they reach Blizzard's
-- error handler (which triggers the "disable addons" popup).
-- Errors are saved to WDD_ErrorLog (a SavedVariable) so they persist after /reload.
-- Use  /wdd errors  to view them and  /wdd clear  to reset.
local function WDDLogError(msg)
    if not WDD_ErrorLog then WDD_ErrorLog = {} end
    local entry = "[" .. date("%H:%M:%S") .. "] " .. tostring(msg)
    -- Append a short stack trace so we know which line failed
    local stack = debugstack(2)
    if stack then
        -- Keep only the first 3 lines of the trace to avoid huge entries
        local lines, n = {}, 0
        for line in stack:gmatch("[^\n]+") do
            n = n + 1
            if n > 3 then break end
            lines[#lines + 1] = "  " .. line
        end
        entry = entry .. "\n" .. table.concat(lines, "\n")
    end
    table.insert(WDD_ErrorLog, entry)
    -- Keep the log bounded to the last 50 errors
    while #WDD_ErrorLog > 50 do
        table.remove(WDD_ErrorLog, 1)
    end
end

-- -- All specializations
-- local specializations = {
-- 	"Blood Death Knight",
-- 	"Frost Death Knight",
-- 	"Unholy Death Knight",
-- 	"Brewmaster Monk",
-- 	"Windwalker Monk",
-- 	"Mistweaver Monk",
-- 	"Arcane Mage",
-- 	"Fire Mage",
-- 	"Frost Mage",
-- 	"Affliction Warlock",
-- 	"Demonology Warlock",
-- 	"Destruction Warlock",
-- 	"Balance Druid",
-- 	"Feral Druid",
-- 	"Guardian Druid",
-- 	"Restoration Druid",
-- 	"Beast Mastery Hunter",
-- 	"Marksmanship Hunter",
-- 	"Survival Hunter",
-- 	"Discipline Priest",
-- 	"Holy Priest",
-- 	"Shadow Priest",
-- 	"Assassination Rogue",
-- 	"Outlaw Rogue",
-- 	"Subtlety Rogue",
-- 	"Elemental Shaman",
-- 	"Enhancement Shaman",
-- 	"Restoration Shaman",
-- 	"Havoc Demon Hunter",
-- 	"Vengeance Demon Hunter",
-- 	"Devourer Demon Hunter",
-- 	"Fury Warrior",
-- 	"Arms Warrior",
-- 	"Protection Warrior",
-- 	"Retribution Paladin",
-- 	"Protection Paladin",
-- 	"Holy Paladin",
-- 	"Devastation Evoker",
-- 	"Preservation Evoker",
-- 	"Augmentation Evoker"
-- }

-- Role mappings
local roleNames = {
    [0] = "",
    [1] = "Tank",
    [2] = "Healer",
    [3] = "DPS"
}

-- Role icons (WoW built-in)
local roleIcons = {
    [0] = "",
    [1] = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:0:64:64:0:19:22:41|t",  -- Tank
    [2] = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:0:64:64:20:39:1:20|t",  -- Healer
    [3] = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp:16:16:0:0:64:64:20:39:22:41|t"  -- DPS
}

local roleColors = {
    [0] = {r=0.5, g=0.5, b=0.5},
    [1] = {r=0.78, g=0.61, b=0.43}, -- Tank (tan)
    [2] = {r=0.25, g=0.78, b=0.25}, -- Healer (green)
    [3] = {r=0.78, g=0.25, b=0.25}  -- DPS (red)
}

-- Format number as K (thousands), always rounded up to next thousand
local function FormatNumber(num)
    if num == 0 then return "0K" end
    return string.format("%.0fK", math.ceil(num / 1000))
end

-- Get color based on percentage
local function GetPercentageColor(percentage)
    if percentage >= 90 then
        return {r=0.9, g=0.8, b=0.5}     -- Artifact gold
    elseif percentage >= 75 then
        return {r=1, g=0.5, b=0}         -- Orange
    elseif percentage >= 60 then
        return {r=0.64, g=0.21, b=0.93}  -- Purple
    elseif percentage >= 45 then
        return {r=0, g=0.44, b=0.87}     -- Blue
    elseif percentage >= 30 then
        return {r=0.25, g=0.78, b=0.25}  -- Green
    else
        return {r=0.5, g=0.5, b=0.5}     -- Gray
    end
end

-- Create a frame to register events
local frame = CreateFrame("Frame")
frame:RegisterEvent("ADDON_LOADED")
frame:SetScript("OnEvent", function(self, event, loadedAddon)
    if loadedAddon == addonName then
        self:UnregisterEvent("ADDON_LOADED")
        
        -- Check if data is loaded
        if not _G.WDD_Data or not _G.WDD_DungeonData then
            print("|cFFFF0000WDD:|r Data file not loaded!")
            return
        end

        local wddDataCount = 0
        for _ in pairs(_G.WDD_Data) do wddDataCount = wddDataCount + 1 end

        -- Build ordered dungeon keys from WDD_DungeonData (order matches pack index in DungeonData files)
        local dungeonKeys = {}
        local dungeonKeyToPackIndex = {}
        for i, info in ipairs(_G.WDD_DungeonData) do
            dungeonKeys[#dungeonKeys + 1] = info.key
            dungeonKeyToPackIndex[info.key] = i
        end
        table.sort(dungeonKeys)

        -- Build dungeon name → key mapping from DungeonData for LFG activity matching.
        -- Names are stripped of non-alpha characters to avoid apostrophe encoding mismatches.
        local function simplifyName(s)
            return s:lower():gsub("[^%a%s]", "")
        end
        local dungeonNameToKey = {}
        for _, dungeonInfo in ipairs(_G.WDD_DungeonData) do
            dungeonNameToKey[simplifyName(dungeonInfo.name)] = dungeonInfo.key
        end

        -- maxDps[key] = pre-computed 10th highest DPS, stored in WDD_DungeonData by the converter
        local maxDps = {}
        for _, info in ipairs(_G.WDD_DungeonData) do
            maxDps[info.key] = info.dpsAt100
        end

        -- Initialize settings (WDD_Settings is nil on first run)
        if not WDD_Settings then WDD_Settings = {} end
        if WDD_Settings.mode then wddMode = WDD_Settings.mode end

        print("|cFFFF8000WDD:|r " .. wddDataCount .. " characters loaded successfully.")

        -- Forward declaration so AddDungeonDpsLines can call it before its definition
        local AddWDDSummaryLine

        -- Helper to add dungeon DPS lines to any tooltip for a given lookup key
        -- currentDungeonKey: optional key (e.g. "AA") to highlight in green
        local function AddDungeonDpsLines(tooltip, lookupKey, currentDungeonKey)
            local packed = _G.WDD_Data[lookupKey]

            if not packed then
                AddWDDSummaryLine(tooltip, lookupKey)
                tooltip:Show()
                return
            end

            tooltip:AddLine(" ")
            AddWDDSummaryLine(tooltip, lookupKey)

            for _, dungeonKey in ipairs(dungeonKeys) do
                local idx = dungeonKeyToPackIndex[dungeonKey]
                local d = packed and tonumber(string.sub(packed, (idx - 1) * 6 + 1, idx * 6), 16) or 0
                local isCurrent = dungeonKey == currentDungeonKey
                local keyR, keyG, keyB = isCurrent and 0 or 1, isCurrent and 1 or 1, isCurrent and 0 or 1

                if d > 0 then
                    local roleIcon = roleIcons[3]
                    local dpsText = FormatNumber(d)
                    local percentage = math.floor((d / maxDps[dungeonKey]) * 100)
                    local displayText = roleIcon .. " " .. dpsText .. " (" .. percentage .. "%)"
                    local percentageColor = GetPercentageColor(percentage)
                    tooltip:AddDoubleLine("  " .. dungeonKey, displayText, keyR, keyG, keyB, percentageColor.r, percentageColor.g, percentageColor.b)
                else
                    tooltip:AddDoubleLine("  " .. dungeonKey, "-", keyR, keyG, keyB, 0.5, 0.5, 0.5)
                end
            end

            tooltip:Show()
        end

        -- Hook into LFG search entry tooltip (shows leader's data)
        hooksecurefunc("LFGListUtil_SetSearchEntryTooltip", function(tooltip, resultID)
            xpcall(function()
                local searchResultInfo = C_LFGList.GetSearchResultInfo(resultID)
                if not searchResultInfo or not searchResultInfo.leaderName then return end

                local charName, realm
                local dashPos = searchResultInfo.leaderName:find("-")
                if dashPos then
                    charName = searchResultInfo.leaderName:sub(1, dashPos - 1)
                    realm = searchResultInfo.leaderName:sub(dashPos + 1):gsub(" ", "")
                else
                    charName = searchResultInfo.leaderName
                    realm = GetRealmName():gsub(" ", "")
                end
                local lookupKey = charName .. "-" .. realm .. "-eu"

                -- Match activityInfo.fullName against our dungeon name→key map
                local currentDungeonKey
                local activityID = searchResultInfo.activityIDs and searchResultInfo.activityIDs[1]
                if activityID then
                    local activityInfo = C_LFGList.GetActivityInfoTable(activityID)
                    if activityInfo and activityInfo.fullName then
                        local simplified = simplifyName(activityInfo.fullName)
                        for name, key in pairs(dungeonNameToKey) do
                            if simplified:find(name, 1, true) then
                                currentDungeonKey = key
                                break
                            end
                        end
                    end
                end

                if wddMode == "minimal" then
                    AddWDDSummaryLine(tooltip, lookupKey)
                    tooltip:Show()
                else
                    AddDungeonDpsLines(tooltip, lookupKey, currentDungeonKey)
                end
            end, WDDLogError)
        end)

        -- Hook applicant member tooltip via the global function Blizzard exposes
        -- LFGListApplicantMember_OnEnter(self) is a global function called by the XML OnEnter script.
        -- Our hook runs after Blizzard has already built and shown the tooltip.
        local function SetupApplicantMemberHooks()
            if not _G.LFGListApplicantMember_OnEnter then return false end
            if _G._wddApplicantHooked then return true end
            _G._wddApplicantHooked = true

            hooksecurefunc("LFGListApplicantMember_OnEnter", function(self)
                xpcall(function()
                    local applicantID = self:GetParent().applicantID
                    local memberIdx = self.memberIdx
                    if not applicantID or not memberIdx then return end

                    local name = C_LFGList.GetApplicantMemberInfo(applicantID, memberIdx)
                    if not name then return end

                    local charName, realm
                    local dashPos = name:find("-")
                    if dashPos then
                        charName = name:sub(1, dashPos - 1)
                        realm = name:sub(dashPos + 1):gsub(" ", "")
                    else
                        charName = name
                        realm = GetRealmName():gsub(" ", "")
                    end

                    if wddMode == "minimal" then
                        AddWDDSummaryLine(GameTooltip, charName .. "-" .. realm .. "-eu")
                        GameTooltip:Show()
                    else
                        AddDungeonDpsLines(GameTooltip, charName .. "-" .. realm .. "-eu")
                    end
                end, WDDLogError)
            end)

            return true
        end

        -- Try immediately; if the addon isn't loaded yet, wait for it
        if not SetupApplicantMemberHooks() then
            local lfgWatcher = CreateFrame("Frame")
            lfgWatcher:RegisterEvent("ADDON_LOADED")
            lfgWatcher:SetScript("OnEvent", function(self, event, addon)
                if SetupApplicantMemberHooks() then
                    self:UnregisterEvent("ADDON_LOADED")
                end
            end)
        end

        -- Shared helper: append a single WDD summary line to any tooltip for a given lookupKey
        AddWDDSummaryLine = function(tooltip, lookupKey)
            local totalDungeons = #dungeonKeys
            local count = 0
            local sumP2 = 0  -- sum of p²  (numerator of weighted average)
            local sumP  = 0  -- sum of p   (denominator of weighted average)

            local packed = _G.WDD_Data[lookupKey]
            if packed then
                for _, dungeonKey in ipairs(dungeonKeys) do
                    local idx = dungeonKeyToPackIndex[dungeonKey]
                    local d = tonumber(string.sub(packed, (idx - 1) * 6 + 1, idx * 6), 16)
                    if d and d > 0 then
                        local percentage = math.floor((d / maxDps[dungeonKey]) * 100)
                        sumP2 = sumP2 + percentage * percentage
                        sumP  = sumP  + percentage
                        count = count + 1
                    end
                end
            end

            local text, r, g, b
            if count == 0 then
                text = "-"
                r, g, b = 0.5, 0.5, 0.5
            else
                local avgPercent = math.floor(sumP2 / sumP)
                local color = GetPercentageColor(avgPercent)
                text = string.format("%d%%", avgPercent)
                r, g, b = color.r, color.g, color.b
            end
            tooltip:AddDoubleLine("WDD", text, 1, 0.5, 0, r, g, b)
        end

        -- Hook into normal unit tooltip (player mouseover in world/UI)
        TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Unit, function(tooltip, data)
            xpcall(function()
                local _, unit = tooltip:GetUnit()
                if not unit then return end
                local ok, isPlayer = pcall(UnitIsPlayer, unit)
                if not ok or not isPlayer then return end
                if UnitLevel(unit) ~= 90 then return end

                local name, realm = UnitName(unit)
                if not name then return end
                if not realm or realm == "" then
                    realm = GetRealmName():gsub(" ", "")
                else
                    realm = realm:gsub(" ", "")
                end
                if wddMode == "verbose" then
                    AddDungeonDpsLines(tooltip, name .. "-" .. realm .. "-eu")
                else
                    AddWDDSummaryLine(tooltip, name .. "-" .. realm .. "-eu")
                end
            end, WDDLogError)
        end)

        -- Hook guild member entries in the Communities/Guild frame
        local function SetupCommunitiesMemberHooks()
            if not CommunitiesMemberListEntryMixin then return end
            hooksecurefunc(CommunitiesMemberListEntryMixin, "OnEnter", function(self)
                xpcall(function()
                    local memberInfo = self.memberInfo
                    if not memberInfo or not memberInfo.name then return end

                    local fullName = memberInfo.name
                    local name, realm
                    local dashPos = fullName:find("-")
                    if dashPos then
                        name = fullName:sub(1, dashPos - 1)
                        realm = fullName:sub(dashPos + 1):gsub(" ", "")
                    else
                        name = fullName
                        realm = GetRealmName():gsub(" ", "")
                    end

                    if wddMode == "verbose" then
                        AddDungeonDpsLines(GameTooltip, name .. "-" .. realm .. "-eu")
                    else
                        AddWDDSummaryLine(GameTooltip, name .. "-" .. realm .. "-eu")
                        GameTooltip:Show()
                    end
                end, WDDLogError)
            end)
        end

        -- Blizzard_Communities loads lazily; hook it when ready
        local commFrame = CreateFrame("Frame")
        commFrame:RegisterEvent("ADDON_LOADED")
        commFrame:SetScript("OnEvent", function(self, event, addon)
            if addon == "Blizzard_Communities" then
                self:UnregisterEvent("ADDON_LOADED")
                SetupCommunitiesMemberHooks()
            end
        end)
        -- In case it's already loaded
        if C_AddOns.IsAddOnLoaded("Blizzard_Communities") then
            SetupCommunitiesMemberHooks()
        end

        -- Slash command: /wdd errors  →  print log   |   /wdd clear  →  wipe log
        SLASH_WDD1 = "/wdd"
        SlashCmdList["WDD"] = function(msg)
            msg = (msg or ""):lower():match("^%s*(.-)%s*$")  -- trim whitespace
            if msg == "errors" then
                if not WDD_ErrorLog or #WDD_ErrorLog == 0 then
                    print("|cFF00FF00WDD:|r No errors logged.")
                else
                    print("|cFF00FF00WDD:|r " .. #WDD_ErrorLog .. " error(s) logged:")
                    for i, entry in ipairs(WDD_ErrorLog) do
                        print("|cFFFF8800[" .. i .. "]|r " .. entry)
                    end
                end
            elseif msg == "clear" then
                WDD_ErrorLog = {}
                print("|cFF00FF00WDD:|r Error log cleared.")
            elseif msg == "mode" then
                print("|cFF00FF00WDD:|r Current mode: " .. wddMode .. "  |  Usage: /wdd mode [minimal|mixed|verbose]")
            else
                local newMode = msg:match("^mode%s+(%a+)$")
                if newMode == "minimal" or newMode == "mixed" or newMode == "verbose" then
                    wddMode = newMode
                    WDD_Settings.mode = newMode
                    print("|cFF00FF00WDD:|r Mode set to " .. newMode .. ".")
                elseif newMode then
                    print("|cFF00FF00WDD:|r Invalid mode. Valid modes: minimal | mixed | verbose")
                else
                    print("|cFF00FF00WDD:|r Commands:  /wdd errors  |  /wdd clear  |  /wdd mode [minimal|mixed|verbose]")
                end
            end
        end

    end
end)
