local _, addon = ...
local HLY = addon.HLY

local CreateFrame = CreateFrame
local GetAvoidance = GetAvoidance
local GetAverageItemLevel = GetAverageItemLevel
local GetCombatRating = GetCombatRating
local GetCombatRatingBonus = GetCombatRatingBonus
local GetCritChance = GetCritChance
local GetHaste = GetHaste
local GetLifesteal = GetLifesteal
local GetMasteryEffect = GetMasteryEffect
local GetSpeed = GetSpeed
local GetVersatilityBonus = GetVersatilityBonus
local UIParent = UIParent
local UnitClass = UnitClass
local GetSpecialization = GetSpecialization
local GetSpecializationInfo = GetSpecializationInfo
local GetSpecializationInfoByID = GetSpecializationInfoByID
local UnitStat = UnitStat
local pcall = pcall
local tonumber = tonumber
local wipe = wipe
local issecretvalue = issecretvalue

local PADDING = 12
local MIN_WIDTH = 225
local MAX_WIDTH = 330

local activeStats = {}
local displayCache = {}
local statsEventFrame = CreateFrame("Frame")

local STAT_COLORS = {
    mainstat = { r = 1, g = 0.82, b = 0 },
    crit = { r = 1, g = 0, b = 0 },
    haste = { r = 0, g = 1, b = 0 },
    mastery = { r = 0, g = 0.5, b = 1 },
    versatility = { r = 1, g = 1, b = 0 },
    leech = { r = 0, g = 0.85, b = 0.55 },
    avoidance = { r = 0.8, g = 0.8, b = 0.8 },
    speed = { r = 1, g = 0.55, b = 0.15 },
}

local PRIMARY_STATS = {
    { key = "strength", stat = LE_UNIT_STAT_STRENGTH, label = "Strength" },
    { key = "agility", stat = LE_UNIT_STAT_AGILITY, label = "Agility" },
    { key = "intellect", stat = LE_UNIT_STAT_INTELLECT, label = "Intellect" },
}

local function SafeCall(func, ...)
    if not func then
        return nil
    end

    local ok, result = pcall(func, ...)
    if ok then
        return result
    end

    return nil
end

local function SafeFormat(cacheKey, formatString, value)
    if type(value) == "number" then
        local ok, text = pcall(string.format, formatString, value)
        if ok then
            if not issecretvalue or not issecretvalue(text) then
                displayCache[cacheKey] = text
            end
            return text
        end
    end

    local cached = displayCache[cacheKey]
    if cached ~= nil then
        return cached
    end

    return "-"
end

local function SafeNumber(value, fallback)
    local ok, numberValue = pcall(tonumber, value)
    if ok and numberValue then
        local usable = pcall(function()
            return numberValue >= 0 or numberValue < 0
        end)
        if usable then
            return numberValue
        end
    end

    return fallback or 0
end

local function FormatPercent(cacheKey, value)
    return SafeFormat(cacheKey, "%.2f%%", value)
end

local function FormatRating(cacheKey, value)
    return SafeFormat(cacheKey, "%d", value)
end

local function FormatDecimal(cacheKey, value)
    return SafeFormat(cacheKey, "%.2f", value)
end

local function GetMovementSpeedStat()
    if GetSpeed then
        return SafeCall(GetSpeed)
    end

    if GetCombatRatingBonus and CR_SPEED then
        return SafeCall(GetCombatRatingBonus, CR_SPEED)
    end

    return 0
end

local function GetPrimaryStatKey()
    local specIndex = GetSpecialization and GetSpecialization()
    local specID = specIndex and SafeCall(GetSpecializationInfo, specIndex)
    if specID and GetSpecializationInfoByID then
        local ok, _, _, _, _, _, primaryStat = pcall(GetSpecializationInfoByID, specID)
        if ok then
            -- GetSpecializationInfoByID returns 1=Str, 2=Agi, 3=Int (not LE_UNIT_STAT_* values)
            if primaryStat == LE_UNIT_STAT_STRENGTH or primaryStat == 1 or primaryStat == "STRENGTH" then
                return "strength"
            elseif primaryStat == LE_UNIT_STAT_AGILITY or primaryStat == 2 or primaryStat == "AGILITY" then
                return "agility"
            elseif primaryStat == LE_UNIT_STAT_INTELLECT or primaryStat == 3 or primaryStat == 4 or primaryStat == "INTELLECT" then
                return "intellect"
            end
        end
    end

    -- Spec detection failed: compare actual stat values to determine primary.
    -- UnitStat can return secret numbers under taint; wrap the comparison in pcall.
    local ok, result = pcall(function()
        local str = SafeCall(UnitStat, "player", LE_UNIT_STAT_STRENGTH) or 0
        local agi = SafeCall(UnitStat, "player", LE_UNIT_STAT_AGILITY) or 0
        local int = SafeCall(UnitStat, "player", LE_UNIT_STAT_INTELLECT) or 0
        if int > str and int > agi then return "intellect"
        elseif agi > str then return "agility"
        else return "strength" end
    end)
    if ok and result then return result end

    return "strength"
