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 UnitStat = UnitStat
local wipe = wipe

local PADDING = 12
local MIN_WIDTH = 160

local activeStats = {}
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 = {
    { stat = LE_UNIT_STAT_STRENGTH, label = "Strength" },
    { stat = LE_UNIT_STAT_AGILITY, label = "Agility" },
    { stat = LE_UNIT_STAT_INTELLECT, label = "Intellect" },
}

local function FormatPercent(value)
    return string.format("%.2f%%", value or 0)
end

local function FormatRating(value)
    return string.format("%d", value or 0)
end

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

    if GetCombatRatingBonus and CR_SPEED then
        return GetCombatRatingBonus(CR_SPEED)
    end

    return 0
end

local function GetMainStat()
    local bestLabel = "Main Stat"
    local bestValue = 0

    for _, info in ipairs(PRIMARY_STATS) do
        local value = select(2, UnitStat("player", info.stat)) or 0
        if value > bestValue then
            bestValue = value
            bestLabel = info.label
        end
    end

    return bestLabel, bestValue
end

local function GetVersatility()
    local total = 0

    if GetCombatRatingBonus and CR_VERSATILITY_DAMAGE_DONE then
        total = total + (GetCombatRatingBonus(CR_VERSATILITY_DAMAGE_DONE) or 0)
    end

    if GetVersatilityBonus and CR_VERSATILITY_DAMAGE_DONE then
        total = total + (GetVersatilityBonus(CR_VERSATILITY_DAMAGE_DONE) or 0)
    end

    return total
end

local function GetVersatilityDefenseBonus(offensePercent)
    local total = 0

    if GetCombatRatingBonus and CR_VERSATILITY_DAMAGE_TAKEN then
        total = total + (GetCombatRatingBonus(CR_VERSATILITY_DAMAGE_TAKEN) or 0)
    end

    if GetVersatilityBonus and CR_VERSATILITY_DAMAGE_TAKEN then
        total = total + (GetVersatilityBonus(CR_VERSATILITY_DAMAGE_TAKEN) or 0)
    end

    if total > 0 and total < (offensePercent or 0) then
        return total
    end

    return (offensePercent or 0) / 2
end

local STAT_DEFS = {
    {
        key = "mainstat",
        label = "Main Stat",
        getText = function()
            local statLabel, statValue = GetMainStat()
            return statLabel .. ": " .. FormatRating(statValue)
        end,
    },
    {
        key = "crit",
        label = "Critical Strike",
        getPercent = function() return GetCritChance() or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_CRIT_MELEE)) or 0 end,
    },
    {
        key = "haste",
        label = "Haste",
        getPercent = function() return GetHaste() or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_HASTE_MELEE)) or 0 end,
    },
    {
        key = "mastery",
        label = "Mastery",
        getPercent = function() return GetMasteryEffect() or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_MASTERY)) or 0 end,
    },
    {
        key = "versatility",
        label = "Versatility",
        getPercent = function() return GetVersatility() or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_VERSATILITY_DAMAGE_DONE)) or 0 end,
        getText = function(self)
            local offensePercent = self.getPercent()
            local defensePercent = GetVersatilityDefenseBonus(offensePercent)
            local rating = self.getRating()
            return self.label .. ": " .. FormatPercent(offensePercent) .. " / " .. FormatPercent(defensePercent) .. " (" .. FormatRating(rating) .. ")"
        end,
    },
    {
        key = "leech",
        label = "Leech",
        getPercent = function() return (GetLifesteal and GetLifesteal()) or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_LIFESTEAL)) or 0 end,
    },
    {
        key = "avoidance",
        label = "Avoidance",
        getPercent = function() return (GetAvoidance and GetAvoidance()) or 0 end,
        getRating = function() return (GetCombatRating and GetCombatRating(CR_AVOIDANCE)) or 0 end,
    },
    {
        key = "speed",
        label = "Speed",
        getPercent = function() return GetMovementSpeedStat() or 0 end,
        getRating = function() return (GetCombatRating and CR_SPEED and GetCombatRating(CR_SPEED)) or 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 maxWidth = 0
    for i = 1, #activeStats do
        local width = frame.lines[i] and frame.lines[i]:GetStringWidth() or 0
        if width > maxWidth then
            maxWidth = width
        end
    end

    if db.showItemLevel and frame.title:GetStringWidth() > maxWidth then
        maxWidth = frame.title:GetStringWidth()
    end

    frame:SetWidth(math.max(MIN_WIDTH, math.ceil(maxWidth + (PADDING * 2) + 12)))
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("TOP", 0, -PADDING)

    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: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 averageItemLevel, equippedItemLevel = GetAverageItemLevel()
        frame.title:SetText(string.format("Item Level: %.2f", equippedItemLevel or averageItemLevel or 0))
    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.getPercent()) .. " (" .. FormatRating(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
