--[[--------------------------------------------------------------------
  PaladinHolyPower - ui/settings_panel/tabs/stats_tab.lua
  "Stats" tab with spec-aware rows and PURE color plates.
  • CardPlate frames: exact RGB fill (alpha=1), 1px border.
  • Gloss overlay: idle = NO gloss, hover (over card OR value chip) = glossy.
  • ONE tooltip region per card via an invisible hover frame.
  • Item Level auto-colors by equipped rarity (slightly mixed toward white).
  • Uses built-in palette by default; call PHP.SetStatBoxColor/Hex to override.
  • Text now has a faint drop shadow (built-in shadow so it renders UNDER text).
  • Perf: headers are created once (no re-alloc per repaint); all repainting is
    coalesced/throttled via C_Timer; no work runs when panel is hidden.
----------------------------------------------------------------------]]

local PHP = _G.PaladinHolyPower
if not PHP then return end

local M = {}

-- ----------------- safe helpers -----------------
local function safe(f, ...) local ok, a,b,c,d,e = pcall(f, ...); if ok then return a,b,c,d,e end end
local function getSpecID() local idx = safe(GetSpecialization); if not idx then return nil end; return select(1, safe(GetSpecializationInfo, idx)) end
local function unitStat(i) return select(2, safe(function() return UnitStat("player", i) end)) or 0 end
local function armor()     return select(2, safe(function() return UnitArmor("player") end))   or 0 end
local function rating(crn) local cr=_G[crn]; if not cr or not _G.GetCombatRating then return 0 end; return safe(GetCombatRating, cr) or 0 end
local function ratingBonus(crn) local cr=_G[crn]; if not cr or not _G.GetCombatRatingBonus then return 0 end; return safe(GetCombatRatingBonus, cr) or 0 end
local function avgIlvlEquipped() local a,b=safe(GetAverageItemLevel); return b or a or 0 end
local function nfmt(x) return tostring(math.floor((x or 0) + 0.5)) end
local function clamp01(x) if x<0 then return 0 elseif x>1 then return 1 else return x end end
local function lerp(a,b,t) return a + (b-a) * t end

local function CRIT_CONST()  return (getSpecID()==65) and "CR_CRIT_SPELL"  or "CR_CRIT_MELEE"  end
local function HASTE_CONST() return (getSpecID()==65) and "CR_HASTE_SPELL" or "CR_HASTE_MELEE" end

-- ----------------- UI constants -----------------
local WHITE_TEX = "Interface\\Buttons\\WHITE8x8"
local C_Timer   = _G.C_Timer

local CARD_W, CARD_H   = 156, 34
local ROW_GAP_Y        = 12
local CARD_GAP_X       = 10

local TOP_PAD              = 10
local GAP_ITEM_TO_SECTION  = 36
local GAP_TITLE_TO_ROW     = 26
local GAP_BETWEEN_SECTIONS = 36
local HEADER_SIZE_BUMP     = 4

local function keyFor(label) return (string.upper((label or "")):gsub("%s+", "_")) end

-- ----------------- Built-in palette -----------------
local PALETTE = {
  ITEM_LEVEL      = { 0.52, 0.52, 0.51, 1.00 },
  STRENGTH        = { 0.84, 0.23, 0.24, 1.00 },
  STAMINA         = { 0.55, 0.71, 0.00, 1.00 },
  ARMOR           = { 0.70, 0.75, 0.71, 1.00 },
  INTELLECT       = { 0.82, 0.62, 0.91, 1.00 },
  CRITICAL_STRIKE = { 0.97, 0.85, 0.37, 1.00 },
  HASTE           = { 0.36, 0.54, 0.66, 1.00 },
  MASTERY         = { 0.60, 0.40, 0.80, 1.00 },
  VERSATILITY     = { 1.00, 0.55, 0.41, 1.00 },
  LEECH           = { 0.40, 0.80, 0.67, 1.00 },
  DODGE           = { 0.76, 0.60, 0.42, 1.00 },
  PARRY           = { 0.75, 0.75, 0.75, 1.00 },
}

local function usingCustomColors(S)
  return S and S.__statsColorsEnabled == true and type(S.statsColors) == "table"
end

local function resolveColor(S, label)
  local key = keyFor(label)
  local c = (usingCustomColors(S) and S.statsColors[key] and S.statsColors[key][1]) and S.statsColors[key]
          or PALETTE[key] or { 0.22, 0.20, 0.14, 1.00 }
  return c[1] or 0, c[2] or 0, c[3] or 0, 1.0
