---@diagnostic disable: redundant-parameter
local TXUI, F, E, I, V, L, P, G = unpack((select(2, ...)))
local M = TXUI:GetModule("Misc")
local UF = E:GetModule("UnitFrames")
local ElvUF = E.oUF

local AbbreviateNumbers = AbbreviateNumbers
local floor = math.floor
local format = string.format
local GetCreatureDifficultyColor = GetCreatureDifficultyColor
local ipairs = ipairs
local match = string.match
local ScaleTo100 = CurveConstants and CurveConstants.ScaleTo100
local select = select
local UnitClassification = UnitClassification
local UnitHealth = UnitHealth
local UnitHealthMax = UnitHealthMax
local UnitHealthPercent = UnitHealthPercent
local UnitIsPlayer = UnitIsPlayer
local UnitLevel = UnitLevel
local UnitPower = UnitPower
local UnitPowerMax = UnitPowerMax
local UnitPowerPercent = UnitPowerPercent
local UnitReaction = UnitReaction

function M:_TagsUpdate()
  if not F.IsTXUIProfile() then return end

  for _, unit in ipairs { "party", "arena", "boss", "pet", "player", "target", "targettarget", "focus", "raid1", "raid2", "raid3" } do
    if unit == "party" or unit:find("raid") then
      for i = 1, UF[unit]:GetNumChildren() do
        local child = select(i, UF[unit]:GetChildren())
        for x = 1, child:GetNumChildren() do
          local subchild = select(x, child:GetChildren())
          if subchild then subchild:UpdateTags() end
        end
      end
    elseif unit == "boss" or unit == "arena" then
      for i = 1, 10 do
        local unitframe = UF[unit .. i]
        if unitframe then unitframe:UpdateTags() end
      end
    else
      local unitframe = UF[unit]
      if unitframe then unitframe:UpdateTags() end
    end
  end
end

function M:TagsUpdate()
  if UF.Initialized then
    F.Event.RunNextFrame(function()
      F.Event.ContinueAfterElvUIUpdate(function()
        self:_TagsUpdate()
      end)
    end)
  elseif not self:IsHooked(UF, "Initialize") then
    self:SecureHook(UF, "Initialize", F.Event.GenerateClosure(self.TagsUpdate, self))
  end
end

-- Event strings
local NAME_EVENTS = "UNIT_NAME_UPDATE PLAYER_TARGET_CHANGED UNIT_FACTION INSTANCE_ENCOUNTER_ENGAGE_UNIT"
local HEALTH_EVENTS = "UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED"
local POWER_EVENTS = "UNIT_DISPLAYPOWER UNIT_POWER_FREQUENT UNIT_MAXPOWER"
local LEVEL_EVENTS = "UNIT_LEVEL PLAYER_LEVEL_UP"

-- Helper: Get unit status (dead/ghost/offline)
local function GetUnitStatus(unit)
  if not UnitIsFeignDeath(unit) and UnitIsDead(unit) then return L["Dead"] end
  if UnitIsGhost(unit) then return L["Ghost"] end
  if not UnitIsConnected(unit) then return L["Offline"] end
end

-- Helper: Convert level to string with "??" fallback
local function GetLevelString(level)
  if level == -1 or not level or level == "" then return "??" end
  return tostring(level)
end

