--------------------------------------------------------------------------------
-- Nanami-UI: Stat Summary & Equipment List (StatSummary.lua)
--------------------------------------------------------------------------------

SFrames.StatSummary = {}
local SS = SFrames.StatSummary
local summaryFrame

--------------------------------------------------------------------------------
-- Theme (reuse CharacterPanel colors)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
    enchanted  = { 0.30, 1, 0.30 },
    noEnchant  = { 1, 0.35, 0.35 },
    setColor   = { 1, 0.85, 0.0 },
    defColor   = { 0.35, 0.90, 0.25 },
    physColor  = { 0.90, 0.75, 0.55 },
    spellColor = { 0.60, 0.80, 1.00 },
    regenColor = { 0.40, 0.90, 0.70 },
    statColors = {
        str  = { 0.78, 0.61, 0.43 },
        agi  = { 0.52, 1, 0.52 },
        sta  = { 0.75, 0.55, 0.25 },
        int  = { 0.41, 0.80, 0.94 },
        spi  = { 1, 1, 1 },
    },
    resistColors = {
        arcane = { 0.95, 0.90, 0.40 },
        fire   = { 1, 0.50, 0.15 },
        nature = { 0.35, 0.90, 0.25 },
        frost  = { 0.45, 0.70, 1.00 },
        shadow = { 0.60, 0.35, 0.90 },
    },
})

local PANEL_W = 220
local PANEL_H = 490
local HEADER_H = 24
local ROW_H = 14
local SECTION_GAP = 4
local SIDE_PAD = 6

local QUALITY_COLORS = {
    [0] = { 0.62, 0.62, 0.62 },
    [1] = { 1, 1, 1 },
    [2] = { 0.12, 1, 0 },
    [3] = { 0.0, 0.44, 0.87 },
    [4] = { 0.64, 0.21, 0.93 },
    [5] = { 1, 0.5, 0 },
}

local ENCHANTABLE_SLOTS = {
    { id = 1,  name = "HeadSlot",         label = "头部" },
    { id = 3,  name = "ShoulderSlot",     label = "肩部" },
    { id = 15, name = "BackSlot",         label = "背部" },
    { id = 5,  name = "ChestSlot",        label = "胸部" },
    { id = 9,  name = "WristSlot",        label = "手腕" },
    { id = 10, name = "HandsSlot",        label = "手套" },
    { id = 7,  name = "LegsSlot",         label = "腿部" },
    { id = 8,  name = "FeetSlot",         label = "脚部" },
    { id = 11, name = "Finger0Slot",      label = "戒指1" },
    { id = 12, name = "Finger1Slot",      label = "戒指2" },
    { id = 16, name = "MainHandSlot",     label = "主手" },
    { id = 17, name = "SecondaryHandSlot",label = "副手" },
    { id = 18, name = "RangedSlot",       label = "远程" },
}

local widgetId = 0

--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function GetFont()
    if SFrames and SFrames.GetFont then return SFrames:GetFont() end
    return "Fonts\\ARIALN.TTF"
end

local function NN(p)
    widgetId = widgetId + 1
    return "SFramesSS" .. (p or "") .. widgetId
end

local function SetBD(f, bg, bd)
    f:SetBackdrop({
        bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 }
    })
    local b = bg or T.bg
    local d = bd or T.border
    f:SetBackdropColor(b[1], b[2], b[3], b[4] or 1)
    f:SetBackdropBorderColor(d[1], d[2], d[3], d[4] or 1)
end

local function SetPBD(f, bg, bd)
    f:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        tile = false, tileSize = 0, edgeSize = 1,
        insets = { left = 1, right = 1, top = 1, bottom = 1 }
    })
    if bg then f:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1) end
    if bd then f:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1) end
end

local function FS(parent, size, jh, color)
    local fs = parent:CreateFontString(nil, "OVERLAY")
    fs:SetFont(GetFont(), size or 10, "OUTLINE")
    fs:SetJustifyH(jh or "LEFT")
    local c = color or T.valueText
    fs:SetTextColor(c[1], c[2], c[3])
    return fs
end