end

-- ----------------- CardPlate (idle no gloss, hover glossy) -------------
-- ONE hover region controls both gloss + tooltip. Chip is non-mouse.
local function CreateCardPlate(parent, label, w, h)
  local f = CreateFrame("Frame", nil, parent, "BackdropTemplate")
  f:SetSize(w or CARD_W, h or CARD_H)
  f:SetBackdrop({ bgFile = WHITE_TEX, edgeFile = WHITE_TEX, edgeSize = 1 })
  f:SetBackdropBorderColor(0,0,0,1)
  f:SetBackdropColor(0,0,0,1)
  if f.SetIgnoreParentAlpha then f:SetIgnoreParentAlpha(true) end
  f:SetAlpha(1)

  -- Title (built-in shadow sits UNDER text)
  local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  title:SetDrawLayer("OVERLAY", 6)
  title:SetPoint("CENTER", f, "CENTER", 0, 0)
  title:SetText(string.upper(label or ""))
  title:SetTextColor(1,1,1,0.97)
  title:SetShadowOffset(2, -2)
  title:SetShadowColor(0,0,0,0.90)

  -- Value chip (NOT mouse-enabled so it never splits hover)
  local chip = CreateFrame("Frame", nil, f, "BackdropTemplate")
  chip:SetBackdrop({ bgFile = WHITE_TEX, edgeFile = WHITE_TEX, edgeSize = 1 })
  chip:SetBackdropColor(0,0,0,0.90)
  chip:SetBackdropBorderColor(0,0,0,1)
  chip:SetSize(math.max(50, math.floor((w or CARD_W)*0.36)), 16)
  chip:SetPoint("BOTTOM", f, "BOTTOM", 0, -7)
  chip:EnableMouse(false)

  local valueFS = chip:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  valueFS:SetPoint("CENTER"); valueFS:SetText("0")
  valueFS:SetShadowOffset(2, -2)
  valueFS:SetShadowColor(0,0,0,0.90)

  -- Gloss overlay (HIDDEN while idle; shown on hover)
  local gloss = f:CreateTexture(nil, "OVERLAY")
  gloss:SetAllPoints(f)
  gloss:SetTexture(WHITE_TEX)
  gloss:SetBlendMode("ADD")
  if gloss.SetIgnoreParentAlpha then gloss:SetIgnoreParentAlpha(true) end
  if gloss.SetGradient and CreateColor then
    gloss:SetGradient("VERTICAL", CreateColor(1,1,1,0.32), CreateColor(1,1,1,0.00))
  else
    gloss:SetVertexColor(1,1,1,0.28)
  end
  gloss:Hide() -- idle = NO gloss
  f.gloss = gloss

  -- Invisible, single hover frame that covers the card + the chip area.
  local hover = CreateFrame("Frame", nil, f)
  hover:SetPoint("TOPLEFT",     f, "TOPLEFT",     0,  0)
  hover:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, -10) -- extend over chip area
  hover:EnableMouse(true)

  hover:SetScript("OnEnter", function()
    if f.gloss then f.gloss:Show() end   -- hover = glossy
  end)
  hover:SetScript("OnLeave", function()
    if f.gloss then f.gloss:Hide() end   -- leave = hide gloss
  end)

  return { plate = f, hover = hover, chip = chip, title = title, text = valueFS }
end

local function TintCardBase(card, r, g, b)
  if not (card and card.plate and card.plate.SetBackdropColor) then return end
  card.plate:SetBackdropColor(r or 0, g or 0, b or 0, 1.0)
end

-- ----------------- Headers (create once; reuse) -----------------
local function CreateHeader(parent, text)
  local fs = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
  if PHP.Theme and PHP.Theme.ApplyGradientText then
    PHP.Theme.ApplyGradientText(fs, text, nil, nil, { outline = true, sizeDelta = HEADER_SIZE_BUMP })
  else
    local fnt, sz, fl = fs:GetFont()
    fs:SetFont(fnt, (tonumber(sz) or 12) + HEADER_SIZE_BUMP, fl)
    fs:SetText(text)
  end
  fs:SetShadowOffset(2, -2)
  fs:SetShadowColor(0,0,0,0.85)
  fs:SetPoint("TOP", parent, "TOP", 0, 0) -- will be repositioned in paint()

  local line = parent:CreateTexture(nil, "OVERLAY")
  line:SetTexture(WHITE_TEX)
  line:SetColorTexture(.95,.90,.55,.25)
  line:SetPoint("TOP", fs, "BOTTOM", 0, -2)
  line:SetSize(160, 1)

  return fs