end

local function GetMainStat()
    local primaryStatKey = GetPrimaryStatKey()

    for _, info in ipairs(PRIMARY_STATS) do
        if info.key == primaryStatKey then
            local ok, _, effectiveStat = pcall(UnitStat, "player", info.stat)
            if ok then
                return info.label, effectiveStat
            end

            return info.label, nil
        end
    end

    return "Main Stat", nil
end

local function GetVersatility()
    if GetCombatRatingBonus and CR_VERSATILITY_DAMAGE_DONE then
        return SafeCall(GetCombatRatingBonus, CR_VERSATILITY_DAMAGE_DONE)
    end

    if GetVersatilityBonus and CR_VERSATILITY_DAMAGE_DONE then
        return SafeCall(GetVersatilityBonus, CR_VERSATILITY_DAMAGE_DONE)
    end

    return 0
end

local function GetVersatilityDefenseBonus(offensePercent)
    if GetCombatRatingBonus and CR_VERSATILITY_DAMAGE_TAKEN then
        return SafeCall(GetCombatRatingBonus, CR_VERSATILITY_DAMAGE_TAKEN)
    end

    if GetVersatilityBonus and CR_VERSATILITY_DAMAGE_TAKEN then
        return SafeCall(GetVersatilityBonus, CR_VERSATILITY_DAMAGE_TAKEN)
    end

    local offense = SafeNumber(offensePercent)
    return offense / 2
end

local STAT_DEFS = {
    {
        key = "mainstat",
        label = "Main Stat",
        getText = function()
            local statLabel, statValue = GetMainStat()
            return statLabel .. ": " .. FormatRating("mainstat", statValue)
        end,
    },
    {
        key = "crit",
        label = "Critical Strike",
        getPercent = function() return SafeCall(GetCritChance) end,
        getRating = function() return SafeCall(GetCombatRating, CR_CRIT_MELEE) end,
    },
    {
        key = "haste",
        label = "Haste",
        getPercent = function() return SafeCall(GetHaste) end,
        getRating = function() return SafeCall(GetCombatRating, CR_HASTE_MELEE) end,
    },
    {
        key = "mastery",
        label = "Mastery",
        getPercent = function() return SafeCall(GetMasteryEffect) end,
        getRating = function() return SafeCall(GetCombatRating, CR_MASTERY) end,
    },
    {
        key = "versatility",
        label = "Versatility",
        getPercent = function() return GetVersatility() end,
        getRating = function() return SafeCall(GetCombatRating, CR_VERSATILITY_DAMAGE_DONE) end,
        getText = function(self)
            local offensePercent = self.getPercent()
            local defensePercent = GetVersatilityDefenseBonus(offensePercent)
            local rating = self.getRating()
            return self.label .. ": " .. FormatPercent("versatilityOffense", offensePercent) .. " / " .. FormatPercent("versatilityDefense", defensePercent) .. " (" .. FormatRating("versatilityRating", rating) .. ")"
        end,
    },
    {
        key = "leech",
        label = "Leech",
        getPercent = function() return SafeCall(GetLifesteal) end,
        getRating = function() return SafeCall(GetCombatRating, CR_LIFESTEAL) end,
    },
    {
        key = "avoidance",
        label = "Avoidance",
        getPercent = function() return SafeCall(GetAvoidance) end,
        getRating = function() return SafeCall(GetCombatRating, CR_AVOIDANCE) end,
    },
    {
        key = "speed",
        label = "Speed",
        getPercent = function() return GetMovementSpeedStat() end,
        getRating = function()
            if CR_SPEED then
                return SafeCall(GetCombatRating, CR_SPEED)
            end

            return 0
        end,
    },
}

