--## Author: Xytrixz ## Version: 1.4.1 ## SavedVariables: ValeeraXPBarDB

local ADDON_NAME = "ValeeraXPBar"
local FACTION_ID = 2744

ValeeraXPBarDB = ValeeraXPBarDB or {}

local defaults = {
    point = "TOP",
    relativePoint = "TOP",
    x = 0,
    y = -30,
    locked = false,
    showBar = true,
    onlyShowInDelves = true,
    width = 310,
    height = 21,
    initialized = false,
}

for k, v in pairs(defaults) do
    if ValeeraXPBarDB[k] == nil then
        ValeeraXPBarDB[k] = v
    end
end

local f = CreateFrame("Frame", "ValeeraXPBarFrame", UIParent)
f:SetSize(ValeeraXPBarDB.width, ValeeraXPBarDB.height)
f:SetClampedToScreen(true)
f:SetFrameStrata("HIGH")
f:SetFrameLevel(20)
f:SetMovable(true)
f:EnableMouse(not ValeeraXPBarDB.locked)
f:RegisterForDrag("LeftButton")

local bg = f:CreateTexture(nil, "BACKGROUND")
bg:SetAllPoints()
bg:SetColorTexture(.015, .015, .02, .92)

local bar = CreateFrame("StatusBar", nil, f)
bar:SetPoint("TOPLEFT", 3, -3)
bar:SetPoint("BOTTOMRIGHT", -3, 3)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(.20, .65, 1, 1)
bar:SetMinMaxValues(0, 1)
bar:SetValue(0)

local bbg = bar:CreateTexture(nil, "BACKGROUND")
bbg:SetAllPoints()
bbg:SetColorTexture(.06, .06, .08, 1)

local text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
text:SetPoint("CENTER")
text:SetShadowOffset(1, -1)

local function FormatNumber(n)
    n = math.floor(tonumber(n) or 0)
    local s = tostring(n)
    while true do
        local x, count = s:gsub("^(-?%d+)(%d%d%d)", "%1,%2")
        s = x
        if count == 0 then break end
    end
    return s
end

local function ParseLevel(reaction)
    if type(reaction) == "number" then return reaction end
    if type(reaction) == "string" then return tonumber(reaction:match("(%d+)")) end
end

local function GetValeeraData()
    if not C_GossipInfo or not C_GossipInfo.GetFriendshipReputation then return nil end
    local d = C_GossipInfo.GetFriendshipReputation(FACTION_ID)
    if not d then return nil end
    return {
        level = ParseLevel(d.reaction),
        raw = d.reaction,
        standing = tonumber(d.standing),
        maxRep = tonumber(d.maxRep),
        threshold = tonumber(d.reactionThreshold),
        nextThreshold = tonumber(d.nextThreshold),
    }
end

local function UpdateBar()
    local d = GetValeeraData()
    if not d or not d.standing or not d.threshold then
        bar:SetMinMaxValues(0, 1)
        bar:SetValue(0)
        text:SetText("Valeera XP unavailable")
        return
    end

    local earned = math.max(0, d.standing - d.threshold)
    local required = d.nextThreshold and math.max(0, d.nextThreshold - d.threshold) or 0

    if not d.level then
        text:SetText("瓦妹 • " .. (d.raw or "？"))
    elseif required <= 0 then
        bar:SetMinMaxValues(0, 1)
        bar:SetValue(1)
        text:SetText(("瓦妹 • %d 级• MAX"):format(d.level))
        return
    else
        bar:SetMinMaxValues(0, required)
        bar:SetValue(math.min(earned, required))
        text:SetText(("瓦妹 • %d 级 • %s / %s • %d%%"):format(
            d.level, FormatNumber(earned), FormatNumber(required),
            math.floor(earned / required * 100 + .5)
        ))
        return
    end

    bar:SetMinMaxValues(0, math.max(1, required))
    bar:SetValue(math.min(earned, math.max(1, required)))
end

local function IsInActiveDelve()
    return C_DelvesUI and C_DelvesUI.HasActiveDelve and C_DelvesUI.HasActiveDelve() == true
end

local function UpdateVisibility()
    if not ValeeraXPBarDB.showBar or (ValeeraXPBarDB.onlyShowInDelves and not IsInActiveDelve()) then
        f:Hide()
    else
        f:Show()
    end
end

local function ApplyPosition()
    f:ClearAllPoints()
    f:SetPoint(
        ValeeraXPBarDB.point, UIParent, ValeeraXPBarDB.relativePoint,
        ValeeraXPBarDB.x, ValeeraXPBarDB.y
    )
    f:SetSize(ValeeraXPBarDB.width, ValeeraXPBarDB.height)
end

local function SavePosition()
    local p, _, rp, x, y = f:GetPoint(1)
    ValeeraXPBarDB.point = p
    ValeeraXPBarDB.relativePoint = rp
    ValeeraXPBarDB.x = x
    ValeeraXPBarDB.y = y
end

local function SetLocked(value)
    ValeeraXPBarDB.locked = value
    f:EnableMouse(not value)
end

local function ResetPosition()
    ValeeraXPBarDB.point = "TOP"
    ValeeraXPBarDB.relativePoint = "TOP"
    ValeeraXPBarDB.x = 0
    ValeeraXPBarDB.y = -30
    ApplyPosition()
end

f:SetScript("OnDragStart", function(self)
    if not ValeeraXPBarDB.locked then self:StartMoving() end
end)

f:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    SavePosition()
end)

-- Blizzard's modern Settings API puts this in the game's AddOns settings.
local settingsCategory