end

-- ----------------- Layout -----------------
local function LayoutRow(parent, yTop, cards, labels, values, S)
  local n = #labels
  local totalW = n*CARD_W + (n-1)*CARD_GAP_X
  local startX = -totalW/2
  for i=1,n do
    local lab = labels[i]
    local val = values[i]
    local card = cards[lab] or CreateCardPlate(parent, lab, CARD_W, CARD_H)
    cards[lab] = card
    card.plate:ClearAllPoints()
    card.plate:SetPoint("TOP", parent, "TOP", startX + (i-0.5)*CARD_W + (i-1)*CARD_GAP_X, yTop)
    local t = nfmt(val)
    card.text:SetText(t)
    local r,g,b = resolveColor(S, lab)
    TintCardBase(card, r,g,b)
    card.plate:Show(); card.chip:Show()
  end
  return yTop - CARD_H - ROW_GAP_Y
end

-- ----------------- Tooltips -----------------
local function attachTooltip(frame, title, body)
  if PHP.Tooltip and PHP.Tooltip.Attach then
    PHP.Tooltip.Attach(frame, title, body)
  end
end

local L = _G
local function tooltipTextFor(label, values)
  label = label or ""
  local up = label:upper()
  local function line2(a,b) return string.format("|cffffffff%s|r %s", a, b) end

  if up == "ITEM LEVEL" then
    local ilvl = avgIlvlEquipped()
    local t1 = L.STAT_AVERAGE_ITEM_LEVEL or "Average Item Level"
    local t2 = L.STAT_AVERAGE_ITEM_LEVEL_TOOLTIP or "Average item level of your equipped gear."
    return t1, line2("Equipped:", nfmt(ilvl)).."\n"..t2
  elseif up == "STRENGTH" then
    return (L.STAT_STRENGTH or "Strength"),
           line2(L.STAT_STRENGTH_TOOLTIP or "Increases Attack Power.", nfmt(values.base or unitStat(1)))
  elseif up == "STAMINA" then
    return (L.STAT_STAMINA or "Stamina"),
           line2(L.STAT_STAMINA_TOOLTIP or "Increases Health.", nfmt(values.base or unitStat(3)))
  elseif up == "ARMOR" then
    return (L.ARMOR or "Armor"),
           line2(L.ARMOR_TOOLTIP or "Reduces physical damage taken.", nfmt(values.base or armor()))
  elseif up == "INTELLECT" then
    return (L.INTELLECT or "Intellect"),
           line2(L.STAT_INTELLECT_TOOLTIP or "Increases spell power and mana.", nfmt(values.base or unitStat(4)))
  elseif up == "CRITICAL STRIKE" then
    local r = rating(CRIT_CONST()); local pct = ratingBonus(CRIT_CONST())
    return (L.STAT_CRITICAL_STRIKE or "Critical Strike"),
           line2("Rating:", nfmt(r)).."\n"..line2("Bonus:", string.format("%.2f%%", pct))
  elseif up == "HASTE" then
    local r = rating(HASTE_CONST()); local pct = ratingBonus(HASTE_CONST())
    return (L.STAT_HASTE or "Haste"),
           line2("Rating:", nfmt(r)).."\n"..line2("Bonus:", string.format("%.2f%%", pct))
  elseif up == "MASTERY" then
    local r = rating("CR_MASTERY"); local pct = safe(GetMasteryEffect) or 0
    return (L.STAT_MASTERY or "Mastery"),
           line2("Rating:", nfmt(r)).."\n"..line2("Effect:", string.format("%.2f%%", pct))
  elseif up == "VERSATILITY" then
    local r = rating("CR_VERSATILITY")
    if r == 0 then r = rating("CR_VERSATILITY_DAMAGE_DONE") end
    local dmg = _G.GetCombatRatingBonus and (safe(GetCombatRatingBonus, _G.CR_VERSATILITY_DAMAGE_DONE) or 0) or 0
    local reduct = _G.GetCombatRatingBonus and (safe(GetCombatRatingBonus, _G.CR_VERSATILITY_DAMAGE_TAKEN) or 0) or (dmg * 0.5)
    return (L.STAT_VERSATILITY or "Versatility"),
           line2("Rating:", nfmt(r)).."\n"..line2("Damage/Healing:", string.format("%.2f%%", dmg)).."\n"..line2("Damage Taken:", string.format("-%.2f%%", reduct))
  elseif up == "LEECH" then
    local r = rating("CR_LIFESTEAL"); local pct = ratingBonus("CR_LIFESTEAL")
    return (L.STAT_LIFESTEAL or "Leech"),
           line2("Rating:", nfmt(r)).."\n"..line2("Leech:", string.format("%.2f%%", pct))
  elseif up == "DODGE" then
    local r = rating("CR_DODGE"); local pct = ratingBonus("CR_DODGE")
    return (L.STAT_DODGE or "Dodge"),
           line2("Rating:", nfmt(r)).."\n"..line2("Chance:", string.format("%.2f%%", pct))
  elseif up == "PARRY" then
    local r = rating("CR_PARRY"); local pct = ratingBonus("CR_PARRY")
    return (L.STAT_PARRY or "Parry"),
           line2("Rating:", nfmt(r)).."\n"..line2("Chance:", string.format("%.2f%%", pct))
  end
  return label, ""
