local AddonName, addon = ...
addon = addon or {}
_G["Fontify"] = addon

local CreateFrame = CreateFrame
local C_NamePlate = C_NamePlate
local UnitIsPlayer = UnitIsPlayer
local UnitIsFriend = UnitIsFriend
local UnitPlayerControlled = UnitPlayerControlled
local UnitReaction = UnitReaction
local UnitCanAttack = UnitCanAttack
local UnitClass = UnitClass
local UnitName = UnitName
local UnitIsUnit = UnitIsUnit
local GetGuildInfo = GetGuildInfo
local UnitExists = UnitExists
local C_CVar = C_CVar
local RAID_CLASS_COLORS = RAID_CLASS_COLORS
local FACTION_BAR_COLORS = FACTION_BAR_COLORS
local STANDARD_TEXT_FONT = STANDARD_TEXT_FONT
local pairs, ipairs, type, pcall = pairs, ipairs, type, pcall
local string_split = string.split

local FONT_DIR = "Interface/AddOns/Fontify/fonts/"

addon.DefaultFonts = {
    ["Friz Quadrata"]          = FONT_DIR .. "FRIZQT__.TTF",
    ["Arial Narrow"]           = FONT_DIR .. "ARIALN.TTF",
    ["Diablo Heavy"]           = FONT_DIR .. "DiabloHeavy.ttf",
    ["Harry Potter"]           = FONT_DIR .. "HARRYP__.TTF",
    ["Expressway"]             = FONT_DIR .. "Expressway.TTF",
    ["Expressway Mono"]        = FONT_DIR .. "ExpresswayMonoNum.ttf",
    ["Accidental Presidency"]  = FONT_DIR .. "AccidentalPresidency.ttf",
    ["Carlito"]                = FONT_DIR .. "Carlito-Regular.ttf",
    ["Forced Square"]          = FONT_DIR .. "ForcedSquare.ttf",
    ["Nueva Std Cond"]         = FONT_DIR .. "NuevaStdCond.ttf",
    ["Oswald"]                 = FONT_DIR .. "Oswald-Regular.ttf",
    ["TrashHand"]              = FONT_DIR .. "TrashHand.TTF",
    ["El Messiri"]             = FONT_DIR .. "ElMessiri.ttf",
    ["PT Sans Narrow"]         = FONT_DIR .. "PTSansNarrow-Bold.ttf",
    ["Prototype"]              = FONT_DIR .. "Prototype.ttf",
    ["Yanone Kaffeesatz"]      = FONT_DIR .. "YanoneKaffeesatz-Medium.ttf",
    ["Lato"]                   = FONT_DIR .. "Lato-Regular.ttf",
    ["Poppins"]                = FONT_DIR .. "Poppins-SemiBold.ttf",
    ["Roboto Condensed"]       = FONT_DIR .. "RobotoCondensed-Bold.ttf",
}

function addon.GetFontList()
    local list = {}
    for name in pairs(addon.DefaultFonts) do
        table.insert(list, name)
    end
    table.sort(list)
    return list
end

function addon.GetFontPath(fontName)
    if fontName and addon.DefaultFonts[fontName] then
        return addon.DefaultFonts[fontName]
    end
    return FONT_DIR .. "FRIZQT__.TTF"
end

local function SafeSetFont(fontString, fontPath, fontSize, fontOutline)
    if not fontString or not fontString.SetFont then return end
    fontOutline = fontOutline or ""
    if fontOutline == "NONE" then fontOutline = "" end
    local ok = pcall(function()
        fontString:SetFont(fontPath, fontSize, fontOutline)
    end)
    if not ok then
        pcall(function()
            fontString:SetFont(FONT_DIR .. "FRIZQT__.TTF", fontSize, fontOutline)
        end)
    end
end