local function RegisterSettings()
    if not Settings or not Settings.RegisterVerticalLayoutCategory then return end
    local category, layout = Settings.RegisterVerticalLayoutCategory("Valeera XP Bar")
    settingsCategory = category
    Settings.RegisterAddOnCategory(category)

    local function checkbox(key, variable, name, tip, callback)
        local setting = Settings.RegisterAddOnSetting(category, variable, key, ValeeraXPBarDB, type(ValeeraXPBarDB[key]), name, ValeeraXPBarDB[key])
        setting:SetValueChangedCallback(callback)
        Settings.CreateCheckbox(category, setting, tip)
    end

    checkbox("showBar","ValeeraXPBar_ShowBar","Show Bar","Show the Valeera XP bar.",function(_,v)
        ValeeraXPBarDB.showBar=v; UpdateVisibility()
    end)
    checkbox("onlyShowInDelves","ValeeraXPBar_OnlyShowInDelves","Only Show in Delves","Automatically hide the bar outside an active Delve.",function(_,v)
        ValeeraXPBarDB.onlyShowInDelves=v; UpdateVisibility()
    end)

    do
        local setting=Settings.RegisterAddOnSetting(category,"ValeeraXPBar_Width","width",ValeeraXPBarDB,type(defaults.width),"Bar Width",defaults.width)
        setting:SetValueChangedCallback(function(_,v) v=math.max(100,math.min(1000,tonumber(v) or defaults.width)); ValeeraXPBarDB.width=v; f:SetWidth(v) end)
        local o=Settings.CreateSliderOptions(100,1000,10); o:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
        Settings.CreateSlider(category,setting,o,"Controls the width of the Valeera XP bar.")
    end
    do
        local setting=Settings.RegisterAddOnSetting(category,"ValeeraXPBar_Height","height",ValeeraXPBarDB,type(defaults.height),"Bar Height",defaults.height)
        setting:SetValueChangedCallback(function(_,v) v=math.max(12,math.min(200,tonumber(v) or defaults.height)); ValeeraXPBarDB.height=v; f:SetHeight(v) end)
        local o=Settings.CreateSliderOptions(12,200,2); o:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right)
        Settings.CreateSlider(category,setting,o,"Controls the height of the Valeera XP bar.")
    end
    do
        local setting=Settings.RegisterAddOnSetting(category,"ValeeraXPBar_Locked","locked",ValeeraXPBarDB,type(defaults.locked),"Lock Bar Position",defaults.locked)
        setting:SetValueChangedCallback(function(_,v) SetLocked(v) end)
        Settings.CreateCheckbox(category,setting,"Prevent the bar from being dragged.")
    end
    layout:AddInitializer(CreateSettingsButtonInitializer("Position","Reset Bar Position",function() ResetPosition() end,"Moves the Valeera XP bar back to its default position.",true))

    -- Apply saved settings immediately after the controls exist. This prevents
    -- the initial UI state from depending on the user toggling a checkbox.
    SetLocked(ValeeraXPBarDB.locked)
    ApplyPosition()
    UpdateVisibility()
end

SLASH_VALEERAXPBAR1 = "/vxp"
SLASH_VALEERAXPBAR2 = "/valeera"

SlashCmdList.VALEERAXPBAR=function(msg)
    msg=(msg or ""):lower():trim()
    if msg=="" then
        if settingsCategory then Settings.OpenToCategory(settingsCategory:GetID()) end
    elseif msg=="reset" then ResetPosition()
    elseif msg=="refresh" then UpdateBar(); UpdateVisibility()
    elseif msg=="debug" then
        local d=GetValeeraData()
        if not d then print("|cff66ccffValeera XP Bar DEBUG:|r unavailable") else
            print("|cff66ccffValeera XP Bar DEBUG:|r Level:",d.level or "nil","Standing:",d.standing,"Threshold:",d.threshold,"Next:",d.nextThreshold)
            print("  Show bar:",ValeeraXPBarDB.showBar and "yes" or "no","Only in Delves:",ValeeraXPBarDB.onlyShowInDelves and "yes" or "no","Active Delve:",IsInActiveDelve() and "yes" or "no")
        end
    else print("|cff66ccffValeera XP Bar:|r /vxp, /vxp reset, /vxp refresh, /vxp debug") end
end

f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("ZONE_CHANGED_NEW_AREA")
f:RegisterEvent("ZONE_CHANGED")
f:RegisterEvent("ZONE_CHANGED_INDOORS")
f:RegisterEvent("CHAT_MSG_COMBAT_FACTION_CHANGE")
f:RegisterEvent("UPDATE_FACTION")
f:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
f:RegisterEvent("CRITERIA_UPDATE")

f:SetScript("OnEvent",function(self,event,...)
    if event=="PLAYER_LOGIN" then
        if not ValeeraXPBarDB.initialized then
            ValeeraXPBarDB.point="CENTER"; ValeeraXPBarDB.relativePoint="CENTER"; ValeeraXPBarDB.x=0; ValeeraXPBarDB.y=-180; ValeeraXPBarDB.initialized=true
        end
        ApplyPosition(); SetLocked(ValeeraXPBarDB.locked); UpdateBar(); UpdateVisibility()
        C_Timer.After(0, function()
            SetLocked(ValeeraXPBarDB.locked)
            ApplyPosition()
            UpdateVisibility()
        end)
    elseif event=="CHAT_MSG_COMBAT_FACTION_CHANGE" then
        local message=...
        if type(message)=="string" and message:find("Valeera Sanguinar",1,true) then
            UpdateBar(); C_Timer.After(.1,UpdateBar); C_Timer.After(.3,UpdateBar); C_Timer.After(1,UpdateBar)
        end
    else
        C_Timer.After(.05,UpdateBar); C_Timer.After(.05,UpdateVisibility)
    end
end)

-- Register settings after the addon environment is initialized.
--RegisterSettings()