end

-- ----------------- Item Level color -----------------
local function avgEquippedQuality()
  local first = _G.INVSLOT_FIRST_EQUIPPED or 1
  local last  = _G.INVSLOT_LAST_EQUIPPED  or 19
  local sum, count = 0, 0
  for slot = first, last do
    if slot ~= 4 then
      local link = safe(GetInventoryItemLink, "player", slot)
      if link then
        local _, _, quality = safe(GetItemInfo, link)
        if quality then sum = sum + quality; count = count + 1 end
      end
    end
  end
  if count == 0 then return nil end
  return sum / count
end

local function colorFromItemLevel(ilvl)
  local t = clamp01((ilvl - 400) / 150)
  local r1,g1,b1 = 0.20, 0.20, 0.18
  local r2,g2,b2 = 0.95, 0.85, 0.40
  return r1 + (r2-r1)*t, g1 + (g2-g1)*t, b1 + (b2-b1)*t, 1
end

local function colorFromEquipment()
  local avgQ = avgEquippedQuality()
  local q = avgQ and math.max(0, math.min(7, math.floor(avgQ + 0.5))) or nil
  local tbl = _G.ITEM_QUALITY_COLORS and q and _G.ITEM_QUALITY_COLORS[q]
  if tbl then
    local t = 0.30
    local r = lerp(tbl.r or 1, 1, t)
    local g = lerp(tbl.g or 1, 1, t)
    local b = lerp(tbl.b or 1, 1, t)
    return r, g, b, 1
  end
  return colorFromItemLevel(avgIlvlEquipped())
end