--------------------------------------------------------------------------------
-- Stat helpers
--------------------------------------------------------------------------------
local function GetIBL()
    if AceLibrary and AceLibrary.HasInstance and AceLibrary:HasInstance("ItemBonusLib-1.0") then
        local ok, lib = pcall(function() return AceLibrary("ItemBonusLib-1.0") end)
        if ok and lib then return lib end
    end
    return nil
end

local function GearB(key)
    local lib = GetIBL()
    if lib and lib.GetBonus then return lib:GetBonus(key) or 0 end
    return 0
end

local function TryAPI(names)
    for i = 1, table.getn(names) do
        local fn = _G[names[i]]
        if fn then
            local ok, val = pcall(fn)
            if ok and type(val) == "number" and val > 0 then return val end
        end
    end
    return 0
end

local function TryAPIa(names, a1)
    for i = 1, table.getn(names) do
        local fn = _G[names[i]]
        if fn then
            local ok, val = pcall(fn, a1)
            if ok and type(val) == "number" and val > 0 then return val end
        end
    end
    return 0
end

local function GetCS()
    if SFrames and SFrames.CharacterPanel and SFrames.CharacterPanel.CS then
        return SFrames.CharacterPanel.CS
    end
    return nil
end

--------------------------------------------------------------------------------
-- Scroll frame
--------------------------------------------------------------------------------
local function MakeScroll(parent, w, h)
    local holder = CreateFrame("Frame", NN("H"), parent)
    holder:SetWidth(w)
    holder:SetHeight(h)

    local sf = CreateFrame("ScrollFrame", NN("S"), holder)
    sf:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, 0)
    sf:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -8, 0)

    local child = CreateFrame("Frame", NN("C"), sf)
    child:SetWidth(w - 12)
    child:SetHeight(1)
    sf:SetScrollChild(child)

    local sb = CreateFrame("Slider", NN("B"), holder)
    sb:SetWidth(5)
    sb:SetPoint("TOPRIGHT", holder, "TOPRIGHT", -1, -2)
    sb:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", -1, 2)
    sb:SetOrientation("VERTICAL")
    sb:SetMinMaxValues(0, 1)
    sb:SetValue(0)
    SetPBD(sb, { 0.1, 0.1, 0.12, 0.4 }, { 0.15, 0.15, 0.18, 0.3 })

    local thumb = sb:CreateTexture(nil, "OVERLAY")
    thumb:SetTexture("Interface\\Buttons\\WHITE8X8")
    thumb:SetVertexColor(0.4, 0.4, 0.48, 0.7)
    thumb:SetWidth(5)
    thumb:SetHeight(24)
    sb:SetThumbTexture(thumb)

    sb:SetScript("OnValueChanged", function()
        sf:SetVerticalScroll(this:GetValue())
    end)
    holder:EnableMouseWheel(1)
    holder:SetScript("OnMouseWheel", function()
        local cur = sb:GetValue()
        local step = 24
        local lo, mx = sb:GetMinMaxValues()
        if arg1 > 0 then
            sb:SetValue(math.max(cur - step, 0))
        else
            sb:SetValue(math.min(cur + step, mx))
        end
    end)

    holder.sf = sf
    holder.child = child
    holder.sb = sb
    holder.UpdateH = function(_, ch)
        child:SetHeight(ch)
        local visH = sf:GetHeight()
        local maxS = math.max(ch - visH, 0)
        sb:SetMinMaxValues(0, maxS)
        if maxS == 0 then sb:Hide() else sb:Show() end
        sb:SetValue(math.min(sb:GetValue(), maxS))
    end
    return holder
end

--------------------------------------------------------------------------------
-- Enchant detection (comprehensive for vanilla 1.12 / Turtle WoW)
--
-- Vanilla WoW tooltip green text types:
--   1. Enchant effects (what we detect)
--   2. "装备：" / "Equip:" item innate effects
--   3. "使用：" / "Use:" item use effects
--   4. "击中时可能：" / "Chance on hit:" proc effects
--   5. "(X) 套装:" active set bonuses
-- Strategy: exclude #2-5, then positive-match enchant patterns.
--
-- Covers: standard enchanting, libram/arcanum (head/legs), ZG class enchants,
-- ZG/Naxx shoulder augments, armor kits, weapon chains, scopes, spurs,
-- counterweights, and Turtle WoW custom enchants.
--------------------------------------------------------------------------------
local scanTip

