local ADDON_NAME, OneWoW = ...

local _, playerClass = UnitClass("player")
local classColor = RAID_CLASS_COLORS[playerClass]
local classColorHex = classColor and classColor.colorStr or "ffffffff"

local SPELL_NAME_COLOR = "ffffffff"
local TALENT_NAME_COLOR = classColorHex

local spellModifierCache = {}

local talentsMissingName = {
    [47541] = { [444072] = true },
    [207317] = { [444072] = true },
}

local blacklistedTalents = {
    [339] = { [33891] = true },
    [102693] = { [393371] = true },
    [8936] = { [33891] = true },
    [774] = { [33891] = true },
    [48438] = { [33891] = true },
    [5176] = { [33891] = true },
    [124682] = { [116680] = true, [388491] = true },
    [191837] = { [116680] = true, [388491] = true },
    [322101] = { [116680] = true, [388491] = true },
    [115151] = { [116680] = true, [388491] = true },
    [107428] = { [116680] = true, [388491] = true },
    [116670] = { [116680] = true, [388491] = true },
    [188443] = { [262303] = true },
    [188389] = { [262303] = true, [378270] = true, [114050] = true },
    [196840] = { [262303] = true },
    [51505] = { [262303] = true },
    [188196] = { [262303] = true },
}

local modifierNameExceptions = {
    ["Slam"] = { "Shield Slam" },
    ["Meteor"] = { "Meteorite" },
}

local modifierInheritance = {
    [1242174] = 47541,
    [199786] = 116,
    [200758] = 53,
    [388667] = 686,
    [431443] = 361469,
    [443454] = 378081,
    [467307] = 107428,
}

local function GetSettings()
    local db = OneWoW.db and OneWoW.db.global and OneWoW.db.global.settings
    if not db or not db.tooltips then return nil end
    return db.tooltips.talentmods
end

local function isBaseCoveredByException(s, e, text, exList)
    if not exList then return false end
    for _, full in ipairs(exList) do
        local init = 1
        while true do
            local s_full, e_full = string.find(text, full, init, true)
            if not s_full then break end
            if s >= s_full and e <= e_full then return true end
            init = e_full + 1
        end
    end
    return false
end

local function hasUnexceptedMatch(baseName, text)
    if not baseName or not text then return false end
    local exceptions = modifierNameExceptions[baseName]
    local init = 1
    while true do
        local s, e = string.find(text, baseName, init, true)
        if not s then break end
        if not isBaseCoveredByException(s, e, text, exceptions) then
            return true
        end
        init = e + 1
    end
    return false
end

local function colorText(text, color)
    return "|c" .. color .. text .. "|r"
end

local function prettifyText(text, spellName)
    if not text or not spellName then return text end
    local exceptions = modifierNameExceptions[spellName]
    local parts = {}
    local last = 1
    local init = 1
    while true do
        local s, e = string.find(text, spellName, init, true)
        if not s then break end
        if not isBaseCoveredByException(s, e, text, exceptions) then
            if s > last then table.insert(parts, text:sub(last, s - 1)) end
            table.insert(parts, colorText(text:sub(s, e), SPELL_NAME_COLOR))
            last = e + 1
        end
        init = e + 1
    end
    if last <= #text then table.insert(parts, text:sub(last)) end
    return table.concat(parts)
end

