local ldb = LibStub("LibDataBroker-1.1", true)
local dbicon = LibStub("LibDBIcon-1.0", true)
local L = LibStub("AceLocale-3.0"):GetLocale("CritMatic")
local qtip = LibStub("LibQTip-1.0", true)
local LSM = LibStub("LibSharedMedia-3.0", true)

local ICON_PATH = "Interface\\AddOns\\CritMatic\\Media\\Textures\\critmatic-medallion.tga"
local QTIP_KEY = "CritMaticBrokerTip"

local brokerTipFont = CreateFont("CritMaticBrokerTipFont")

local function TopRecords()
    local bestCrit, bestCritName = 0, nil
    local bestHit, bestHitName = 0, nil
    local bestHealCrit, bestHealCritName = 0, nil
    local bestHeal, bestHealName = 0, nil
    for spellID, d in pairs(CritMaticData or {}) do
        if d.highestCrit and d.highestCrit > bestCrit then
            bestCrit = d.highestCrit
            bestCritName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestNormal and d.highestNormal > bestHit then
            bestHit = d.highestNormal
            bestHitName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestHealCrit and d.highestHealCrit > bestHealCrit then
            bestHealCrit = d.highestHealCrit
            bestHealCritName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestHeal and d.highestHeal > bestHeal then
            bestHeal = d.highestHeal
            bestHealName = Critmatic.GetRecordName(spellID, d)
        end
    end
    return bestCrit, bestCritName, bestHit, bestHitName, bestHealCrit, bestHealCritName, bestHeal, bestHealName
end

local function ColorHex(c, fallback)
    if type(c) ~= "table" then return fallback end
    local r = math.floor((c[1] or 1) * 255 + 0.5)
    local g = math.floor((c[2] or 1) * 255 + 0.5)
    local b = math.floor((c[3] or 1) * 255 + 0.5)
    return string.format("ff%02x%02x%02x", r, g, b)
end

local function FontColors()
    local critHex, hitHex = "ffffd700", "ffffffff"
    if Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.fontSettings then
        critHex = ColorHex(Critmatic.db.profile.fontSettings.fontColorCrit, critHex)
        hitHex = ColorHex(Critmatic.db.profile.fontSettings.fontColor, hitHex)
    end
    return critHex, hitHex
end

local function AddCompactSummary(tooltip)
    local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
    local bestCrit, bestCritName, bestHit, bestHitName, bestHealCrit, bestHealCritName, bestHeal, bestHealName = TopRecords()
    local function addCritLine()
        if bestCrit > 0 then
            tooltip:AddDoubleLine(L["broker_top_crit"], (bestCritName and (bestCrit .. "  " .. bestCritName)) or tostring(bestCrit))
        end
    end
    local function addHitLine()
        if bestHit > 0 then
            tooltip:AddDoubleLine(L["broker_top_hit"], (bestHitName and (bestHit .. "  " .. bestHitName)) or tostring(bestHit))
        end
    end
    if broker.hitFirst then
        addHitLine()
        addCritLine()
    else
        addCritLine()
        addHitLine()
    end
    if bestHealCrit > 0 then
        tooltip:AddDoubleLine(L["broker_top_crit_heal"], (bestHealCritName and (bestHealCrit .. "  " .. bestHealCritName)) or tostring(bestHealCrit))
    end
    if bestHeal > 0 then
        tooltip:AddDoubleLine(L["broker_top_heal"], (bestHealName and (bestHeal .. "  " .. bestHealName)) or tostring(bestHeal))
    end
end