local function EnsureTip()
    if not scanTip then
        scanTip = CreateFrame("GameTooltip", "SFramesSScanTip", nil, "GameTooltipTemplate")
    end
    scanTip:SetOwner(UIParent, "ANCHOR_NONE")
    return scanTip
end

local PROC_ENCHANTS = {
    "十字军", "Crusader",
    "吸取生命", "Lifestealing",
    "灼热武器", "Fiery Weapon", "火焰武器",
    "寒冰", "Icy Chill",
    "邪恶武器", "Unholy Weapon",
    "恶魔杀手", "Demonslaying",
    "无法被缴械", "Cannot be Disarmed",
}

local function IsEnchantLine(txt)
    if string.find(txt, "%+%d") then return true end
    if string.find(txt, "%d+%%") then return true end
    for i = 1, table.getn(PROC_ENCHANTS) do
        if string.find(txt, PROC_ENCHANTS[i]) then return true end
    end
    return false
end

local function GetEnchant(slotId)
    local tip = EnsureTip()
    tip:ClearLines()
    tip:SetInventoryItem("player", slotId)
    local n = tip:NumLines()
    if not n or n < 2 then return false, nil end

    for i = 2, n do
        local obj = _G["SFramesSScanTipTextLeft" .. i]
        if obj then
            local txt = obj:GetText()
            if txt and txt ~= "" then
                local r, g, b = obj:GetTextColor()
                if g > 0.8 and r < 0.5 and b < 0.5 then
                    local skip = false
                    if string.find(txt, "装备：") or string.find(txt, "装备:") or string.find(txt, "Equip:") then
                        skip = true
                    elseif string.find(txt, "使用：") or string.find(txt, "使用:") or string.find(txt, "Use:") then
                        skip = true
                    elseif string.find(txt, "击中时可能") or string.find(txt, "Chance on hit") then
                        skip = true
                    elseif string.find(txt, "^%(") then
                        skip = true
                    elseif string.find(txt, "套装：") or string.find(txt, "套装:") or string.find(txt, "Set:") then
                        skip = true
                    end
                    if not skip and IsEnchantLine(txt) then
                        return true, txt
                    end
                end
            end
        end
    end
    return false, nil
end

--------------------------------------------------------------------------------
-- Set bonus detection
--------------------------------------------------------------------------------
local function GetSets()
    local tip = EnsureTip()
    local sets = {}
    local seen = {}
    local slots = { 1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }

    for si = 1, table.getn(slots) do
        local sid = slots[si]
        local link = GetInventoryItemLink("player", sid)
        if link then
            tip:ClearLines()
            tip:SetOwner(UIParent, "ANCHOR_NONE")
            tip:SetInventoryItem("player", sid)
            local nl = tip:NumLines()
            if nl then
                for li = 1, nl do
                    local obj = _G["SFramesSScanTipTextLeft" .. li]
                    if obj then
                        local txt = obj:GetText()
                        if txt then
                            local a, b, sn, sc, sm
                            a, b, sn, sc, sm = string.find(txt, "^(.-)%s*%((%d+)/(%d+)%)$")
                            if sn and sc and sm then
                                sn = string.gsub(sn, "^%s+", "")
                                sn = string.gsub(sn, "%s+$", "")
                                if sn ~= "" and not seen[sn] then
                                    seen[sn] = true
                                    table.insert(sets, {
                                        name = sn,
                                        current = tonumber(sc) or 0,
                                        max = tonumber(sm) or 0,
                                    })
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return sets
end