addon.defaultDB = {
    profile = "Default",
    profiles = {
        ["Default"] = {
            hideRealm = false,
            enableTitles = false,
            enableFriendlyNPCPlates = false,
            headlineMode = false,
            globalFontOverride = false,
            globalFont = "Friz Quadrata",
            globalOutline = "NONE",
            globalAlignment = "LEFT",
            globalShadow = false,
            globalSize = 13,
            globalTitleSize = 10,
            globalUseClassColors = false,
            globalUseReactionColors = false,
            self = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=0.8,g=0.8,b=0.8},
            },
            friendlyPlayer = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=0.3,g=0.9,b=0.3},
            },
            hostilePlayer = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=1,g=0.82,b=0.2},
            },
            friendlyNPC = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=1,g=0.82,b=0},
            },
            interactableNPC = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=1,g=0.82,b=0},
            },
            hostileNPC = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=0.8,g=0.8,b=0.8},
            },
            neutralNPC = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 10, titleColor = {r=1,g=0.82,b=0},
            },
            pet = {
                font = "Friz Quadrata", size = 11, outline = "NONE", alignment = "LEFT",
                offsetY = 0, enableShadow = false, headlineMode = false, colorMode = "custom",
                customColor = {r=1,g=1,b=1}, showTitle = false, titleSize = 9, titleColor = {r=0.7,g=0.7,b=0.7},
            },
            questWindow = {
                font = "Friz Quadrata", size = 13, outline = "NONE", alignment = "LEFT",
                enableShadow = false, titleSize = 15,
            },
        }
    }
}

function addon.ApplyMasterFontToAll(fontName, fontSize, fontOutline, subSize, alignment, shadow, useClassColors, useReactionColors)
    local db = addon:GetProfile()
    db.globalFont = fontName or db.globalFont
    if fontSize then db.globalSize = fontSize end
    if fontOutline then db.globalOutline = fontOutline end
    if subSize then db.globalTitleSize = subSize end
    if alignment then db.globalAlignment = alignment end
    if shadow ~= nil then db.globalShadow = shadow end
    if useClassColors ~= nil then db.globalUseClassColors = useClassColors end
    if useReactionColors ~= nil then db.globalUseReactionColors = useReactionColors end

    local categories = {"self","friendlyPlayer","hostilePlayer","friendlyNPC","interactableNPC","hostileNPC","neutralNPC","pet","questWindow"}
    for _, catKey in ipairs(categories) do
        if db[catKey] then
            db[catKey].font = db.globalFont
            if fontSize then db[catKey].size = fontSize end
            if fontOutline then db[catKey].outline = fontOutline end
            if subSize then db[catKey].titleSize = subSize end
            if alignment then db[catKey].alignment = db.globalAlignment end
            if shadow ~= nil then db[catKey].enableShadow = db.globalShadow end
            if catKey ~= "questWindow" then
                if catKey == "friendlyPlayer" or catKey == "hostilePlayer" or catKey == "self" then
                    if useClassColors == true then db[catKey].colorMode = "class"
                    elseif useClassColors == false then db[catKey].colorMode = "custom" end
                else
                    if useReactionColors == true then db[catKey].colorMode = "reaction"
                    elseif useReactionColors == false then db[catKey].colorMode = "custom" end
                end
            end
        end
    end
    addon:RefreshAllNameplates()
    if type(addon.RefreshCategoryView) == "function" then addon.RefreshCategoryView() end
    print("|cff00ff00[Fontify]|r Applied Master settings across all categories!")
end

local tooltipScanner = CreateFrame("GameTooltip", "FontifyTooltipScanner", nil, "GameTooltipTemplate")
tooltipScanner:SetOwner(WorldFrame, "ANCHOR_NONE")

local function GetNPCTitle(unit)
    if not unit or not UnitExists(unit) then return nil end
    tooltipScanner:ClearLines()
    tooltipScanner:SetUnit(unit)
    local numLines = tooltipScanner:NumLines()
    if numLines >= 2 then
        local line2 = _G["FontifyTooltipScannerTextLeft2"]
        if line2 then
            local text = line2:GetText()
            if text and text ~= "" and not text:find("^Level") and not text:find("^PvP") then
                return text
            end
        end
    end
    return nil