function HLY:GetHLYstatsDefinitions()
    return STAT_DEFS
end

function HLY:EnsureHLYstatsDefaults()
    if self.db and not self.db.hlyStats and addon.P and addon.P.hlyqol and addon.P.hlyqol.hlyStats then
        self.db.hlyStats = self:CopyTable(addon.P.hlyqol.hlyStats)
    end

    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    db.point = db.point or "CENTER"
    db.relativePoint = db.relativePoint or "CENTER"
    db.x = db.x or 0
    db.y = db.y or 0
    db.font = db.font or "Homespun"
    db.fontSize = db.fontSize or 12
    if type(db.showBackground) ~= "boolean" then
        db.showBackground = true
    end
    if type(db.backgroundAlpha) ~= "number" then
        db.backgroundAlpha = 0.6
    end
    if type(db.showItemLevel) ~= "boolean" then
        db.showItemLevel = db.showTitle ~= false
    end
    db.showTitle = nil
    db.statsEnabled = db.statsEnabled or {}

    for _, def in ipairs(STAT_DEFS) do
        if type(db.statsEnabled[def.key]) ~= "boolean" then
            db.statsEnabled[def.key] = true
        end
    end
end

function HLY:GetHLYstatsFrame()
    if self.hlyStatsFrame then
        return self.hlyStatsFrame
    end

    local frame = CreateFrame("Frame", "HLYqolHLYstatsFrame", UIParent, "BackdropTemplate")
    frame:SetSize(230, 180)
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame.lines = {}

    frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        tile = false,
        edgeSize = 1,
        insets = { left = 1, right = 1, top = 1, bottom = 1 },
    })
    frame:SetBackdropBorderColor(0, 0, 0, 1)

    frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    frame.title:SetPoint("TOP", 0, -10)
    frame.title:SetText("Item Level: 0.00")

    frame:SetScript("OnDragStart", function(this)
        local db = self.db and self.db.hlyStats
        if db and not db.locked then
            this:StartMoving()
        end
    end)

    frame:SetScript("OnDragStop", function(this)
        this:StopMovingOrSizing()
        local db = self.db and self.db.hlyStats
        if db then
            local point, _, relativePoint, x, y = this:GetPoint()
            db.point = point
            db.relativePoint = relativePoint
            db.x = x
            db.y = y
        end
    end)

    self.hlyStatsFrame = frame
    return frame
end

function HLY:CreateHLYstatsLine(frame, index)
    local line = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    line:SetJustifyH("LEFT")
    line:SetText("")
    frame.lines[index] = line
    return line
end

