local addonName, ns = ...
_G.rOrbs = ns -- make global so other files see the same object
PlayerFrame:UnregisterAllEvents()
PlayerFrame:Hide()

-- Global helper to strip secret value taint (12.0+)
-- pcall(string.format) creates a NEW untainted string
function rOrbs_CleanNumber(v)
  if v == nil then return 0 end
  local ok, result = pcall(string.format, "%.10g", v)
  if ok then return tonumber(result) or 0 end
  return 0
end

-- all your init stuff
local LSM = LibStub("LibSharedMedia-3.0")
local mediaCallbacks = {}
local initCallbacks = {}
local configLoaded = false

LSM.RegisterCallback(ns, "LibSharedMedia_Registered", function(name, mediaType, key)
  if configLoaded then
    for _, callback in pairs(mediaCallbacks) do
      callback(name, mediaType, key)
    end
  end
end)

function ns.RegisterInit(nameOrFunc, maybeFunc)
    local name, func

    if type(nameOrFunc) == "function" then
        func = nameOrFunc
        -- debug library is not available in WoW Retail; use a simple counter instead
        name = "callback#" .. (#initCallbacks + 1)
    else
        name = tostring(nameOrFunc) -- force string in case someone passed a table
        func = maybeFunc
    end

    local wrapped = function()
        local ok, err = xpcall(func, debugstack)
        if not ok then
            error(("Init callback failed:\n%s"):format(err))
        end
    end

    table.insert(initCallbacks, { name = name, func = wrapped })
end


function ns:Init()
    local errors = {}

    for _, cb in ipairs(initCallbacks) do
        local ok, err = xpcall(cb.func, debugstack)
        if not ok then
            table.insert(errors, ("Init callback failed:\n%s"):format( err))
        end
    end

    -- After running everything, dump errors (if any) through Blizzard's handler
    if #errors > 0 then
        for _, msg in ipairs(errors) do
            geterrorhandler()(msg)
        end
    end

    wipe(initCallbacks)
end

function rOrbs:CheckClientVersion(version)
    if WOW_PROJECT_ID ~= version then return false
    else return true
    end
end

function ns:ListenForMediaChange(callback)
  table.insert(mediaCallbacks, callback)
end

-- DEBUG: Toggle individual target orb layers
-- /rorbsdebug 1..10 to toggle layers, /rorbsdebug to show status
-- /rorbsdebug info to print detailed layer info
-- Fix: SpellActivationOverlayFrame sometimes gets stuck invisible (Blizzard bug).
-- We reset all overlays on PLAYER_ENTERING_WORLD and periodically check for stuck frames.
do
  local spellOverlayFixer = CreateFrame("Frame")
  local CHECK_INTERVAL = 2.0  -- check every 2 seconds
  local elapsed = 0

  local function ResetStuckOverlays()
    if SpellActivationOverlay and SpellActivationOverlay.FreeAllOverlays then
      SpellActivationOverlay:FreeAllOverlays()
    end
    -- Also manually hide any stuck overlay frames
    if SpellActivationOverlayFrame then
      for i = 1, SpellActivationOverlayFrame:GetNumChildren() do
        local child = select(i, SpellActivationOverlayFrame:GetChildren())
        if child and child:IsShown() then
          local alpha = child:GetAlpha()
          -- A stuck overlay has alpha 0 or near 0 but IsShown() returns true
          if alpha ~= nil and alpha < 0.01 then
            child:Hide()
          end
        end
      end
    end
  end

  spellOverlayFixer:RegisterEvent("PLAYER_ENTERING_WORLD")
  spellOverlayFixer:RegisterEvent("PLAYER_LOGIN")
  spellOverlayFixer:SetScript("OnEvent", function(self, event)
    -- Small delay so Blizzard frames finish initializing
    C_Timer.After(1, ResetStuckOverlays)
  end)

  spellOverlayFixer:SetScript("OnUpdate", function(self, dt)
    elapsed = elapsed + dt
    if elapsed >= CHECK_INTERVAL then
      elapsed = 0
      if SpellActivationOverlayFrame then
        for i = 1, SpellActivationOverlayFrame:GetNumChildren() do
          local child = select(i, SpellActivationOverlayFrame:GetChildren())
          if child and child:IsShown() then
            local alpha = child:GetAlpha()
            if alpha ~= nil and alpha < 0.01 then
              child:Hide()
            end
          end
        end
      end
    end
  end)
end

-- ================================================================
-- VEHICLE HIDE: Orbs ausblenden wenn Spieler ein Fahrzeug betritt
-- Frames: Player-Orbs + Angel/Demon + Combat/Resting Icons
-- ================================================================
do
  local vehicleFrame = CreateFrame("Frame")
  vehicleFrame:RegisterUnitEvent("UNIT_ENTERED_VEHICLE", "player")
  vehicleFrame:RegisterUnitEvent("UNIT_EXITED_VEHICLE",  "player")
  vehicleFrame:RegisterEvent("PLAYER_ENTERING_WORLD")

  local function SetOrbsVisible(visible)
    -- Player-Orb-Parent (enthält Health + Power Orb als Kinder)
    local playerFrame = _G["rOrbsPlayerFrame"]
    if playerFrame then
      if visible then playerFrame:Show() else playerFrame:Hide() end
    end
    -- Figuren + Standalone-Icons: nur ausblenden per Hide(); beim Wieder-Einblenden
    -- kein blindes Show() — das würde deaktivierte Icons und mode=="none" Figuren falsch anzeigen.
    local frames = {
      _G["rOrbsAngelFrame"],
      _G["rOrbsDemonFrame"],
      _G["rOrbsPlayerCombatFrame"],
      _G["rOrbsPlayerLeaderFrame"],
      ns.AngelFrame,
      ns.DemonFrame,
      ns.PlayerCombatIcon,
      ns.PlayerRestingIcon,
      ns.PlayerLeaderIcon,
    }
    local seen = {}
    if not visible then
      for _, f in ipairs(frames) do
        if f and not seen[f] then
          seen[f] = true
          f:Hide()
        end
      end
      return
    end
    if ns.ApplyFigureMode then ns.ApplyFigureMode() end
    if ns.ApplyPowerFigureMode then ns.ApplyPowerFigureMode() end
    if ns.RefreshStandalonePlayerIcons then
      ns.RefreshStandalonePlayerIcons()
    else
      C_Timer.After(0, function()
        if ns.ApplyFigureMode then ns.ApplyFigureMode() end
        if ns.ApplyPowerFigureMode then ns.ApplyPowerFigureMode() end
        if ns.RefreshStandalonePlayerIcons then ns.RefreshStandalonePlayerIcons() end
      end)
    end
  end

  vehicleFrame:SetScript("OnEvent", function(self, event, unit)
    if event == "UNIT_ENTERED_VEHICLE" then
      SetOrbsVisible(false)
    elseif event == "UNIT_EXITED_VEHICLE" then
      SetOrbsVisible(true)
    elseif event == "PLAYER_ENTERING_WORLD" then
      -- Kleines Delay damit alle Frames initialisiert sind
      C_Timer.After(0.5, function()
        if UnitInVehicle("player") then
          SetOrbsVisible(false)
        else
          SetOrbsVisible(true)
        end
      end)
    end
  end)
end

SLASH_RORBSDEBUG1 = "/rorbsdebug"
SlashCmdList["RORBSDEBUG"] = function(msg)
  local orb = _G["rOrbsTargetHealthOrb"]
  if not orb then print("|cffff0000[rOrbs Debug]|r Target health orb not found") return end
  
  local layers = {
    { name = "bg",         obj = orb.bg,         desc = "1: Background (orb_back2)" },
    { name = "fill",       obj = orb.fill,       desc = "2: Fill StatusBar FRAME (the actual bar)", isFrame = true },
    { name = "fillTex",    obj = orb.fillTex,     desc = "3: Fill Texture object" },
    { name = "clipFrame",  obj = orb.clipFrame,   desc = "4: Clip Frame", isFrame = true },
    { name = "galaxies",   obj = orb.galaxies,    desc = "5: Galaxies (animated)", isTable = true },
    { name = "bubbles",    obj = orb.bubbles,     desc = "6: Bubbles (animated)", isTable = true },
    { name = "spark",      obj = orb.spark,        desc = "7: Spark" },
    { name = "lowHP",      obj = orb.lowHP,        desc = "8: Low HP Glow" },
    { name = "highlight",  obj = orb.highlight,    desc = "9: Gloss (orb_gloss)" },
    { name = "overlay",    obj = orb.overlay,      desc = "10: Overlay Frame (parent of gloss/skull/lowHP)", isFrame = true },
  }
  
  -- Store overlay ref for debug
  if not orb.overlay then
    -- Find overlay by child search
    for _, child in ipairs({orb:GetChildren()}) do
      local n = child:GetName()
      if n and n:find("Overlay") then
        orb.overlay = child
        layers[10].obj = child
        break
      end
    end
  end
  
  if msg == "info" then
    print("|cff00ff00[rOrbs Debug]|r === DETAILED INFO ===")
    -- Fill StatusBar info
    if orb.fill then
      local min, max = orb.fill:GetMinMaxValues()
      local val = orb.fill:GetValue()
      print(string.format("  Fill StatusBar: value=%.2f, min=%.2f, max=%.2f", val or 0, min or 0, max or 0))
      print(string.format("  Fill FrameLevel: %d, Strata: %s", orb.fill:GetFrameLevel(), orb.fill:GetFrameStrata()))
      local ft = orb.fill:GetStatusBarTexture()
      if ft then
        local r, g, b, a = ft:GetVertexColor()
        print(string.format("  FillTex VertexColor: r=%.2f g=%.2f b=%.2f a=%.2f", r or 0, g or 0, b or 0, a or 0))
        print(string.format("  FillTex DrawLayer: %s, Alpha: %.2f", ft:GetDrawLayer() or "?", ft:GetAlpha()))
        print(string.format("  FillTex Size: %.1f x %.1f", ft:GetWidth(), ft:GetHeight()))
      end
    end
    -- Highlight info
    if orb.highlight then
      local r, g, b, a = orb.highlight:GetVertexColor()
      print(string.format("  Gloss VertexColor: r=%.2f g=%.2f b=%.2f a=%.2f", r or 0, g or 0, b or 0, a or 0))
      local dl, sl = orb.highlight:GetDrawLayer()
      print(string.format("  Gloss DrawLayer: %s-%d, Alpha: %.2f, Blend: %s", dl or "?", sl or 0, orb.highlight:GetAlpha(), orb.highlight:GetBlendMode()))
    end
    -- Background info
    if orb.bg then
      local r, g, b, a = orb.bg:GetVertexColor()
      print(string.format("  BG VertexColor: r=%.2f g=%.2f b=%.2f a=%.2f", r or 0, g or 0, b or 0, a or 0))
      print(string.format("  BG Desaturated: %s", tostring(orb.bg:IsDesaturated())))
    end
    -- Orb frame info
    print(string.format("  Orb FrameLevel: %d", orb:GetFrameLevel()))
    -- List ALL textures on orb
    print("  --- All textures on orb button ---")
    for i, region in ipairs({orb:GetRegions()}) do
      if region.GetTexture then
        local tex = region:GetTexture()
        local texStr = tex and tostring(tex) or "nil"
        local texName = type(tex) == "string" and (tex:match("([^\\]+)$") or tex) or texStr
        local dl, sl = region:GetDrawLayer()
        local r, g, b, a = region:GetVertexColor()
        print(string.format("    [%d] tex=%s layer=%s-%d rgba=%.1f,%.1f,%.1f,%.1f vis=%s alpha=%.2f blend=%s", 
          i, texName, dl or "?", sl or 0, r, g, b, a, tostring(region:IsShown()), region:GetAlpha(), region:GetBlendMode()))
      end
    end
    -- List ALL child frames and THEIR textures
    print("  --- All child frames + their textures ---")
    local function dumpFrame(frame, indent)
      local n = frame:GetName() or "unnamed"
      print(string.format("%s[Frame] %s FL=%d vis=%s strata=%s type=%s", indent, n, frame:GetFrameLevel(), tostring(frame:IsShown()), frame:GetFrameStrata(), frame:GetObjectType()))
      for j, region in ipairs({frame:GetRegions()}) do
        if region.GetTexture then
          local tex = region:GetTexture()
          local texStr = tex and tostring(tex) or "nil"
          local texName = type(tex) == "string" and (tex:match("([^\\]+)$") or tex) or texStr
          local dl, sl = region:GetDrawLayer()
          local r, g, b, a = region:GetVertexColor()
          print(string.format("%s  [Tex %d] %s layer=%s-%d rgba=%.1f,%.1f,%.1f,%.1f vis=%s alpha=%.2f blend=%s", 
            indent, j, texName, dl or "?", sl or 0, r, g, b, a, tostring(region:IsShown()), region:GetAlpha(), region:GetBlendMode()))
        elseif region.GetText then
          print(string.format("%s  [Font %d] text='%s' vis=%s", indent, j, tostring(region:GetText()), tostring(region:IsShown())))
        end
      end
      for _, child in ipairs({frame:GetChildren()}) do
        dumpFrame(child, indent .. "    ")
      end
    end
    for _, child in ipairs({orb:GetChildren()}) do
      dumpFrame(child, "    ")
    end
    return
  end
  
  local num = tonumber(msg)
  if num and layers[num] then
    local layer = layers[num]
    if layer.isTable then
      local first = layer.obj and layer.obj[1]
      if first then
        local visible = first:IsShown()
        for _, t in pairs(layer.obj) do
          if visible then t:Hide() else t:Show() end
        end
        print("|cff00ff00[rOrbs Debug]|r", layer.desc, visible and "|cffff0000HIDDEN|r" or "|cff00ff00SHOWN|r")
      end
    elseif layer.isFrame then
      if layer.obj then
        local visible = layer.obj:IsShown()
        if visible then layer.obj:Hide() else layer.obj:Show() end
        print("|cff00ff00[rOrbs Debug]|r", layer.desc, visible and "|cffff0000HIDDEN|r" or "|cff00ff00SHOWN|r")
      else
        print("|cffff0000[rOrbs Debug]|r", layer.desc, "= NIL (not found)")
      end
    else
      if layer.obj then
        local visible = layer.obj:IsShown()
        if visible then layer.obj:Hide() else layer.obj:Show() end
        print("|cff00ff00[rOrbs Debug]|r", layer.desc, visible and "|cffff0000HIDDEN|r" or "|cff00ff00SHOWN|r")
      end
    end
  else
    print("|cff00ff00[rOrbs Debug]|r Target Health Orb Layers:")
    for i, layer in ipairs(layers) do
      local shown = "?"
      if layer.isTable then
        local first = layer.obj and layer.obj[1]
        shown = first and (first:IsShown() and "SHOWN" or "HIDDEN") or "nil"
      elseif layer.obj then
        shown = layer.obj:IsShown() and "SHOWN" or "HIDDEN"
      else
        shown = "nil"
      end
      print(string.format("  |cffffff00%d|r = %s [%s]", i, layer.desc, shown))
    end
    print("  |cffffff00info|r = Print detailed layer info")
    print("Usage: /rorbsdebug <1-10|info>")
  end
end