--------------------------------------------------------------------------------
-- Build the panel
--------------------------------------------------------------------------------
local function BuildPanel()
    if summaryFrame then return summaryFrame end

    local f = CreateFrame("Frame", "SFramesStatSummary", UIParent)
    f:SetWidth(PANEL_W)
    f:SetHeight(PANEL_H)
    f:SetPoint("CENTER", UIParent, "CENTER", 200, 0)
    f:SetFrameStrata("DIALOG")
    f:SetFrameLevel(100)
    f:EnableMouse(true)
    f:SetMovable(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", function() this:StartMoving() end)
    f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
    f:SetClampedToScreen(true)
    SetBD(f, T.bg, T.border)
    f:Hide()

    -- Header
    local hdr = FS(f, 11, "LEFT", T.gold)
    hdr:SetPoint("TOPLEFT", f, "TOPLEFT", SIDE_PAD + 2, -6)
    hdr:SetText("属性总览")

    -- Tab buttons
    local tabW = 60
    f.tabs = {}
    f.curTab = 1
    local tNames = { "属性", "装备" }
    for i = 1, 2 do
        local btn = CreateFrame("Button", NN("T"), f)
        btn:SetWidth(tabW)
        btn:SetHeight(16)
        btn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(SIDE_PAD + (2 - i) * (tabW + 2) + 20), -5)
        btn:SetFrameLevel(f:GetFrameLevel() + 2)
        SetPBD(btn, T.tabBg, T.tabBorder)
        local lbl = FS(btn, 9, "CENTER", T.tabText)
        lbl:SetPoint("CENTER", btn, "CENTER", 0, 0)
        lbl:SetText(tNames[i])
        btn.lbl = lbl
        btn.idx = i
        btn:SetScript("OnClick", function() SS:SetTab(this.idx) end)
        f.tabs[i] = btn
    end

    -- Close
    local cb = CreateFrame("Button", nil, f)
    cb:SetWidth(14)
    cb:SetHeight(14)
    cb:SetPoint("TOPRIGHT", f, "TOPRIGHT", -5, -5)
    cb:SetFrameLevel(f:GetFrameLevel() + 3)
    SetBD(cb, T.buttonDownBg or { 0.35, 0.06, 0.06, 0.85 }, T.btnBorder or { 0.45, 0.1, 0.1, 0.6 })
    local cx = FS(cb, 8, "CENTER", { 1, 0.7, 0.7 })
    cx:SetPoint("CENTER", cb, "CENTER", 0, 0)
    cx:SetText("x")
    cb:SetScript("OnClick", function() f:Hide() end)

    -- Sep
    local sep = f:CreateTexture(nil, "ARTWORK")
    sep:SetTexture("Interface\\Buttons\\WHITE8X8")
    sep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
    sep:SetHeight(1)
    sep:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -HEADER_H)
    sep:SetPoint("TOPRIGHT", f, "TOPRIGHT", -4, -HEADER_H)

    -- Content
    local cH = PANEL_H - HEADER_H - 8
    local cW = PANEL_W - 8

    f.statsScroll = MakeScroll(f, cW, cH)
    f.statsScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(HEADER_H + 2))

    f.equipScroll = MakeScroll(f, cW, cH)
    f.equipScroll:SetPoint("TOPLEFT", f, "TOPLEFT", 4, -(HEADER_H + 2))
    f.equipScroll:Hide()

    summaryFrame = f
    return f
end

--------------------------------------------------------------------------------
-- Tab switching
--------------------------------------------------------------------------------
function SS:SetTab(idx)
    if not summaryFrame then return end
    summaryFrame.curTab = idx
    for i = 1, 2 do
        local btn = summaryFrame.tabs[i]
        if i == idx then
            SetPBD(btn, T.tabActiveBg, T.tabActiveBorder)
            btn.lbl:SetTextColor(T.tabActiveText[1], T.tabActiveText[2], T.tabActiveText[3])
        else
            SetPBD(btn, T.tabBg, T.tabBorder)
            btn.lbl:SetTextColor(T.tabText[1], T.tabText[2], T.tabText[3])
        end
    end
    if idx == 1 then
        summaryFrame.statsScroll:Show()
        summaryFrame.equipScroll:Hide()
        local ok, err = pcall(function() SS:BuildStats() end)
        if not ok then
            DEFAULT_CHAT_FRAME:AddMessage("|cffff4444SS BuildStats err: " .. tostring(err) .. "|r")
        end
    else
        summaryFrame.statsScroll:Hide()
        summaryFrame.equipScroll:Show()
        local ok, err = pcall(function() SS:BuildEquip() end)
        if not ok then
            DEFAULT_CHAT_FRAME:AddMessage("|cffff4444SS BuildEquip err: " .. tostring(err) .. "|r")
        end
    end