end

local function IsInteractableNPC(unit)
    if not unit or not UnitExists(unit) or UnitIsPlayer(unit) then return false end
    if UnitCanAttack and UnitCanAttack("player", unit) then return false end
    local title = GetNPCTitle(unit)
    return title and title ~= ""
end

function addon:GetProfile()
    if not FontifyDB or not FontifyDB.profiles then return addon.defaultDB.profiles["Default"] end
    local profName = FontifyDB.profile or "Default"
    if not FontifyDB.profiles[profName] then
        FontifyDB.profiles[profName] = addon.CopyTable(addon.defaultDB.profiles["Default"])
    end
    return FontifyDB.profiles[profName]
end

function addon.CopyTable(src)
    if type(src) ~= "table" then return src end
    local dest = {}
    for k, v in pairs(src) do dest[k] = addon.CopyTable(v) end
    return dest
end

function addon:ResetProfile()
    if not FontifyDB or not FontifyDB.profiles then return end
    local profName = FontifyDB.profile or "Default"
    FontifyDB.profiles[profName] = addon.CopyTable(addon.defaultDB.profiles["Default"])
    addon:ApplySystemFonts()
    addon:ApplyQuestFonts()
    if C_CVar and C_CVar.SetCVar then
        C_CVar.SetCVar("nameplateShowFriendlyNPCs", "0")
        C_CVar.SetCVar("nameplateShowOnlyNames", "0")
    end
    addon:RefreshAllNameplates()
    print("|cff00ff00[Fontify]|r Reset all settings to default un-modified state!")
end

function addon.GetProfilesList()
    local list = {}
    if FontifyDB and FontifyDB.profiles then
        for name in pairs(FontifyDB.profiles) do table.insert(list, name) end
    end
    table.sort(list)
    if #list == 0 then table.insert(list, "Default") end
    return list
end

function addon.SaveProfile(name)
    if not name or name == "" then return end
    if not FontifyDB or not FontifyDB.profiles then return end
    FontifyDB.profiles[name] = addon.CopyTable(addon:GetProfile())
    FontifyDB.profile = name
    local charName, charRealm = UnitName("player"), GetRealmName()
    if charName and charRealm then
        FontifyDB.characterProfiles = FontifyDB.characterProfiles or {}
        FontifyDB.characterProfiles[charName .. "-" .. charRealm] = name
    end
    addon:RefreshAllNameplates()
    if type(addon.RefreshCategoryView) == "function" then addon.RefreshCategoryView() end
    print("|cff00ff00[Fontify]|r Saved profile: " .. name)
end

function addon.CreateDefaultProfile(name)
    if not name or name == "" then return end
    if not FontifyDB or not FontifyDB.profiles then return end
    FontifyDB.profiles[name] = addon.CopyTable(addon.defaultDB.profiles["Default"])
    FontifyDB.profile = name
    local charName, charRealm = UnitName("player"), GetRealmName()
    if charName and charRealm then
        FontifyDB.characterProfiles = FontifyDB.characterProfiles or {}
        FontifyDB.characterProfiles[charName .. "-" .. charRealm] = name
    end
    addon:RefreshAllNameplates()
    if type(addon.RefreshCategoryView) == "function" then addon.RefreshCategoryView() end
    print("|cff00ff00[Fontify]|r Created new profile: " .. name)
end

function addon.LoadProfile(name)
    if not name or name == "" then return end
    if not FontifyDB or not FontifyDB.profiles or not FontifyDB.profiles[name] then return end
    FontifyDB.profile = name
    local charName, charRealm = UnitName("player"), GetRealmName()
    if charName and charRealm then
        FontifyDB.characterProfiles = FontifyDB.characterProfiles or {}
        FontifyDB.characterProfiles[charName .. "-" .. charRealm] = name
    end
    addon:RefreshAllNameplates()
    if type(addon.RefreshCategoryView) == "function" then addon.RefreshCategoryView() end
    print("|cff00ff00[Fontify]|r Loaded profile: " .. name)
