local addonName, ns = ...

-- Stats Display Module
-- Floating text showing secondary stats (Crit/Haste/Mastery/Vers),
-- optional primary stats (Str/Agi/Int) and tertiary stats (Leech/Avoidance/Speed).

local SS = {}
ns.Stats = SS

local function GetBorderColor(db)
    if not db then return 0, 207/255, 1.0 end
    if db.borderUseClassColor then
        local cc = RAID_CLASS_COLORS and RAID_CLASS_COLORS[select(2, UnitClass("player"))]
        if cc then return cc.r, cc.g, cc.b end
    end
    local r = db.borderColorR or 0
    local g = db.borderColorG or (207/255)
    local b = db.borderColorB or 1.0
    return r, g, b
end


local W = ns.Widgets

local UPDATE_INTERVAL = 0.5
local PADDING         = 8
local timeSince       = 0

local statsFrame = nil
local statsText  = nil

---------------------------------------------------------------------------
-- Helpers
---------------------------------------------------------------------------

local function GetDB()
    return BrightQoL.swiftStats
end

local function GetFont()
    local db = GetDB()
    return db.font or "Fonts\\FRIZQT__.TTF"
end

local function Hex(db, rKey, gKey, bKey)
    local r = db[rKey] or 1
    local g = db[gKey] or 1
    local b = db[bKey] or 1
    return string.format("%02x%02x%02x",
        math.floor(r * 255 + 0.5),
        math.floor(g * 255 + 0.5),
        math.floor(b * 255 + 0.5))
end

---------------------------------------------------------------------------
-- Backdrop
---------------------------------------------------------------------------

function SS:ApplyBackdrop()
    if not statsFrame then return end
    local db = GetDB()
    local wantBg  = db.showBackground ~= false
    local wantBrd = db.showBorder     ~= false
    if not wantBg and not wantBrd then
        statsFrame:SetBackdrop(nil)
        return
    end
    statsFrame:SetBackdrop({
        bgFile   = wantBg  and [[Interface\Buttons\WHITE8x8]] or nil,
        edgeFile = wantBrd and [[Interface\Buttons\WHITE8x8]] or nil,
        edgeSize = wantBrd and 1 or 0,
        insets   = { left = 1, right = 1, top = 1, bottom = 1 },
    })
    if wantBg  then statsFrame:SetBackdropColor(0.05, 0.05, 0.08, 0.85) end
    if wantBrd then local _r,_g,_b = GetBorderColor(BrightQoL.swiftStats); statsFrame:SetBackdropBorderColor(_r,_g,_b, 0.5) end
end

---------------------------------------------------------------------------
-- Stat formatting
---------------------------------------------------------------------------

local function FormatValue(rating, pct, db, statHex)
    local ratingHex = db.matchValueColor and statHex or Hex(db, "colorRatingR",     "colorRatingG",     "colorRatingB")
    local pctHex    = db.matchValueColor and statHex or Hex(db, "colorPercentageR", "colorPercentageG", "colorPercentageB")

    if db.showRating and db.showPercentage then
        return string.format("|cff%s%d|r |cff808080(|r|cff%s%.2f%%|r|cff808080)|r",
            ratingHex, rating, pctHex, pct)
    elseif db.showRating then
        return string.format("|cff%s%d|r", ratingHex, rating)
    elseif db.showPercentage then
        return string.format("|cff%s%.2f%%|r", pctHex, pct)
    end
    return string.format("|cff%s%d|r", ratingHex, rating)
end

---------------------------------------------------------------------------
-- UpdateStats
---------------------------------------------------------------------------