end

--------------------------------------------------------------------------------
-- Helpers for building rows
--------------------------------------------------------------------------------
local function HideRows(parent)
    if parent._r then
        for i = 1, table.getn(parent._r) do
            local r = parent._r[i]
            if r and r.Hide then r:Hide() end
        end
    end
    parent._r = {}
end

local function AddHeader(p, txt, y, clr)
    local f1 = FS(p, 10, "LEFT", clr or T.sectionTitle)
    f1:SetPoint("TOPLEFT", p, "TOPLEFT", 4, y)
    f1:SetText(txt)
    tinsert(p._r, f1)

    local s = p:CreateTexture(nil, "ARTWORK")
    s:SetTexture("Interface\\Buttons\\WHITE8X8")
    s:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
    s:SetHeight(1)
    s:SetPoint("TOPLEFT", p, "TOPLEFT", 4, y - 13)
    s:SetPoint("TOPRIGHT", p, "TOPRIGHT", -4, y - 13)
    tinsert(p._r, s)
    return y - 16
end

local function AddRow(p, lbl, val, y, lc, vc)
    local f1 = FS(p, 9, "LEFT", lc or T.labelText)
    f1:SetPoint("TOPLEFT", p, "TOPLEFT", 8, y)
    f1:SetText(lbl)
    tinsert(p._r, f1)

    local f2 = FS(p, 9, "RIGHT", vc or T.valueText)
    f2:SetPoint("TOPRIGHT", p, "TOPRIGHT", -12, y)
    f2:SetWidth(100)
    f2:SetJustifyH("RIGHT")
    f2:SetText(val)
    tinsert(p._r, f2)
    return y - ROW_H
end