end

function addon.DeleteProfile(name)
    if not name or name == "" then return end
    if name == "Default" then print("|cffff5555[Fontify]|r Cannot delete the Default profile.") return end
    if not FontifyDB or not FontifyDB.profiles or not FontifyDB.profiles[name] then return end
    FontifyDB.profiles[name] = nil
    if FontifyDB.profile == name then
        addon.LoadProfile("Default")
    else
        if type(addon.RefreshCategoryView) == "function" then addon.RefreshCategoryView() end
    end
    print("|cff00ff00[Fontify]|r Deleted profile: " .. name)
end

local function GetUnitCategory(unit)
    if not unit or not UnitExists(unit) then return "friendlyNPC" end
    if UnitIsUnit(unit, "player") then return "self" end
    local isPlayer = UnitIsPlayer(unit)
    local isPet = UnitPlayerControlled(unit) and not isPlayer
    if isPet then return "pet"
    elseif isPlayer then
        return UnitIsFriend("player", unit) and "friendlyPlayer" or "hostilePlayer"
    else
        if IsInteractableNPC(unit) then return "interactableNPC" end
        local reaction = UnitReaction("player", unit) or 4
        if reaction >= 5 then return "friendlyNPC"
        elseif reaction == 4 then return "neutralNPC"
        else return "hostileNPC" end
    end
end

local function GetNameFontStrings(nameplate)
    if not nameplate then return {}, nil, nil end
    local fontStrings = {}
    local UnitFrame = nameplate.UnitFrame or nameplate.unitFrame or nameplate
    local unit = nameplate.namePlateUnitToken or nameplate.unit or (UnitFrame and UnitFrame.unit)
    if UnitFrame then
        local targets = {"name","Name","actorName","nameText","fakeName","BBPname","bbpName","BBPtitle","bbpTitle","guildName","npcTitle","title","Title","subtitle","Subtitle"}
        for _, key in ipairs(targets) do
            if UnitFrame[key] and type(UnitFrame[key].SetFont) == "function" then
                table.insert(fontStrings, UnitFrame[key])
            end
        end
        if UnitFrame.healthBar and type(UnitFrame.healthBar) == "table" and UnitFrame.healthBar.name and type(UnitFrame.healthBar.name.SetFont) == "function" then
            table.insert(fontStrings, UnitFrame.healthBar.name)
        end
    end
    if nameplate.name and type(nameplate.name.SetFont) == "function" and not (UnitFrame and nameplate.name == UnitFrame.name) then
        table.insert(fontStrings, nameplate.name)
    end
    return fontStrings, UnitFrame, unit
end

function addon:ApplyQuestFonts()
    local db = addon:GetProfile()
    local cfg = db.questWindow or {font="Friz Quadrata",size=13,outline="NONE"}
    local targetFont = (db.globalFontOverride and db.globalFont) or cfg.font or "Friz Quadrata"
    local fontPath = addon.GetFontPath(targetFont)
    local fontSize = (db.globalFontOverride and db.globalSize) or cfg.size or 13
    local fontOutline = (db.globalFontOverride and db.globalOutline) or cfg.outline or "NONE"
    local questFontObjects = {
        _G["QuestFont"], _G["QuestFont_Large"], _G["QuestFont_Huge"],
        _G["QuestFont_Super_Huge"], _G["QuestFont_Shadow_Huge"], _G["QuestFont_Shadow_Small"],
        _G["QuestFont_Enchant"], _G["GossipNormalFont"], _G["GossipTitleFont"], _G["DialogButtonNormalText"],
    }
    for _, fontObj in ipairs(questFontObjects) do
        SafeSetFont(fontObj, fontPath, fontSize, fontOutline)
    end
end