local function UpdateStats()
    if not statsText or not statsFrame then return end
    local db = GetDB()
    if not db then return end

    if not db.enabled and not db.unlock then return end

    -- Secondaries
    local critPct     = GetCritChance()
    local hastePct    = GetHaste()
    local mastPct     = GetMasteryEffect()
    local versPct     = GetCombatRatingBonus(CR_VERSATILITY_DAMAGE_DONE)
                      + GetVersatilityBonus(CR_VERSATILITY_DAMAGE_DONE)
    local critRating  = GetCombatRating(CR_CRIT_MELEE)
    local hasteRating = GetCombatRating(CR_HASTE_MELEE)
    local mastRating  = GetCombatRating(CR_MASTERY)
    local versRating  = GetCombatRating(CR_VERSATILITY_DAMAGE_DONE)

    -- Tertiaries (use GetCombatRatingBonus for speed, NOT GetSpeed() which is movement yards/sec)
    local leechPct    = GetLifesteal()
    local leechRating = GetCombatRating(CR_LIFESTEAL)
    local avoidPct    = GetAvoidance()
    local avoidRating = GetCombatRating(CR_AVOIDANCE)
    local speedPct    = GetCombatRatingBonus(CR_SPEED)
    local speedRating = GetCombatRating(CR_SPEED)

    local hCrit  = Hex(db, "colorCritR",      "colorCritG",      "colorCritB")
    local hHaste = Hex(db, "colorHasteR",     "colorHasteG",     "colorHasteB")
    local hMast  = Hex(db, "colorMasteryR",   "colorMasteryG",   "colorMasteryB")
    local hVers  = Hex(db, "colorVersR",      "colorVersG",      "colorVersB")
    local hLeech = Hex(db, "colorLeechR",     "colorLeechG",     "colorLeechB")
    local hAvoid = Hex(db, "colorAvoidanceR", "colorAvoidanceG", "colorAvoidanceB")
    local hSpeed = Hex(db, "colorSpeedR",     "colorSpeedG",     "colorSpeedB")
    local hPrim  = Hex(db, "colorPrimaryR",   "colorPrimaryG",   "colorPrimaryB")

    local lines = {}
    local n = 0

    local hVal = db.matchValueColor and hPrim or Hex(db, "colorRatingR", "colorRatingG", "colorRatingB")
    if db.showStrength  then n=n+1; lines[n] = string.format("|cff%sStrength:|r |cff%s%d|r",  hPrim, hVal, UnitStat("player",1)) end
    if db.showAgility   then n=n+1; lines[n] = string.format("|cff%sAgility:|r |cff%s%d|r",   hPrim, hVal, UnitStat("player",2)) end
    if db.showIntellect then n=n+1; lines[n] = string.format("|cff%sIntellect:|r |cff%s%d|r", hPrim, hVal, UnitStat("player",4)) end

    n=n+1; lines[n] = string.format("|cff%sCrit:|r %s",    hCrit,  FormatValue(critRating,  critPct,  db, hCrit))
    n=n+1; lines[n] = string.format("|cff%sHaste:|r %s",   hHaste, FormatValue(hasteRating, hastePct, db, hHaste))
    n=n+1; lines[n] = string.format("|cff%sMastery:|r %s", hMast,  FormatValue(mastRating,  mastPct,  db, hMast))
    n=n+1; lines[n] = string.format("|cff%sVers:|r %s",    hVers,  FormatValue(versRating,  versPct,  db, hVers))

    if db.showTertiary then
        if db.showLeech    and (not db.hideZeroTertiary or leechRating > 0) then
            n=n+1; lines[n] = string.format("|cff%sLeech:|r %s",     hLeech, FormatValue(leechRating, leechPct, db, hLeech))
        end
        if db.showAvoidance and (not db.hideZeroTertiary or avoidRating > 0) then
            n=n+1; lines[n] = string.format("|cff%sAvoidance:|r %s", hAvoid, FormatValue(avoidRating, avoidPct, db, hAvoid))
        end
        if db.showSpeed    and (not db.hideZeroTertiary or speedRating > 0) then
            n=n+1; lines[n] = string.format("|cff%sSpeed:|r %s",     hSpeed, FormatValue(speedRating, speedPct, db, hSpeed))
        end
    end

    statsText:SetText(table.concat(lines, "\n", 1, n))

    -- Resize from TOPLEFT so the frame grows downward/rightward without jumping
    local tw = statsText:GetStringWidth()
    local th = statsText:GetStringHeight()
    statsFrame:SetSize(tw + PADDING * 2, th + PADDING * 2)