--------------------------------------------------------------------------------
-- Stats page
--------------------------------------------------------------------------------
function SS:BuildStats()
    local child = summaryFrame.statsScroll.child
    HideRows(child)
    local y = -4

    -- Primary Stats
    y = AddHeader(child, "主属性:", y)
    local sn = { "力量", "敏捷", "耐力", "智力", "精神" }
    local sc = { T.statColors.str, T.statColors.agi, T.statColors.sta, T.statColors.int, T.statColors.spi }
    for i = 1, 5 do
        local _, eff = UnitStat("player", i)
        y = AddRow(child, sn[i], tostring(math.floor(eff or 0)), y, sc[i], sc[i])
    end

    y = y - SECTION_GAP

    -- Defense
    y = AddHeader(child, "防御属性:", y, T.defColor)
    local hp = UnitHealthMax("player") or 0
    y = AddRow(child, "生命值", tostring(hp), y, nil, T.defColor)

    local dodge = 0
    if GetDodgeChance then dodge = GetDodgeChance() or 0 end
    if dodge == 0 then dodge = GearB("DODGE") end
    y = AddRow(child, "躲闪", string.format("%.2f%%", dodge), y)

    local baseA, effA = UnitArmor("player")
    effA = math.floor(effA or baseA or 0)
    y = AddRow(child, "护甲", tostring(effA), y)

    local lvl = UnitLevel("player") or 60
    local k1 = 400 + 85 * lvl
    local p1 = effA / (effA + k1) * 100
    if p1 > 75 then p1 = 75 end
    y = AddRow(child, "物理免伤同级", string.format("%.2f%%", p1), y)

    local k2 = 400 + 85 * (lvl + 3)
    local p2 = effA / (effA + k2) * 100
    if p2 > 75 then p2 = 75 end
    y = AddRow(child, "物理免伤骷髅级", string.format("%.2f%%", p2), y)

    y = y - SECTION_GAP

    -- Resistances
    y = AddHeader(child, "抗性:", y, T.resistColors.arcane)
    local rInfo = {
        { 6, "奥术抗性", T.resistColors.arcane },
        { 2, "火焰抗性", T.resistColors.fire },
        { 3, "自然抗性", T.resistColors.nature },
        { 4, "冰霜抗性", T.resistColors.frost },
        { 5, "暗影抗性", T.resistColors.shadow },
    }
    for i = 1, table.getn(rInfo) do
        local ri = rInfo[i]
        local _, tot = UnitResistance("player", ri[1])
        y = AddRow(child, ri[2], tostring(math.floor(tot or 0)), y, ri[3], ri[3])
    end

    y = y - SECTION_GAP

    -- Physical
    y = AddHeader(child, "物理:", y, T.physColor)
    local mb, mp, mn = UnitAttackPower("player")
    local mAP = math.floor((mb or 0) + (mp or 0) + (mn or 0))
    local rb, rp, rn = UnitRangedAttackPower("player")
    local rAP = math.floor((rb or 0) + (rp or 0) + (rn or 0))
    y = AddRow(child, "近战/远程攻强", mAP .. "/" .. rAP, y, nil, T.physColor)

    local cs = GetCS()
    local mHit = cs and cs.SafeGetMeleeHit and cs.SafeGetMeleeHit() or 0
    if mHit == 0 then
        mHit = TryAPI({ "GetHitModifier", "GetMeleeHitModifier" })
        if mHit == 0 then mHit = GearB("TOHIT") end
    end
    y = AddRow(child, "近战/远程命中", string.format("+%d%%/+%d%%", mHit, mHit), y)

    local mCrit = cs and cs.SafeGetMeleeCrit and cs.SafeGetMeleeCrit() or 0
    if mCrit == 0 then
        mCrit = TryAPI({ "GetCritChance", "GetMeleeCritChance" })
        if mCrit == 0 then mCrit = GearB("CRIT") end
    end
    local rCrit = cs and cs.SafeGetRangedCrit and cs.SafeGetRangedCrit() or 0
    if rCrit == 0 then
        rCrit = TryAPI({ "GetRangedCritChance" })
        if rCrit == 0 then rCrit = mCrit end
    end
    y = AddRow(child, "近战/远程暴击", string.format("%.2f%%/%.2f%%", mCrit, rCrit), y)

    y = y - SECTION_GAP

    -- Spell
    y = AddHeader(child, "法术:", y, T.spellColor)
    local mana = UnitManaMax("player") or 0
    y = AddRow(child, "法力值", tostring(mana), y, nil, T.spellColor)

    local sDmg = 0
    if GetSpellBonusDamage then
        for s = 2, 7 do
            local d = GetSpellBonusDamage(s) or 0
            if d > sDmg then sDmg = d end
        end
    end
    if sDmg == 0 then
        local lib = GetIBL()
        if lib and lib.GetBonus then
            local baseDmg = lib:GetBonus("DMG") or 0
            sDmg = baseDmg
            local schools = { "FIREDMG","FROSTDMG","SHADOWDMG","ARCANEDMG","NATUREDMG","HOLYDMG" }
            for _, sk in ipairs(schools) do
                local sv = baseDmg + (lib:GetBonus(sk) or 0)
                if sv > sDmg then sDmg = sv end
            end
        end
    end
    y = AddRow(child, "法术伤害", tostring(math.floor(sDmg)), y)

    local sHeal = 0
    if GetSpellBonusHealing then sHeal = GetSpellBonusHealing() or 0 end
    if sHeal == 0 then sHeal = GearB("HEAL") end
    y = AddRow(child, "法术治疗", tostring(math.floor(sHeal)), y)

    local sCrit = cs and cs.SafeGetSpellCrit and cs.SafeGetSpellCrit() or 0
    if sCrit == 0 then
        sCrit = TryAPIa({ "GetSpellCritChance" }, 2)
        if sCrit == 0 then sCrit = GearB("SPELLCRIT") end
    end
    y = AddRow(child, "法术暴击", string.format("%.2f%%", sCrit), y)

    local sHit = cs and cs.SafeGetSpellHit and cs.SafeGetSpellHit() or 0
    if sHit == 0 then
        sHit = TryAPI({ "GetSpellHitModifier" })
        if sHit == 0 then sHit = GearB("SPELLTOHIT") end
    end
    y = AddRow(child, "法术命中", string.format("+%d%%", sHit), y)

    local sHaste = GearB("SPELLHASTE")
    if sHaste > 0 then
        y = AddRow(child, "急速", string.format("+%.2f%%", sHaste), y)
    end

    local sPen = GearB("SPELLPEN")
    if sPen > 0 then
        y = AddRow(child, "法术穿透", "+" .. tostring(math.floor(sPen)), y)
    end

    y = y - SECTION_GAP

    -- Regen
    y = AddHeader(child, "回复:", y, T.regenColor)

    local mp5 = GearB("MANAREG")
    y = AddRow(child, "装备回蓝", tostring(math.floor(mp5)) .. " MP/5s", y, nil, T.regenColor)

    local _, spi = UnitStat("player", 5)
    spi = math.floor(spi or 0)
    local spReg = math.floor(15 + spi / 5)
    y = AddRow(child, "精神回蓝", spReg .. " MP/2s", y)

    y = y - SECTION_GAP

    -- Set bonuses
    local ok2, sets = pcall(GetSets)
    if ok2 and sets and table.getn(sets) > 0 then
        y = AddHeader(child, "套装:", y, T.setColor)
        for i = 1, table.getn(sets) do
            local s = sets[i]
            y = AddRow(child, s.name, "(" .. s.current .. "/" .. s.max .. ")", y, T.setColor, T.setColor)
        end
    end

    summaryFrame.statsScroll:UpdateH(math.abs(y) + 12)