function addon:ApplySystemFonts()
    local db = addon:GetProfile()
    local targetFont = (db.globalFontOverride and db.globalFont) or "Friz Quadrata"
    local fontPath = addon.GetFontPath(targetFont)
    local fontSize = (db.globalFontOverride and db.globalSize) or 13
    local fontOutline = (db.globalFontOverride and db.globalOutline) or "NONE"
    _G["UNIT_NAME_FONT"] = fontPath
    _G["NAMEPLATE_FONT"] = fontPath
    for k, fontObj in pairs(_G) do
        if type(k) == "string" and (k:find("^SystemFont_") or k:find("^GameFont")) and type(fontObj) == "table" and type(fontObj.SetFont) == "function" then
            SafeSetFont(fontObj, fontPath, fontSize, fontOutline)
        end
    end
end

function addon:FormatNameplate(nameplate)
    if not nameplate then return end
    local fontStrings, UnitFrame, unit = GetNameFontStrings(nameplate)
    if not unit or not UnitExists(unit) or #fontStrings == 0 then return end

    local db = addon:GetProfile()
    local categoryKey = GetUnitCategory(unit)
    local cfg = db[categoryKey] or db.friendlyNPC

    local isCustom = db.globalFontOverride
        or (cfg.font and cfg.font ~= "Friz Quadrata")
        or (cfg.outline and cfg.outline ~= "NONE")
        or (cfg.size and cfg.size ~= 13)
        or (cfg.colorMode and cfg.colorMode ~= "custom")
        or cfg.enableShadow or cfg.headlineMode or db.headlineMode
        or (cfg.customColor and (cfg.customColor.r ~= 1 or cfg.customColor.g ~= 1 or cfg.customColor.b ~= 1))
        or (cfg.alignment and cfg.alignment ~= "LEFT")
        or db.hideRealm or db.enableTitles
        or db.globalUseClassColors or db.globalUseReactionColors

    if not isCustom then
        if UnitFrame then
            if UnitFrame.healthBar and type(UnitFrame.healthBar.Show) == "function" then UnitFrame.healthBar:Show() end
            if UnitFrame.HealthBar and type(UnitFrame.HealthBar.Show) == "function" then UnitFrame.HealthBar:Show() end
            if UnitFrame.fontifyTitle then UnitFrame.fontifyTitle:Hide() end
        end
        for _, nameString in ipairs(fontStrings) do
            SafeSetFont(nameString, FONT_DIR .. "FRIZQT__.TTF", 13, "")
            if nameString.SetJustifyH then nameString:SetJustifyH("LEFT") end
            nameString:SetShadowOffset(0, 0)
        end
        return
    end

    local isHeadline = cfg.headlineMode or db.headlineMode
    if UnitFrame then
        if isHeadline then
            if UnitFrame.healthBar and type(UnitFrame.healthBar.Hide) == "function" then UnitFrame.healthBar:Hide() end
            if UnitFrame.HealthBar and type(UnitFrame.HealthBar.Hide) == "function" then UnitFrame.HealthBar:Hide() end
            if UnitFrame.SelectionFrame and type(UnitFrame.SelectionFrame.Hide) == "function" then UnitFrame.SelectionFrame:Hide() end
        else
            if UnitFrame.healthBar and type(UnitFrame.healthBar.Show) == "function" then UnitFrame.healthBar:Show() end
            if UnitFrame.HealthBar and type(UnitFrame.HealthBar.Show) == "function" then UnitFrame.HealthBar:Show() end
        end
    end

    local targetFont = (db.globalFontOverride and db.globalFont) or cfg.font or "Friz Quadrata"
    local fontPath = addon.GetFontPath(targetFont)
    local fontSize = (db.globalFontOverride and db.globalSize) or cfg.size or 13
    local fontOutline = (db.globalFontOverride and db.globalOutline) or cfg.outline or "NONE"

    local rawName = UnitName(unit)
    if rawName and db.hideRealm and UnitIsPlayer(unit) then
        rawName = string_split("-", rawName)
    end

    local colorMode = cfg.colorMode
    if UnitIsPlayer(unit) and db.globalUseClassColors then colorMode = "class"
    elseif not UnitIsPlayer(unit) and db.globalUseReactionColors then colorMode = "reaction" end

    local r, g, b = 1, 1, 1
    if colorMode == "class" and UnitIsPlayer(unit) then
        local _, classFilename = UnitClass(unit)
        if classFilename and RAID_CLASS_COLORS[classFilename] then
            local c = RAID_CLASS_COLORS[classFilename]
            r, g, b = c.r, c.g, c.b
        end
    elseif colorMode == "reaction" then
        local reaction = UnitReaction("player", unit) or 4
        if FACTION_BAR_COLORS and FACTION_BAR_COLORS[reaction] then
            local c = FACTION_BAR_COLORS[reaction]
            r, g, b = c.r, c.g, c.b
        end
    elseif cfg.customColor and colorMode == "custom" then
        r, g, b = cfg.customColor.r or 1, cfg.customColor.g or 1, cfg.customColor.b or 1
    end

    for _, nameString in ipairs(fontStrings) do
        SafeSetFont(nameString, fontPath, fontSize, fontOutline)
        if colorMode ~= "custom" then nameString:SetTextColor(r, g, b) end
        if rawName and db.hideRealm then nameString:SetText(rawName) end
        local align = (db.globalFontOverride and db.globalAlignment) or cfg.alignment or "LEFT"
        if align and nameString.SetJustifyH then nameString:SetJustifyH(align) end
        local shadow = (db.globalFontOverride and db.globalShadow) or cfg.enableShadow
        if shadow then
            nameString:SetShadowColor(0, 0, 0, 0.8)
            nameString:SetShadowOffset(1, -1)
        else
            nameString:SetShadowOffset(0, 0)
        end
        if not nameString.fontifyHooked then
            nameString.fontifyHooked = true
            hooksecurefunc(nameString, "SetFontObject", function(self)
                if self.fontifyUpdating then return end
                self.fontifyUpdating = true
                local p = addon:GetProfile()
                local cat = GetUnitCategory(unit)
                local c = p[cat] or p.friendlyNPC
                local fFont = (p.globalFontOverride and p.globalFont) or c.font or "Friz Quadrata"
                local fPath = addon.GetFontPath(fFont)
                local fSize = (p.globalFontOverride and p.globalSize) or c.size or 13
                local fOutline = (p.globalFontOverride and p.globalOutline) or c.outline or "NONE"
                local fAlign = (p.globalFontOverride and p.globalAlignment) or c.alignment or "LEFT"
                local fShadow = (p.globalFontOverride and p.globalShadow) or c.enableShadow
                SafeSetFont(self, fPath, fSize, fOutline)
                if self.SetJustifyH then self:SetJustifyH(fAlign) end
                if fShadow then
                    self:SetShadowColor(0, 0, 0, 0.8)
                    self:SetShadowOffset(1, -1)
                else
                    self:SetShadowOffset(0, 0)
                end
                self.fontifyUpdating = false
            end)
            hooksecurefunc(nameString, "SetJustifyH", function(self)
                if self.fontifyAlignLocked then return end
                local p = addon:GetProfile()
                local cat = GetUnitCategory(unit)
                local c = p[cat] or p.friendlyNPC
                local fAlign = (p.globalFontOverride and p.globalAlignment) or c.alignment or "LEFT"
                if self:GetJustifyH() ~= fAlign then
                    self.fontifyAlignLocked = true
                    self:SetJustifyH(fAlign)
                    self.fontifyAlignLocked = false
                end
            end)
        end
    end

    local mainFS = fontStrings[1]
    local showTitleText = db.enableTitles or cfg.showTitle
    if showTitleText and mainFS and UnitFrame then
        if not UnitFrame.fontifyTitle then
            UnitFrame.fontifyTitle = UnitFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            UnitFrame.fontifyTitle:SetPoint("TOP", mainFS, "BOTTOM", 0, -2)
        end
        local titleText = nil
        if UnitIsPlayer(unit) then
            local guildName = GetGuildInfo(unit)
            if guildName then titleText = "<" .. guildName .. ">" end
        else
            titleText = GetNPCTitle(unit)
        end
        if titleText and titleText ~= "" then
            local tColor = cfg.titleColor or {r=1,g=0.82,b=0}
            local tSize = (db.globalFontOverride and db.globalTitleSize) or cfg.titleSize or math.max(8, fontSize - 3)
            SafeSetFont(UnitFrame.fontifyTitle, fontPath, tSize, fontOutline)
            UnitFrame.fontifyTitle:SetTextColor(tColor.r, tColor.g, tColor.b)
            UnitFrame.fontifyTitle:SetText(titleText)
            UnitFrame.fontifyTitle:Show()
        else
            if UnitFrame.fontifyTitle then UnitFrame.fontifyTitle:Hide() end
        end
    else
        if UnitFrame and UnitFrame.fontifyTitle then UnitFrame.fontifyTitle:Hide() end
    end