end

---------------------------------------------------------------------------
-- Position
-- KEY FIX: use db.point as BOTH point and relativePoint (same as CombatTimer).
-- This way after MakeDraggable saves db.point from GetPoint(), both sides
-- of SetPoint are always consistent — no random position jumping.
---------------------------------------------------------------------------

function SS:ApplyPosition()
    if not statsFrame then return end
    local db = GetDB()
    statsFrame:ClearAllPoints()
    local pt = db.point or "TOPLEFT"
    statsFrame:SetPoint(pt, UIParent, pt, db.x or 0, db.y or -200)
    statsFrame:SetScale(db.scale or 1.0)
end

---------------------------------------------------------------------------
-- Init / Enable / Disable / Refresh
---------------------------------------------------------------------------

function SS:Init()
    if statsFrame then
        self:ApplyPosition()
        self:Refresh()
        return
    end

    statsFrame = CreateFrame("Frame", "BrightQoL_StatsFrame", UIParent, "BackdropTemplate")
    statsFrame:SetSize(200, 80)
    statsFrame:SetClampedToScreen(true)
    statsFrame:SetFrameStrata("MEDIUM")

    statsText = statsFrame:CreateFontString(nil, "OVERLAY")
    statsText:SetFont(GetFont(), GetDB().fontSize or 14, "OUTLINE")
    -- Anchor text to TOPLEFT so frame resizes downward/rightward predictably
    statsText:SetPoint("TOPLEFT", PADDING, -PADDING)
    statsText:SetJustifyH("LEFT")
    statsText:SetTextColor(1, 1, 1, 1)

    self:ApplyPosition()
    self:ApplyBackdrop()

    -- KEY FIX: don't save relPoint separately — use anchorToKey so both
    -- point and relPoint are always saved consistently from GetPoint()
    W.MakeDraggable(statsFrame, {
        db          = GetDB(),
        enableMouse = false,
        userPlaced  = false,
        anchorToKey = "relPoint",
    })

    statsFrame:SetScript("OnUpdate", function(self, elapsed)
        timeSince = timeSince + elapsed
        if timeSince >= UPDATE_INTERVAL then
            UpdateStats()
            timeSince = 0
        end
    end)

    self:Refresh()
end

function SS:Enable()
    if not statsFrame then self:Init(); return end
    statsFrame:SetScript("OnUpdate", function(self, elapsed)
        timeSince = timeSince + elapsed
        if timeSince >= UPDATE_INTERVAL then
            UpdateStats()
            timeSince = 0
        end
    end)
    statsFrame:Show()
    UpdateStats()
end

function SS:Disable()
    if statsFrame then
        statsFrame:SetScript("OnUpdate", nil)
        statsFrame:Hide()
    end
end

function SS:Refresh()
    if not statsFrame then return end
    local db = GetDB()
    self:ApplyBackdrop()
    statsText:SetFont(GetFont(), db.fontSize or 14, "OUTLINE")
    self:ApplyPosition()
    statsFrame:EnableMouse(db.unlock or false)

    if db.unlock then
        -- Always show in unlock mode so user can reposition
        statsFrame:SetScript("OnUpdate", function(self, elapsed)
            timeSince = timeSince + elapsed
            if timeSince >= UPDATE_INTERVAL then
                UpdateStats()
                timeSince = 0
            end
        end)
        statsFrame:Show()
        UpdateStats()
    elseif db.enabled then
        self:Enable()
    else
        self:Disable()
    end
end

SS.UpdateStats = UpdateStats

-- Auto-init on login
local ssInitFrame = CreateFrame("Frame")
ssInitFrame:RegisterEvent("PLAYER_LOGIN")
ssInitFrame:SetScript("OnEvent", function(self)
    self:UnregisterEvent("PLAYER_LOGIN")
    SS:Init()
end)