function HLY:BuildHLYstatsActiveStats()
    wipe(activeStats)
    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    for _, def in ipairs(STAT_DEFS) do
        if db.statsEnabled[def.key] then
            activeStats[#activeStats + 1] = def
        end
    end
end

function HLY:ApplyHLYstatsLockState()
    local db = self.db and self.db.hlyStats
    local frame = self.hlyStatsFrame
    if not db or not frame then
        return
    end

    frame:EnableMouse(not db.locked)
    self:ApplyHLYstatsBackdrop()
end

function HLY:ApplyHLYstatsBackdrop()
    local db = self.db and self.db.hlyStats
    local frame = self.hlyStatsFrame
    if not db or not frame then
        return
    end

    local alpha = db.showBackground and (db.backgroundAlpha or 0.6) or 0
    frame:SetBackdropColor(0, 0, 0, alpha)
    frame:SetBackdropBorderColor(0, 0, 0, db.showBackground and 1 or 0)
end

function HLY:ApplyHLYstatsFont()
    local db = self.db and self.db.hlyStats
    local frame = self.hlyStatsFrame
    if not db or not frame then
        return
    end

    self:ApplyFont(frame.title, db.font, (db.fontSize or 12) + 2, "SHADOWOUTLINE")
    for i = 1, #frame.lines do
        self:ApplyFont(frame.lines[i], db.font, db.fontSize or 12, "SHADOWOUTLINE")
    end
end

function HLY:UpdateHLYstatsWidth()
    local db = self.db and self.db.hlyStats
    local frame = self.hlyStatsFrame
    if not db or not frame then
        return
    end

    local fontSize = SafeNumber(db.fontSize, 12)
    local width = MIN_WIDTH + math.max(0, fontSize - 12) * 7
    if width > MAX_WIDTH then
        width = MAX_WIDTH
    end

    frame:SetWidth(width)

    local contentWidth = width - (PADDING * 2)
    if frame.title then
        frame.title:SetWidth(contentWidth)
    end

    for i = 1, #frame.lines do
        frame.lines[i]:SetWidth(contentWidth)
    end
end

function HLY:RefreshHLYstatsLines()
    self:EnsureHLYstatsDefaults()

    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    local frame = self:GetHLYstatsFrame()
    self:BuildHLYstatsActiveStats()

    if db.showItemLevel then
        frame.title:Show()
    else
        frame.title:Hide()
    end

    local fontSize = db.fontSize or 12
    local lineHeight = math.max(14, fontSize + 6)
    local titleGap = math.max(4, math.floor(fontSize * 0.4))
    local titleHeight = fontSize + 8
    local startY = -PADDING

    frame.title:ClearAllPoints()
    frame.title:SetPoint("TOPLEFT", PADDING, -PADDING)
    frame.title:SetPoint("TOPRIGHT", -PADDING, -PADDING)
    frame.title:SetJustifyH("CENTER")
    if frame.title.SetNonSpaceWrap then
        frame.title:SetNonSpaceWrap(false)
    end

    if db.showItemLevel then
        startY = startY - titleHeight - titleGap
    end

    for i, def in ipairs(activeStats) do
        local line = frame.lines[i] or self:CreateHLYstatsLine(frame, i)
        local color = STAT_COLORS[def.key] or { r = 1, g = 1, b = 1 }
        line:ClearAllPoints()
        line:SetPoint("TOPLEFT", PADDING, startY - ((i - 1) * lineHeight))
        line:SetPoint("TOPRIGHT", -PADDING, startY - ((i - 1) * lineHeight))
        line:SetJustifyH("LEFT")
        line:SetWordWrap(false)
        if line.SetNonSpaceWrap then
            line:SetNonSpaceWrap(false)
        end
        line:SetTextColor(color.r, color.g, color.b)
        line:Show()
    end

    for i = #activeStats + 1, #frame.lines do
        frame.lines[i]:Hide()
    end

    self:ApplyHLYstatsFont()

    local titleBlock = db.showItemLevel and (titleHeight + titleGap) or 0
    frame:SetHeight((PADDING * 2) + titleBlock + (#activeStats * lineHeight))
    self:UpdateHLYstats()
end

function HLY:UpdateHLYstats()
    local db = self.db and self.db.hlyStats
    local frame = self.hlyStatsFrame
    if not db or not frame then
        return
    end

    if not db.enabled then
        frame:Hide()
        return
    end

    if db.showItemLevel then
        local ok, averageItemLevel, equippedItemLevel = pcall(GetAverageItemLevel)
        local itemLevel
        if ok then
            if type(equippedItemLevel) == "number" then
                itemLevel = equippedItemLevel
            else
                itemLevel = averageItemLevel
            end
        end
        frame.title:SetText("Item Level: " .. FormatDecimal("itemLevel", itemLevel))
    end

    for i, def in ipairs(activeStats) do
        local line = frame.lines[i]
        if line then
            if def.getText then
                line:SetText(def:getText())
            else
                line:SetText(def.label .. ": " .. FormatPercent(def.key .. "Percent", def.getPercent()) .. " (" .. FormatRating(def.key .. "Rating", def.getRating()) .. ")")
            end
        end
    end

    self:UpdateHLYstatsWidth()
    frame:Show()
end

function HLY:RefreshHLYstats()
    self:EnsureHLYstatsDefaults()

    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    local frame = self:GetHLYstatsFrame()
    frame:ClearAllPoints()
    frame:SetPoint(db.point or "CENTER", UIParent, db.relativePoint or "CENTER", db.x or 0, db.y or 0)
    self:ApplyHLYstatsLockState()
    self:ApplyHLYstatsBackdrop()
    self:RefreshHLYstatsLines()
end

function HLY:ResetHLYstatsPosition()
    self:EnsureHLYstatsDefaults()

    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    db.point = "CENTER"
    db.relativePoint = "CENTER"
    db.x = 0
    db.y = 0
    self:RefreshHLYstats()
end

function HLY:HandleHLYstatsEvent(event, unit)
    if (event == "UNIT_STATS" or event == "UNIT_AURA") and unit and unit ~= "player" then
        return
    end

    self:UpdateHLYstats()
end

function HLY:HandleHLYstatsCommand(msg)
    msg = (msg or ""):lower()
    self:EnsureHLYstatsDefaults()

    local db = self.db and self.db.hlyStats
    if not db then
        return
    end

    if msg == "lock" then
        db.locked = true
        self:ApplyHLYstatsLockState()
        print("HLYstats: Frame locked.")
    elseif msg == "unlock" then
        db.locked = false
        self:ApplyHLYstatsLockState()
        print("HLYstats: Frame unlocked.")
    elseif msg == "reset" then
        self:ResetHLYstatsPosition()
        print("HLYstats: Position reset.")
    else
        print("HLYstats commands: /hlystats lock | unlock | reset")
    end
end

function HLY:SetupHLYstats()
    if self.hlyStatsSetup then
        return
    end

    self:EnsureHLYstatsDefaults()
    statsEventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    statsEventFrame:RegisterEvent("UNIT_STATS")
    statsEventFrame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
    statsEventFrame:RegisterEvent("UNIT_AURA")
    statsEventFrame:RegisterEvent("COMBAT_RATING_UPDATE")
    statsEventFrame:RegisterEvent("MASTERY_UPDATE")
    statsEventFrame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
    statsEventFrame:SetScript("OnEvent", function(_, event, ...)
        self:HandleHLYstatsEvent(event, ...)
    end)

    self:RegisterChatCommand("hlystats", "HandleHLYstatsCommand")
    self:RefreshHLYstats()

    self.hlyStatsSetup = true
end

-- LibHLYUI options pane (v2) ------------------------------------------------
function HLY:BuildHLYstatsOptions(tab)
    local function db() self:EnsureHLYstatsDefaults() return self.db.hlyStats end

    tab:Note("Zeigt deine aktuellen Kampfwerte in einem verschiebbaren Fenster.")
    tab:Row({
        function(c) c:Checkbox({ label = "Enable", get = function() return db().enabled end,
            set = function(v) db().enabled = v self:RefreshHLYstats() end }) end,
        function(c) c:Checkbox({ label = "Lock Frame", get = function() return db().locked end,
            set = function(v) db().locked = v self:ApplyHLYstatsLockState() end }) end,
    })
    tab:Button({ label = "Reset Position", func = function() self:ResetHLYstatsPosition() end })

    tab:Section("Text")
    tab:Checkbox({ label = "Show Item Level", get = function() return db().showItemLevel end,
        set = function(v) db().showItemLevel = v self:RefreshHLYstatsLines() end })
    tab:Dropdown({ label = "Font", fontPreview = true, values = function() return self:GetFontList() end,
        get = function() return db().font end, set = function(v) db().font = v self:RefreshHLYstatsLines() end })
    tab:Slider({ label = "Font Size", min = 8, max = 24, step = 1,
        get = function() return db().fontSize end, set = function(v) db().fontSize = v self:RefreshHLYstatsLines() end })

    tab:Section("Background")
    tab:Checkbox({ label = "Show Background", get = function() return db().showBackground end,
        set = function(v) db().showBackground = v self:ApplyHLYstatsBackdrop() end })
    tab:Slider({ label = "Background Alpha", min = 0, max = 1, step = 0.05,
        format = function(v) return string.format("%d%%", math.floor((v or 0) * 100 + 0.5)) end,
        get = function() return db().backgroundAlpha end,
        set = function(v) db().backgroundAlpha = v self:ApplyHLYstatsBackdrop() end })

    tab:Section("Stats")
    local defs = self:GetHLYstatsDefinitions()
    for i = 1, #defs, 2 do
        local a, b = defs[i], defs[i + 1]
        local builders = {
            function(c) c:Checkbox({ label = a.label, get = function() return db().statsEnabled[a.key] end,
                set = function(v) db().statsEnabled[a.key] = v self:RefreshHLYstatsLines() end }) end,
        }
        if b then
            builders[2] = function(c) c:Checkbox({ label = b.label, get = function() return db().statsEnabled[b.key] end,
                set = function(v) db().statsEnabled[b.key] = v self:RefreshHLYstatsLines() end }) end
        end
        tab:Row(builders, 2)
    end
end

addon.options = addon.options or {}
addon.options[#addon.options + 1] = {
    parent = "UI", parentOrder = 100, group = "HLYstats", groupOrder = 3,
    name = "HLYstats", order = 1, build = "BuildHLYstatsOptions",
}