end

function addon:RefreshAllNameplates()
    local db = addon:GetProfile()
    if C_CVar and C_CVar.SetCVar then
        if db.enableFriendlyNPCPlates == true then
            C_CVar.SetCVar("nameplateShowFriendlyNPCs", "1")
        else
            C_CVar.SetCVar("nameplateShowFriendlyNPCs", "0")
        end
    end
    addon:ApplySystemFonts()
    addon:ApplyQuestFonts()
    if not C_NamePlate then return end
    local nameplates = C_NamePlate.GetNamePlates()
    if not nameplates then return end
    for _, nameplate in pairs(nameplates) do
        addon:FormatNameplate(nameplate)
    end
end

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_LOGIN")
eventFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
eventFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
eventFrame:RegisterEvent("UNIT_NAME_UPDATE")
eventFrame:RegisterEvent("GOSSIP_SHOW")
eventFrame:RegisterEvent("QUEST_DETAIL")
eventFrame:RegisterEvent("QUEST_PROGRESS")
eventFrame:RegisterEvent("QUEST_COMPLETE")

eventFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "ADDON_LOADED" and arg1 == AddonName then
        if not FontifyDB then
            FontifyDB = addon.CopyTable(addon.defaultDB)
        elseif not FontifyDB.profiles then
            FontifyDB.profiles = addon.CopyTable(addon.defaultDB.profiles)
            FontifyDB.profile = "Default"
        end
        local charName, charRealm = UnitName("player"), GetRealmName()
        if charName and charRealm and FontifyDB.characterProfiles then
            local savedProf = FontifyDB.characterProfiles[charName .. "-" .. charRealm]
            if savedProf and FontifyDB.profiles[savedProf] then
                FontifyDB.profile = savedProf
            end
        end
        if addon.InitConfig then addon.InitConfig() end
    elseif event == "PLAYER_LOGIN" then
        addon:RefreshAllNameplates()
    elseif event == "NAME_PLATE_UNIT_ADDED" then
        if type(arg1) == "string" and arg1:find("^nameplate") then
            local nameplate = C_NamePlate.GetNamePlateForUnit(arg1)
            if nameplate then addon:FormatNameplate(nameplate) end
        end
    elseif event == "UNIT_NAME_UPDATE" then
        if type(arg1) == "string" and arg1:find("^nameplate") then
            local nameplate = C_NamePlate.GetNamePlateForUnit(arg1)
            if nameplate then addon:FormatNameplate(nameplate) end
        end
    elseif event == "GOSSIP_SHOW" or event == "QUEST_DETAIL" or event == "QUEST_PROGRESS" or event == "QUEST_COMPLETE" then
        addon:ApplyQuestFonts()
    end
end)

if CompactUnitFrame_UpdateName then
    hooksecurefunc("CompactUnitFrame_UpdateName", function(frame)
        if frame and frame.unit then
            local nameplate = nil
            if type(frame.unit) == "string" and frame.unit:find("^nameplate") then
                nameplate = C_NamePlate.GetNamePlateForUnit(frame.unit)
            end
            if not nameplate then nameplate = frame:GetParent() end
            if nameplate then addon:FormatNameplate(nameplate) end
        end
    end)
end