local function BuildQtipTooltip(tip)
    local profile = Critmatic.db and Critmatic.db.profile
    local tfs = (profile and profile.tooltipFontSettings) or {}
    local broker = (profile and profile.brokerSettings) or {}
    local colors = tfs.colors or {}
    local grayHex = "ffd4d4d4"

    local fontPath = (LSM and tfs.font and LSM:Fetch("font", tfs.font)) or _G.STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF"
    brokerTipFont:SetFont(fontPath, tfs.fontSize or 12, tfs.fontOutline ~= "NONE" and tfs.fontOutline or "")
    tip:SetFont(brokerTipFont)
    tip:SetHeaderFont(brokerTipFont)

    local nameHex = ColorHex(colors.spellName, "ffffd700")
    local critHex = ColorHex(colors.crit, "ffffd700")
    local hitHex = ColorHex(colors.hit, "ffe5e5e5")
    local critHealHex = ColorHex(colors.critHeal, "ffffd700")
    local healHex = ColorHex(colors.heal, "ffe5e5e5")

    tip:AddLine("|cffffd700CritMatic|r")

    local entries = (Critmatic.GetGroupedSpellRecords and Critmatic.GetGroupedSpellRecords({
        sortMode = tfs.sortMode,
        groupByType = tfs.groupByType,
    })) or {}

    local cap = broker.tooltipSpellCap or 15
    local groupByType = tfs.groupByType
    local EntryKind = _G.CritMaticRecordGrouping and _G.CritMaticRecordGrouping.EntryKind

    local function isIgnored(name)
        return Critmatic.ignoredSpells and Critmatic.ignoredSpells[name:lower()]
    end

    local function renderEntry(entry, showDamage, showHealing)
        local d = entry.data
        local idForIcon = (#entry.ids > 1 and entry.ids[2]) or entry.ids[1]
        local icon = d.spellIcon or select(3, GetSpellInfo(idForIcon))
        local iconPrefix = icon and ("|T" .. icon .. ":18|t ") or ""
        tip:AddLine(iconPrefix .. "|c" .. nameHex .. entry.name .. "|r")

        if showDamage then
            if d.highestCrit and d.highestCrit > 0 then
                local oldStr = ""
                if d.highestCritOld and d.highestCritOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestCritOld .. ")|r"
                end
                tip:AddLine("  |c" .. critHex .. L["crit_log_crit"] .. " " .. d.highestCrit .. "|r" .. oldStr)
            end
            if d.highestNormal and d.highestNormal > 0 then
                local oldStr = ""
                if d.highestNormalOld and d.highestNormalOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestNormalOld .. ")|r"
                end
                tip:AddLine("  |c" .. hitHex .. L["crit_log_hit"] .. " " .. d.highestNormal .. "|r" .. oldStr)
            end
        end

        if showHealing then
            if d.highestHealCrit and d.highestHealCrit > 0 then
                local oldStr = ""
                if d.highestHealCritOld and d.highestHealCritOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestHealCritOld .. ")|r"
                end
                tip:AddLine("  |c" .. critHealHex .. L["crit_log_crit"] .. " " .. L["crit_log_heal"] .. " " .. d.highestHealCrit .. "|r" .. oldStr)
            end
            if d.highestHeal and d.highestHeal > 0 then
                local oldStr = ""
                if d.highestHealOld and d.highestHealOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestHealOld .. ")|r"
                end
                tip:AddLine("  |c" .. healHex .. L["crit_log_heal"] .. " " .. d.highestHeal .. "|r" .. oldStr)
            end
        end
    end

    local shown = 0
    local total = 0

    if groupByType and EntryKind then
        local hasDamage, hasHealing = false, false
        for _, entry in ipairs(entries) do
            if not isIgnored(entry.name) then
                local kind = EntryKind(entry.data)
                if kind.damage then hasDamage = true end
                if kind.healing then hasHealing = true end
            end
        end

        local function emitBlock(kindKey, headerKey)
            local headerShown = false
            for _, entry in ipairs(entries) do
                if not isIgnored(entry.name) and EntryKind(entry.data)[kindKey] then
                    total = total + 1
                    if shown < cap then
                        shown = shown + 1
                        if not headerShown then
                            headerShown = true
                            tip:AddHeader("|cffffd700" .. L[headerKey] .. "|r")
                        end
                        renderEntry(entry, kindKey == "damage", kindKey == "healing")
                    end
                end
            end
        end

        if tfs.groupOrder == "healing" then
            if hasHealing then emitBlock("healing", "appearance_section_healing") end
            if hasDamage then emitBlock("damage", "appearance_section_damage") end
        else
            if hasDamage then emitBlock("damage", "appearance_section_damage") end
            if hasHealing then emitBlock("healing", "appearance_section_healing") end
        end
    else
        for _, entry in ipairs(entries) do
            if not isIgnored(entry.name) then
                total = total + 1
                if shown < cap then
                    shown = shown + 1
                    renderEntry(entry, true, true)
                end
            end
        end
    end

    local remaining = total - shown
    if remaining > 0 then
        tip:AddLine("|c" .. grayHex .. string.format(L["broker_more_records"], remaining) .. "|r")
    end

    tip:AddLine("|c" .. grayHex .. L["broker_left_click_crit_log"] .. "|r")
    tip:AddLine("|c" .. grayHex .. L["broker_right_click_options"] .. "|r")
end

local function RenderFullLogTooltip(tooltip)
    local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
    tooltip:AddLine("|cffffd700CritMatic|r")
    if broker.showFullLog ~= false then
        local critHex, hitHex = FontColors()
        local grayHex = "ffd4d4d4"
        local entries = (Critmatic.GetGroupedSpellRecords and Critmatic.GetGroupedSpellRecords()) or {}
        local cap = broker.tooltipSpellCap or 15
        local shown, total = 0, 0
        for _, entry in ipairs(entries) do
            local name = entry.name
            if not (Critmatic.ignoredSpells and Critmatic.ignoredSpells[name:lower()]) then
                total = total + 1
                if shown < cap then
                    shown = shown + 1
                    local d = entry.data
                    local idForIcon = (#entry.ids > 1 and entry.ids[2]) or entry.ids[1]
                    local icon = d.spellIcon or select(3, GetSpellInfo(idForIcon))
                    local nameLine = "|cffffd700" .. name .. "|r"
                    if icon then
                        nameLine = "|T" .. icon .. ":18|t " .. nameLine
                    end
                    tooltip:AddLine(nameLine)
                    if d.highestCrit and d.highestCrit > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", critHex, L["crit_log_crit"], d.highestCrit, grayHex, L["crit_log_old"], d.highestCritOld or 0))
                    end
                    if d.highestNormal and d.highestNormal > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", hitHex, L["crit_log_hit"], d.highestNormal, grayHex, L["crit_log_old"], d.highestNormalOld or 0))
                    end
                    if d.highestHealCrit and d.highestHealCrit > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s %s|r |c%s(%s %s)|r", critHex, L["crit_log_crit"], L["crit_log_heal"], d.highestHealCrit, grayHex, L["crit_log_old"], d.highestHealCritOld or 0))
                    end
                    if d.highestHeal and d.highestHeal > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", hitHex, L["crit_log_heal"], d.highestHeal, grayHex, L["crit_log_old"], d.highestHealOld or 0))
                    end
                end
            end
        end
        if total > shown then
            tooltip:AddLine(string.format(L["broker_more_records"], total - shown), 0.8, 0.8, 0.8)
        end
    else
        AddCompactSummary(tooltip)
    end
    tooltip:AddLine(L["broker_left_click_crit_log"], 0.8, 0.8, 0.8)
    tooltip:AddLine(L["broker_right_click_options"], 0.8, 0.8, 0.8)
end

if ldb then
    Critmatic.brokerObject = ldb:NewDataObject("CritMatic", {
        type = "data source",
        text = "CritMatic",
        icon = ICON_PATH,
        OnClick = function(_, button)
            if button == "RightButton" then
                Critmatic:OpenOptions()
            else
                Critmatic:OpenCritLog()
            end
        end,
        OnEnter = function(self)
            local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
            local ok = false
            if qtip and broker.showFullLog ~= false then
                ok = pcall(function()
                    local tip = qtip:Acquire(QTIP_KEY, 1, "LEFT")
                    BuildQtipTooltip(tip)
                    tip:SmartAnchorTo(self)
                    tip:Show()
                    Critmatic.brokerObject._activeTip = tip
                end)
            end
            if not ok then
                GameTooltip:SetOwner(self, "ANCHOR_NONE")
                GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -4)
                GameTooltip:ClearLines()
                RenderFullLogTooltip(GameTooltip)
                GameTooltip:Show()
            end
        end,
        OnLeave = function(self)
            local tip = Critmatic.brokerObject._activeTip
            if tip then
                qtip:Release(tip)
                Critmatic.brokerObject._activeTip = nil
            else
                GameTooltip:Hide()
            end
        end,
    })
end

function Critmatic.UpdateBroker()
    if not Critmatic.brokerObject then return end
    local bestCrit, _, bestHit = TopRecords()
    local critHex, hitHex = "ffffd700", "ffffffff"
    local profile = Critmatic.db and Critmatic.db.profile
    if profile then
        local bcs = profile.brokerColorSettings
        if bcs and bcs.colors then
            critHex = ColorHex(bcs.colors.crit, critHex)
            hitHex = ColorHex(bcs.colors.value, hitHex)
        elseif profile.fontSettings then
            critHex = ColorHex(profile.fontSettings.fontColorCrit, critHex)
            hitHex = ColorHex(profile.fontSettings.fontColor, hitHex)
        end
    end
    local broker = (profile and profile.brokerSettings) or {}

    local critLabel, hitLabel, separator
    if broker.compactNumbers then
        critLabel, hitLabel, separator = "", "", "/"
    elseif broker.compactInitials then
        critLabel, hitLabel, separator = L["broker_crit_initial"] .. ":", L["broker_hit_initial"] .. ":", " "
    elseif broker.compactLabels then
        critLabel, hitLabel, separator = L["broker_crit_short"] .. ": ", L["broker_hit_short"] .. ": ", "  "
    else
        critLabel, hitLabel, separator = L["broker_top_crit"] .. ": ", L["broker_top_hit"] .. ": ", "  "
    end

    local critPart, hitPart
    if bestCrit and bestCrit > 0 then
        critPart = "|c" .. critHex .. critLabel .. bestCrit .. "|r"
    end
    if bestHit and bestHit > 0 then
        hitPart = "|c" .. hitHex .. hitLabel .. bestHit .. "|r"
    end

    local first, second
    if broker.hitFirst then
        first, second = hitPart, critPart
    else
        first, second = critPart, hitPart
    end

    local parts = {}
    if first then parts[#parts + 1] = first end
    if second then parts[#parts + 1] = second end

    if #parts > 0 then
        Critmatic.brokerObject.text = table.concat(parts, separator)
    else
        Critmatic.brokerObject.text = "CritMatic"
    end
end

function Critmatic.SetupMinimapIcon()
    if not dbicon or not Critmatic.brokerObject then return end
    if not Critmatic.db or not Critmatic.db.profile then return end
    Critmatic.db.profile.minimap = Critmatic.db.profile.minimap or { hide = false }
    if not dbicon:IsRegistered("CritMatic") then
        dbicon:Register("CritMatic", Critmatic.brokerObject, Critmatic.db.profile.minimap)
    elseif dbicon.Refresh then
        dbicon:Refresh("CritMatic", Critmatic.db.profile.minimap)
    end
    if Critmatic.db.profile.minimap.hide then
        dbicon:Hide("CritMatic")
    else
        dbicon:Show("CritMatic")
    end
end

function Critmatic.SetMinimapHidden(hide)
    if not Critmatic.db or not Critmatic.db.profile then return end
    Critmatic.db.profile.minimap = Critmatic.db.profile.minimap or { hide = false }
    Critmatic.db.profile.minimap.hide = hide and true or false
    if not dbicon then return end
    if hide then
        dbicon:Hide("CritMatic")
    else
        dbicon:Show("CritMatic")
    end
end