end

--------------------------------------------------------------------------------
-- Equipment page
--------------------------------------------------------------------------------
function SS:BuildEquip()
    local child = summaryFrame.equipScroll.child
    HideRows(child)
    local y = -4

    y = AddHeader(child, "装备列表 & 附魔检查:", y, T.gold)

    local totalSlots = 0
    local enchCount = 0

    for si = 1, table.getn(ENCHANTABLE_SLOTS) do
        local slot = ENCHANTABLE_SLOTS[si]
        local link = GetInventoryItemLink("player", slot.id)

        if link then
            totalSlots = totalSlots + 1
            local _, _, rawName = string.find(link, "%[(.-)%]")
            local itemName = rawName or slot.label

            local quality = nil
            local _, _, qH = string.find(link, "|c(%x+)|H")
            local qMap = {}
            qMap["ff9d9d9d"] = 0
            qMap["ffffffff"] = 1
            qMap["ff1eff00"] = 2
            qMap["ff0070dd"] = 3
            qMap["ffa335ee"] = 4
            qMap["ffff8000"] = 5
            if qH then quality = qMap[qH] end
            local nc = (quality and QUALITY_COLORS[quality]) or T.valueText

            -- Slot label
            local sf = FS(child, 8, "LEFT", T.dimText)
            sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
            sf:SetText(slot.label)
            sf:SetWidth(32)
            tinsert(child._r, sf)

            -- Item name
            local nf = FS(child, 9, "LEFT", nc)
            nf:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
            nf:SetWidth(130)
            if string.len(itemName) > 18 then
                itemName = string.sub(itemName, 1, 16) .. ".."
            end
            nf:SetText(itemName)
            tinsert(child._r, nf)

            -- Enchant check
            local hasE, eTxt = false, nil
            local eOk, eR1, eR2 = pcall(GetEnchant, slot.id)
            if eOk then hasE = eR1; eTxt = eR2 end

            local ico = FS(child, 9, "RIGHT")
            ico:SetPoint("TOPRIGHT", child, "TOPRIGHT", -8, y)
            if hasE then
                ico:SetTextColor(T.enchanted[1], T.enchanted[2], T.enchanted[3])
                ico:SetText("*")
                enchCount = enchCount + 1
            else
                ico:SetTextColor(T.noEnchant[1], T.noEnchant[2], T.noEnchant[3])
                ico:SetText("-")
            end
            tinsert(child._r, ico)

            y = y - ROW_H

            if hasE and eTxt then
                local ef = FS(child, 8, "LEFT", T.enchanted)
                ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
                ef:SetWidth(155)
                ef:SetText("  " .. eTxt)
                tinsert(child._r, ef)
                y = y - 12
            elseif not hasE then
                local ef = FS(child, 8, "LEFT", T.noEnchant)
                ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
                ef:SetText("  未附魔")
                tinsert(child._r, ef)
                y = y - 12
            end
            y = y - 2
        else
            local sf = FS(child, 8, "LEFT", T.dimText)
            sf:SetPoint("TOPLEFT", child, "TOPLEFT", 4, y)
            sf:SetText(slot.label)
            tinsert(child._r, sf)

            local ef = FS(child, 9, "LEFT", T.dimText)
            ef:SetPoint("TOPLEFT", child, "TOPLEFT", 38, y)
            ef:SetText("-- 未装备 --")
            tinsert(child._r, ef)
            y = y - ROW_H - 2
        end
    end

    y = y - SECTION_GAP
    y = AddHeader(child, "附魔统计:", y, T.gold)
    local sc2 = (enchCount == totalSlots) and T.enchanted or T.noEnchant
    y = AddRow(child, "已附魔/总装备", enchCount .. "/" .. totalSlots, y, nil, sc2)
    if enchCount < totalSlots then
        y = AddRow(child, "缺少附魔", tostring(totalSlots - enchCount) .. " 件", y, T.noEnchant, T.noEnchant)
    end

    summaryFrame.equipScroll:UpdateH(math.abs(y) + 12)