local function populateSpellModifiers(spellID)
    if spellModifierCache[spellID] then return end

    local configID = C_ClassTalents.GetActiveConfigID()
    if not configID then return end

    local configInfo = C_Traits.GetConfigInfo(configID)
    if not configInfo then return end

    local settings = GetSettings()
    local includeActive = settings and settings.includeActive

    for _, treeID in ipairs(configInfo.treeIDs) do
        local nodes = C_Traits.GetTreeNodes(treeID)
        if nodes then
            for _, nodeID in ipairs(nodes) do
                local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID)
                if nodeInfo then
                    for _, entryID in ipairs(nodeInfo.entryIDs) do
                        local entryInfo = C_Traits.GetEntryInfo(configID, entryID)
                        if entryInfo and entryInfo.definitionID then
                            local definitionInfo = C_Traits.GetDefinitionInfo(entryInfo.definitionID)
                            if definitionInfo and definitionInfo.spellID and C_SpellBook.IsSpellKnown(definitionInfo.spellID) then
                                local knownTalentID = definitionInfo.spellID
                                if knownTalentID and spellID ~= knownTalentID and (C_Spell.IsSpellPassive(knownTalentID) or includeActive) then
                                    local isNotBlacklisted = not (blacklistedTalents[spellID] and blacklistedTalents[spellID][knownTalentID])
                                    local isTalentMissingName = talentsMissingName[spellID] and talentsMissingName[spellID][knownTalentID]

                                    if isNotBlacklisted then
                                        local spell = C_Spell.GetSpellInfo(spellID)
                                        local knownTalent = Spell:CreateFromSpellID(knownTalentID)

                                        knownTalent:ContinueOnSpellLoad(function()
                                            local talentName = knownTalent:GetSpellName()
                                            local talentDesc = knownTalent:GetSpellDescription()

                                            if spell and spell.name then
                                                if isTalentMissingName or hasUnexceptedMatch(spell.name, talentDesc) then
                                                    talentDesc = prettifyText(talentDesc, spell.name)
                                                    talentDesc = "\n" .. colorText(talentName, TALENT_NAME_COLOR) .. "\n" .. talentDesc
                                                    spellModifierCache[spellID] = spellModifierCache[spellID] or {}
                                                    spellModifierCache[spellID][knownTalentID] = talentDesc
                                                end
                                            end
                                        end)
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

local function getCachedModifiers(spellID)
    populateSpellModifiers(spellID)
    return spellModifierCache[spellID] or {}
end

local function OnSpellTooltip(tooltip, data)
    if not OneWoW.TooltipEngine or not OneWoW.TooltipEngine:IsFeatureEnabled("talentmods") then return end
    if not data or not data.lines or not data.type then return end
    if data.type ~= Enum.TooltipDataType.Spell and data.type ~= Enum.TooltipDataType.Macro then return end

    local settings = GetSettings()
    if not settings then return end

    if settings.hideInCombat and UnitAffectingCombat("player") then return end

    local spellID = (data.type == Enum.TooltipDataType.Macro and data.lines[1] and data.lines[1].tooltipID) or data.id
    if not spellID or issecretvalue(spellID) then return end

    local inheritedModifiers = {}
    local baseSpellID = modifierInheritance[spellID]
    if baseSpellID then
        inheritedModifiers = getCachedModifiers(baseSpellID)
    end

    local modifierTooltips = getCachedModifiers(spellID)
    for _, tooltipText in pairs(modifierTooltips) do
        tooltip:AddLine(tooltipText, nil, nil, nil, true)
    end

    if baseSpellID then
        local hasInherited = false
        for modifierID, tooltipText in pairs(inheritedModifiers) do
            if not modifierTooltips[modifierID] then
                if not hasInherited then
                    local baseSpellInfo = C_Spell.GetSpellInfo(baseSpellID)
                    local baseSpellName = baseSpellInfo and baseSpellInfo.name
                    if baseSpellName then
                        local fromFmt = OneWoW.L and OneWoW.L["TIPS_TALENTMODS_FROM"] or "From %s:"
                        tooltip:AddLine("\n" .. string.format(fromFmt, colorText(baseSpellName, SPELL_NAME_COLOR)))
                    end
                    hasInherited = true
                end
                tooltip:AddLine(tooltipText, nil, nil, nil, true)
            end
        end
    end

end

C_Timer.After(2, function()
    if TooltipDataProcessor and TooltipDataProcessor.AddTooltipPostCall then
        TooltipDataProcessor.AddTooltipPostCall(TooltipDataProcessor.AllTypes, OnSpellTooltip)
    end

    local frame = CreateFrame("Frame")
    frame:RegisterEvent("SPELLS_CHANGED")
    frame:SetScript("OnEvent", function()
        wipe(spellModifierCache)
    end)
end)