-- ----------------- Build -----------------
function M.Build(parent, PHP, S)
  S = S or {}
  local cards = {}

  -- Create headers ONCE (no per-repaint allocations)
  local headerAttr = CreateHeader(parent, "Attributes")
  local headerEnh  = CreateHeader(parent, "Enhancements")

  -- Item Level (card)
  local ilvl = CreateCardPlate(parent, "Item Level", CARD_W, CARD_H)
  ilvl.plate:ClearAllPoints()
  ilvl.plate:SetPoint("TOP", parent, "TOP", 0, -TOP_PAD)

  -- ---- paint & throttling ----
  local repaintScheduled = false
  local function paint()
    if not parent or not parent:IsShown() then return end
    -- Item Level
    local iv = avgIlvlEquipped()
    ilvl.text:SetText(nfmt(iv))
    local rr,gg,bb = colorFromEquipment()
    TintCardBase(ilvl, rr,gg,bb)
    local ttl, body = tooltipTextFor("Item Level", {})
    attachTooltip(ilvl.hover, ttl, body)

    -- hide previous (we’ll re-show what we use)
    for _,c in pairs(cards) do c.plate:Hide(); c.chip:Hide() end

    local spec = getSpecID() -- 65 Holy, 66 Prot, 70 Ret
    local isHoly, isProt = (spec == 65), (spec == 66)

    -- ===== Attributes =====
    local y = -TOP_PAD - CARD_H - GAP_ITEM_TO_SECTION
    headerAttr:ClearAllPoints()
    headerAttr:SetPoint("TOP", parent, "TOP", 0, y)
    y = y - GAP_TITLE_TO_ROW

    local attrLabels, attrValues
    if isHoly then
      attrLabels = {"Intellect", "Stamina"}
      attrValues = { unitStat(4), unitStat(3) }
    elseif isProt then
      attrLabels = {"Strength", "Stamina", "Armor"}
      attrValues = { unitStat(1), unitStat(3), armor() }
    else
      attrLabels = {"Strength", "Stamina"}
      attrValues = { unitStat(1), unitStat(3) }
    end

    y = LayoutRow(parent, y, cards, attrLabels, attrValues, S)

    for i, lab in ipairs(attrLabels) do
      local card = cards[lab]
      local ttl2, body2 = tooltipTextFor(lab, { base = attrValues[i] })
      attachTooltip(card.hover, ttl2, body2)
    end

    -- ===== Enhancements =====
    y = y - GAP_BETWEEN_SECTIONS
    headerEnh:ClearAllPoints()
    headerEnh:SetPoint("TOP", parent, "TOP", 0, y)
    y = y - GAP_TITLE_TO_ROW

    local enhLabels = {"Critical Strike", "Haste", "Mastery", "Versatility", "Leech"}
    local enhValues = { rating(CRIT_CONST()), rating(HASTE_CONST()), rating("CR_MASTERY") }

    local vers = rating("CR_VERSATILITY"); if vers == 0 then vers = rating("CR_VERSATILITY_DAMAGE_DONE") end
    enhValues[#enhValues+1] = vers
    enhValues[#enhValues+1] = rating("CR_LIFESTEAL")

    if isProt then
      enhLabels[#enhLabels+1] = "Dodge"; enhValues[#enhValues+1] = rating("CR_DODGE")
      enhLabels[#enhLabels+1] = "Parry"; enhValues[#enhValues+1] = rating("CR_PARRY")
    end

    local i = 1
    while i <= #enhLabels do
      local take = math.min(3, #enhLabels - i + 1)
      local labs, vals = {}, {}
      for k = 0, take-1 do labs[#labs+1] = enhLabels[i+k]; vals[#vals+1] = enhValues[i+k] end
      y = LayoutRow(parent, y, cards, labs, vals, S)
      for j=1,#labs do
        local lab = labs[j]
        local ttl3, body3 = tooltipTextFor(lab, {})
        attachTooltip(cards[lab].hover, ttl3, body3)
      end
      i = i + take
    end
  end

  local function schedulePaint()
    if repaintScheduled then return end
    repaintScheduled = true
    if C_Timer and C_Timer.After then
      C_Timer.After(0.10, function()
        repaintScheduled = false
        if parent and parent:IsShown() then paint() end
      end)
    else
      -- Fallback if C_Timer not available
      repaintScheduled = false
      paint()
    end
  end

  -- Event coalescing (reduces CPU churn on spammy events)
  local ev = CreateFrame("Frame", nil, parent)
  ev:SetScript("OnEvent", function(_, evt, unit)
    if evt == "PLAYER_ENTERING_WORLD" then
      schedulePaint()
      return
    end
    if unit and unit~="player" then return end
    schedulePaint()
  end)
  ev:RegisterEvent("PLAYER_ENTERING_WORLD")
  ev:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
  ev:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
  ev:RegisterEvent("UNIT_STATS")
  ev:RegisterEvent("COMBAT_RATING_UPDATE")
  ev:RegisterEvent("PLAYER_AVG_ITEM_LEVEL_UPDATE")
  ev:RegisterEvent("UNIT_AURA") -- kept for completeness; throttled above

  parent:HookScript("OnShow", schedulePaint)
  parent:HookScript("OnHide", function() -- stop pending paints when hidden
    -- nothing to cancel for C_Timer.After; paint() checks IsShown() anyway
  end)

  paint() -- initial build
end

-- ----------------- Color helpers -----------------
local function ensureStore()
  PHP.settings = PHP.settings or {}
  PHP.settings.statsColors = PHP.settings.statsColors or {}
end

function PHP.SetStatBoxColor(label, r, g, b, a)
  ensureStore()
  PHP.settings.__statsColorsEnabled = true
  PHP.settings.statsColors[keyFor(label)] = { r, g, b, 1.0 }
end

function PHP.SetStatBoxHexColor(label, hex)
  if type(hex) ~= "string" then return end
  local r, g, b = hex:match("^#?(%x%x)(%x%x)(%x%x)$")
  if not r then return end
  r = tonumber(r,16)/255; g = tonumber(g,16)/255; b = tonumber(b,16)/255
  PHP.SetStatBoxColor(label, r, g, b, 1.0)
end

function PHP.ResetStatBoxColors()
  if not PHP.settings then return end
  PHP.settings.__statsColorsEnabled = false
end

StatsTab = M
_G.StatsTab = M
return M