end

--------------------------------------------------------------------------------
-- Public API
--------------------------------------------------------------------------------
function SS:Toggle()
    local ok, err = pcall(function()
        BuildPanel()
        if summaryFrame:IsShown() then
            summaryFrame:Hide()
            return
        end
        local cpf = _G["SFramesCharacterPanel"]
        if cpf and cpf:IsShown() then
            summaryFrame:ClearAllPoints()
            summaryFrame:SetPoint("TOPLEFT", cpf, "TOPRIGHT", 2, 0)
        else
            summaryFrame:ClearAllPoints()
            summaryFrame:SetPoint("CENTER", UIParent, "CENTER", 200, 0)
        end
        local scale = SFramesDB and SFramesDB.charPanelScale or 1.0
        summaryFrame:SetScale(scale)
        summaryFrame:Show()
        SS:SetTab(summaryFrame.curTab or 1)
    end)
    if not ok then
        DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] SS:Toggle error: " .. tostring(err) .. "|r")
    end
end

function SS:Show()
    local ok, err = pcall(function()
        BuildPanel()
        local cpf = _G["SFramesCharacterPanel"]
        if cpf and cpf:IsShown() then
            summaryFrame:ClearAllPoints()
            summaryFrame:SetPoint("TOPLEFT", cpf, "TOPRIGHT", 2, 0)
        end
        local scale = SFramesDB and SFramesDB.charPanelScale or 1.0
        summaryFrame:SetScale(scale)
        summaryFrame:Show()
        SS:SetTab(summaryFrame.curTab or 1)
    end)
    if not ok then
        DEFAULT_CHAT_FRAME:AddMessage("|cffff4444[Nanami-UI] SS:Show error: " .. tostring(err) .. "|r")
    end
end

function SS:Hide()
    if summaryFrame then summaryFrame:Hide() end
end

function SS:IsShown()
    return summaryFrame and summaryFrame:IsShown()
end

function SS:Refresh()
    if not summaryFrame or not summaryFrame:IsShown() then return end
    SS:SetTab(summaryFrame.curTab or 1)
end

--------------------------------------------------------------------------------
-- Events
--------------------------------------------------------------------------------
local evf = CreateFrame("Frame", "SFramesSSEv", UIParent)
evf:RegisterEvent("UNIT_INVENTORY_CHANGED")
evf:RegisterEvent("PLAYER_AURAS_CHANGED")
evf:RegisterEvent("UNIT_ATTACK_POWER")
evf:RegisterEvent("UNIT_RESISTANCES")
evf:SetScript("OnEvent", function()
    if summaryFrame and summaryFrame:IsShown() then
        SS:Refresh()
    end
end)

DEFAULT_CHAT_FRAME:AddMessage("SF: Loading StatSummary.lua...")