function M:Tags()
  local iconsDb = E.db.TXUI.wunderbar.subModules["SpecSwitch"].icons
  local iconTheme = E.db.TXUI.elvUIIcons.classIcons.theme or "ToxiClasses"
  local iconPath = self:GetClassIconPath(iconTheme)

  local dm = TXUI:GetModule("ThemesDarkTransparency")

  local function FormatColorTag(str, unit)
    -- i don't fucking know, i don't see this string anywhere but otherwise get Lua errors
    if not str then return "Missing string, very bad!" end

    if UnitIsPlayer(unit) then
      local _, unitClass = UnitClass(unit)
      local cs = ElvUF.colors.class[unitClass]
      return cs and ("|cff" .. F.String.FastRGB(cs.r, cs.g, cs.b) .. str) or ("|cffcccccc" .. str)
    else
      local cr = ElvUF.colors.reaction[UnitReaction(unit, "player")]
      return cr and ("|cff" .. F.String.FastRGB(cr.r, cr.g, cr.b) .. str) or ("|cffcccccc" .. str)
    end
  end

  -- Name tags
  E:AddTag("tx:name", NAME_EVENTS, function(unit)
    local name = UnitName(unit)
    if not name then return "missing name wtf" end

    if not dm.isEnabled then return name end

    return FormatColorTag(name, unit)
  end)

  -- ToxiUI: Health Tags
  local function GetHealthPercentage(unit)
    if TXUI.IsMidnight then return format("%d", UnitHealthPercent(unit, true, ScaleTo100)) end

    local max = UnitHealthMax(unit)
    if max == 0 then
      return 0
    else
      return floor(UnitHealth(unit) / max * 100 + 0.5)
    end
  end

  local function ColorHealthTag(unit, percentSign)
    local status = GetUnitStatus(unit)
    if status then return status end

    local percentHealth = GetHealthPercentage(unit)
    local percentHealthStr = tostring(percentHealth)

    if percentSign then percentHealthStr = percentHealthStr .. "%" end

    -- Return different coloring for Dark Mode
    if dm.isEnabled then
      return FormatColorTag(percentHealthStr, unit)
    -- If not gradient mode, or the option is disabled, return early an uncolored string
    else
      return percentHealthStr
    end
  end

  E:AddTag("tx:health:percent:nosign", "UNIT_HEALTH PLAYER_TARGET_CHANGED UNIT_FACTION UNIT_MAXHEALTH", function(unit)
    return ColorHealthTag(unit)
  end)

  E:AddTag("tx:health:current:shortvalue", HEALTH_EVENTS, function(unit)
    local status = GetUnitStatus(unit)
    if status then return status end

    local health = UnitHealth(unit)
    local healthStr = AbbreviateNumbers(health, E.Abbreviate.short)

    if not dm.isEnabled then return healthStr end

    return FormatColorTag(healthStr, unit)
  end)

  E:AddTag("tx:health:current:shortvalue:absorb", HEALTH_EVENTS .. " UNIT_ABSORB_AMOUNT_CHANGED", function(unit)
    local status = GetUnitStatus(unit)
    if status then return status end

    local health = UnitHealth(unit)
    local healthStr = AbbreviateNumbers(health, E.Abbreviate.short)

    local absorb = UnitGetTotalAbsorbs(unit)
    local absorbStr = AbbreviateNumbers(absorb, E.Abbreviate.short)

    local ret = format("%s + %s", healthStr, absorbStr)

    if not dm.isEnabled then return ret end

    return FormatColorTag(ret, unit)
  end, not TXUI.IsRetail)

  -- Power Percent No Sign Tag
  E:AddTag("tx:power:percent:nosign", POWER_EVENTS, function(unit)
    if TXUI.IsMidnight then
      local power = format("%d", UnitPowerPercent(unit, nil, true, ScaleTo100))

      if not dm.isEnabled then
        return power
      else
        return FormatColorTag(power, unit)
      end
    else
      local max = UnitPowerMax(unit)
      local power = floor(UnitPower(unit) / max * 100 + 0.5)

      if not dm.isEnabled then
        if max ~= 0 then return power end
      end

      local powerStr = tostring(power)

      if max ~= 0 then return FormatColorTag(powerStr, unit) end
    end
  end)

  -- Specs that should not display mana in Midnight
  local hideManaSpecs = {
    [63] = true, -- Fire Mage
    [64] = true, -- Frost Mage
    [263] = true, -- Enhancement Shaman
  }

  -- Cache current spec ID to avoid repeated API calls
  local cachedSpecID = PlayerUtil.GetCurrentSpecID()
  F.Event.RegisterFrameEventAndCallback("PLAYER_SPECIALIZATION_CHANGED", function()
    cachedSpecID = PlayerUtil.GetCurrentSpecID()
  end, self)

  E:AddTag("tx:power", POWER_EVENTS, function(unit)
    local power = UnitPower(unit)

    if TXUI.IsMidnight then
      if E.myclass == "WARLOCK" and unit == "player" then power = UnitPower(unit, Enum.PowerType.SoulShards) end
      if E.myclass == "PALADIN" and unit == "player" then power = UnitPower(unit, Enum.PowerType.HolyPower) end
      if unit == "player" and hideManaSpecs[cachedSpecID] then return end
    end

    if dm.isEnabled then
      return FormatColorTag(power, unit)
    else
      return power
    end
  end)

  local usingSpecIcons = TXUI.IsRetail and match(iconTheme, "ToxiSpec")

  -- Class Icon Tag
  E:AddTag("tx:classicon", "PLAYER_TARGET_CHANGED PLAYER_SPECIALIZATION_CHANGED", function(unit)
    if UnitIsPlayer(unit) then
      local _, class = UnitClass(unit)
      local icon = M.ClassIcons[class]

      if usingSpecIcons then
        local specIcon = ""
        local specId = nil

        local info = E:GetUnitSpecInfo(unit)
        if info and info.id then specId = info.id end

        if iconsDb and specId then
          icon = M.SpecIcons[specId]

          if icon then specIcon = format(iconPath, icon) end
        end

        return specIcon
      end

      if icon then return format(iconPath, icon) end
    end
  end)

  -- Level Tag
  E:AddTag("tx:level", LEVEL_EVENTS, function(unit)
    local level = UnitLevel(unit)

    -- Do not show level for max level units
    if level >= I.MaxLevelTable[TXUI.MetaFlavor] then return end

    local levelDisplayStr = "Lv " .. GetLevelString(level)

    if not dm.isEnabled then return levelDisplayStr end

    return FormatColorTag(levelDisplayStr, unit)
  end)

  -- Level Difficulty Tag
  E:AddTag("tx:level:difficulty", LEVEL_EVENTS, function(unit)
    local level = UnitLevel(unit)
    local levelStr = GetLevelString(level)

    local hex
    if levelStr == "??" then
      hex = "6e6e6e"
    else
      local color = GetCreatureDifficultyColor(level)
      hex = E:RGBToHex(color.r, color.g, color.b, "")
    end

    local coloredLvl = F.String.Color(levelStr, hex)

    if not dm.isEnabled then return "Lv " .. coloredLvl end

    -- After reload without a target FormatColorTag returns nil so we have to fallback to "Lv"
    return (FormatColorTag("Lv ", unit) or "Lv ") .. coloredLvl
  end)

  -- Credits to ElvUI [classification:icon]
  do
    local icon = F.String.ConvertGlyph(59706) -- star (xp) icon
    local gold, silver = F.String.Color(icon, I.Enum.Colors.GOLD), F.String.Color(icon, I.Enum.Colors.SILVER)
    local typeIcon = { elite = gold, worldboss = gold, rareelite = silver, rare = silver }
    E:AddTag("tx:classification", "UNIT_NAME_UPDATE", function(unit)
      if UnitIsPlayer(unit) then return end
      return typeIcon[UnitClassification(unit)]
    end)
  end

  local TagNames = {
    GENERAL = TXUI.Title,
    NAMES = TXUI.Title .. " Names",
    HEALTH = TXUI.Title .. " Health",
    POWER = TXUI.Title .. " Power",
  }

  -- Tag info
  -- Tag info: General

  -- Class Icon
  do
    E:AddTagInfo(
      "tx:classicon",
      TagNames.GENERAL,
      "Displays " .. TXUI.Title .. " class icon. The class icon style can be customized in " .. TXUI.Title .. " settings -> " .. F.String.Menu.Skins() .. " -> " .. F.String.ElvUI()
    )

    -- Level
    E:AddTagInfo("tx:level", TagNames.GENERAL, "Displays unit's level with " .. TXUI.Title .. " colors (e.g. Lv 42). Hides when the unit is max level.")

    -- Level Difficulty
    do
      local color = GetCreatureDifficultyColor(42)
      local hex = E:RGBToHex(color.r, color.g, color.b, "")
      E:AddTagInfo(
        "tx:level:difficulty",
        TagNames.GENERAL,
        "Displays unit's level with " .. TXUI.Title .. " and difficulty colors (e.g. Lv " .. F.String.Color("42", hex) .. "). Does not hide when the unit is max level."
      )
    end
    -- Classification
    E:AddTagInfo(
      "tx:classification",
      TagNames.GENERAL,
      "Displays a silver or gold " .. TXUI.Title .. " star for rare & elite monsters. This tag can be used only with the " .. F.String.ToxiUI("'- ToxiUI'") .. " font!"
    )
  end

  -- Tag info: Names
  do
    E:AddTagInfo("tx:name", TagNames.NAMES, "Displays unit's name with " .. TXUI.Title .. " colors.")
  end

  -- Tag info: Health
  do
    E:AddTagInfo("tx:health:percent:nosign", TagNames.HEALTH, "Displays percentage HP of unit without decimals or the % sign. Also adds " .. TXUI.Title .. " colors.")
    E:AddTagInfo("tx:health:current:shortvalue", TagNames.HEALTH, "Shortvalue of the unit's current health (e.g. 81k instead of 81200). Also adds " .. TXUI.Title .. " colors.")
    if TXUI.IsRetail then
      E:AddTagInfo(
        "tx:health:current:shortvalue:absorb",
        TagNames.HEALTH,
        "Shortvalue of the unit's current health with absorb value (e.g. 81k + 20k). Also adds " .. TXUI.Title .. " colors."
      )
    end
  end

  -- Tag info: Power
  do
    E:AddTagInfo(
      "tx:power:percent:nosign",
      TagNames.POWER,
      "Displays percentage Power of unit without decimals or the % sign. Also adds " .. TXUI.Title .. " colors and does not display when Power is at 0."
    )
    E:AddTagInfo(
      "tx:power",
      TagNames.POWER,
      "Displays current Power of unit. Also adds "
        .. TXUI.Title
        .. " colors."
        .. (TXUI.IsRetail and " Displays Soul Shards for " .. F.String.Class("Warlock", "WARLOCK") .. " and hides mana for non-Arcane " .. F.String.Class("Mages", "MAGE") or "")
    )
  end

  -- Requires ElvUI 13.67 or later
  if UF and UF.overrideTags then
    local overrideTags = {
      ["tx:level"] = true,
      ["tx:level:difficulty"] = true,
      ["tx:classicon"] = true,
      ["tx:health:percent:nosign"] = true,
      ["tx:health:current:shortvalue"] = true,
      ["tx:health:current:shortvalue:absorb"] = true,

      ["tx:name"] = true,

      ["tx:power:percent:nosign"] = true,
    }

    F.Table.Crush(UF.overrideTags, overrideTags)
  end

  -- Settings Callback
  F.Event.RegisterCallback("Tags.DatabaseUpdate", self.TagsUpdate, self)
  F.Event.RegisterCallback("TXUI.DatabaseUpdate", self.TagsUpdate, self)
  F.Event.RegisterOnceCallback("TXUI.InitializedSafe", F.Event.GenerateClosure(self.TagsUpdate, self))
end

M:AddCallback("Tags")
