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

local _G = _G
local C_Timer = C_Timer
local abs = abs
local format = format
local ipairs = ipairs
local max = max
local tinsert = tinsert

local GetInspectSpecialization = GetInspectSpecialization
local GetInventoryItemLink = GetInventoryItemLink
local GetItemStats = (C_Item and C_Item.GetItemStats) or _G.GetItemStats
local GetSpecialization = GetSpecialization
local GetSpecializationInfo = GetSpecializationInfo
local GetSpecializationInfoByID = GetSpecializationInfoByID
local UnitClass = UnitClass
local UnitHealthMax = UnitHealthMax
local UnitLevel = UnitLevel
local UnitName = UnitName
local UnitStat = UnitStat
local UnitGUID = UnitGUID

local INVSLOT_FIRST = INVSLOT_FIRST_EQUIPPED or 1
local INVSLOT_LAST = INVSLOT_LAST_EQUIPPED or 17
local STAT_ROW_HEIGHT = 18
local STAT_PADDING = 8
local STAT_ROW_SEPARATOR_SPACING = 6
local INSPECT_RETRY_DELAY = 0.2
local INSPECT_RETRY_MAX_ATTEMPTS = 3
local INSPECT_WATCH_INTERVAL = 0.2

local STATS_CONFIG = {
    groups = {
        {
            rows = {
                { key = "header", isHeader = true },
            },
        },
        {
            color = { r = 0.36, g = 0.62, b = 1 },
            rows = {
                { key = "level", name = "Level" },
                { key = "health", name = "Health" },
                { key = "itemLevel", name = "iLvl", diffType = "value" },
            },
        },
        {
            color = { r = 1, g = 0.74, b = 0.2 },
            rows = {
                { key = "stamina", name = "Stam", diffType = "percent" },
                { key = "strength", name = "Str", diffType = "percent" },
                { key = "agility", name = "Agi", diffType = "percent" },
                { key = "intellect", name = "Int", diffType = "percent" },
            },
        },
        {
            color = { r = 0.3, g = 0.8, b = 0.3 },
            rows = {
                { key = "crit", name = "Crit", diffType = "percent" },
                { key = "haste", name = "Haste", diffType = "percent" },
                { key = "mastery", name = "Mastery", diffType = "percent" },
                { key = "versatility", name = "Vers", diffType = "percent" },
                { key = "leech", name = "Leech", diffType = "percent" },
                { key = "speed", name = "Speed", diffType = "percent" },
                { key = "avoidance", name = "Avoid", diffType = "percent" },
            },
        },
    },
}

local function ColorText(text, color)
    if not color then
        return text
    end

    local r = color.r or color[1] or 1
    local g = color.g or color[2] or 1
    local b = color.b or color[3] or 1
    return format("|cff%02x%02x%02x%s|r", r * 255, g * 255, b * 255, text)
end

local function GetPrimaryStatKey(unit)
    local specID
    if unit == "player" then
        local specIndex = GetSpecialization and GetSpecialization()
        if specIndex and GetSpecializationInfo then
            specID = select(1, GetSpecializationInfo(specIndex))
        end
    else
        specID = GetInspectSpecialization and GetInspectSpecialization(unit) or nil
    end

    if specID and GetSpecializationInfoByID then
        local _, _, _, _, _, _, primaryStat = GetSpecializationInfoByID(specID)
        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 == 4 or primaryStat == "INTELLECT" then
            return "intellect"
        end
    end

    if UnitStat then
        local strength = select(2, UnitStat(unit, LE_UNIT_STAT_STRENGTH or 1)) or 0
        local agility = select(2, UnitStat(unit, LE_UNIT_STAT_AGILITY or 2)) or 0
        local intellect = select(2, UnitStat(unit, LE_UNIT_STAT_INTELLECT or 4)) or 0

        if strength > 0 or agility > 0 or intellect > 0 then
            if strength >= agility and strength >= intellect then
                return "strength"
            elseif agility >= strength and agility >= intellect then
                return "agility"
            else
                return "intellect"
            end
        end
    end

    local class = select(2, UnitClass(unit))
    if class == "WARRIOR" or class == "PALADIN" or class == "DEATHKNIGHT" then
        return "strength"
    elseif class == "ROGUE" or class == "HUNTER" or class == "DEMONHUNTER" then
        return "agility"
    elseif class == "MAGE" or class == "PRIEST" or class == "WARLOCK" then
        return "intellect"
    end

    return nil
end

local function CalculateStatsFromEquipment(unit)
    local stats = {
        stamina = 0,
        strength = 0,
        agility = 0,
        intellect = 0,
        crit = 0,
        haste = 0,
        mastery = 0,
        versatility = 0,
        leech = 0,
        speed = 0,
        avoidance = 0,
    }

    for slot = INVSLOT_FIRST, INVSLOT_LAST do
        local itemLink = GetInventoryItemLink(unit, slot)
        if itemLink then
            local itemStats = GetItemStats and GetItemStats(itemLink)
            if itemStats then
                stats.stamina = stats.stamina + (itemStats["ITEM_MOD_STAMINA_SHORT"] or 0)
                stats.strength = stats.strength + (itemStats["ITEM_MOD_STRENGTH_SHORT"] or 0)
                stats.agility = stats.agility + (itemStats["ITEM_MOD_AGILITY_SHORT"] or 0)
                stats.intellect = stats.intellect + (itemStats["ITEM_MOD_INTELLECT_SHORT"] or 0)
                stats.crit = stats.crit + (itemStats["ITEM_MOD_CRIT_RATING_SHORT"] or 0)
                stats.haste = stats.haste + (itemStats["ITEM_MOD_HASTE_RATING_SHORT"] or 0)
                stats.mastery = stats.mastery + (itemStats["ITEM_MOD_MASTERY_RATING_SHORT"] or 0)
                stats.versatility = stats.versatility + (itemStats["ITEM_MOD_VERSATILITY"] or 0)
                stats.leech = stats.leech + (itemStats["ITEM_MOD_CR_LIFESTEAL_SHORT"] or 0)
                stats.speed = stats.speed + (itemStats["ITEM_MOD_CR_SPEED_SHORT"] or 0)
                stats.avoidance = stats.avoidance + (itemStats["ITEM_MOD_CR_AVOIDANCE_SHORT"] or 0)
            end
        end
    end

    return stats
end

local function GetUnitStatsForCompare(unit, itemLevel)
    local result = CalculateStatsFromEquipment(unit)
    result.level = UnitLevel(unit) or 0
    result.health = UnitHealthMax(unit) or 0
    result.itemLevel = itemLevel or 0
    result.primaryStatKey = GetPrimaryStatKey(unit)
    return result
end

function HLY:ApplyCompareStatsTextStyle(text)
    local db = self.db.compareStats.text
    self:ApplyFont(text, db.font, db.fontSize, "SHADOWOUTLINE")
end

function HLY:CreateCompareStatsFrame()
    if self.compareStatsFrame then
        return self.compareStatsFrame
    end

    local parent = _G.InspectFrame
    if not parent then
        return nil
    end

    local frame = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    frame:SetFrameLevel(parent:GetFrameLevel() + 3)
    frame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 3, 0)
    frame:Hide()
    self:ApplyBackdrop(frame)

    frame.rows = {}
    frame.separators = {}

    for groupIndex, group in ipairs(STATS_CONFIG.groups) do
        for _, def in ipairs(group.rows) do
            local row = CreateFrame("Frame", nil, frame, "BackdropTemplate")
            row:SetHeight(STAT_ROW_HEIGHT)

            row.label = row:CreateFontString(nil, "OVERLAY")
            row.label:SetPoint("LEFT", 5, 0)
            row.label:SetJustifyH("LEFT")

            row.inspectedValue = row:CreateFontString(nil, "OVERLAY")
            row.inspectedValue:SetJustifyH("RIGHT")

            row.playerValue = row:CreateFontString(nil, "OVERLAY")
            row.playerValue:SetJustifyH("RIGHT")

            row.def = def
            row.groupColor = group.color
            row.groupIndex = groupIndex

            tinsert(frame.rows, row)
        end

        if groupIndex < #STATS_CONFIG.groups then
            local separator = frame:CreateTexture(nil, "ARTWORK")
            separator:SetTexture(addon.media.normTex)
            separator:SetVertexColor(0.5, 0.5, 0.5, 0.5)
            separator:SetHeight(1)
            tinsert(frame.separators, separator)
        end
    end

    parent:HookScript("OnHide", function()
        frame:Hide()
    end)

    self.compareStatsFrame = frame
    return frame
end

function HLY:UpdateCompareStatsFrame(frame, inspectedUnit, inspectedItemLevel)
    if not frame or not frame.rows then
        return
    end

    local db = self.db.compareStats
    local inspectedStats = GetUnitStatsForCompare(inspectedUnit, inspectedItemLevel)
    local playerStats = GetUnitStatsForCompare("player", self:GetUnitItemLevel("player"))

    local inspectedName = UnitName(inspectedUnit) or ""
    local playerName = UnitName("player") or ""
    local inspectedClassColor = self:GetClassColor(select(2, UnitClass(inspectedUnit)))
    local playerClassColor = self:GetClassColor(select(2, UnitClass("player")))

    local maxWidths = { label = 0, inspected = 0, player = 0 }
    local visibleRows = {}

    for _, row in ipairs(frame.rows) do
        local def = row.def

        self:ApplyCompareStatsTextStyle(row.label)
        self:ApplyCompareStatsTextStyle(row.inspectedValue)
        self:ApplyCompareStatsTextStyle(row.playerValue)

        local isVisible = false
        local inspectedWidth, playerWidth = 0, 0

        if def.isHeader then
            row.label:SetText("")
            row.label:Hide()
            row.inspectedValue:SetText(inspectedName)
            row.playerValue:SetText(playerName)
            row.inspectedValue:SetTextColor(inspectedClassColor.r, inspectedClassColor.g, inspectedClassColor.b)
            row.playerValue:SetTextColor(playerClassColor.r, playerClassColor.g, playerClassColor.b)

            isVisible = true
            inspectedWidth = row.inspectedValue:GetStringWidth()
            playerWidth = row.playerValue:GetStringWidth()
        else
            local inspectedValue = inspectedStats[def.key] or 0
            local playerValue = playerStats[def.key] or 0
            local primaryKey = inspectedStats.primaryStatKey or playerStats.primaryStatKey
            local isPrimaryRow = def.key == "strength" or def.key == "agility" or def.key == "intellect"

            if not (isPrimaryRow and primaryKey and def.key ~= primaryKey) then
                isVisible = not (db.comparison.hideIfBothZero and inspectedValue == 0 and playerValue == 0)
            end

            if isVisible then
                row.label:Show()
                row.label:SetText(def.name)

                local inspectedText = self:ShortValue(inspectedValue, 1)
                local playerText = self:ShortValue(playerValue, 1)

                if playerValue > 0 and inspectedValue > 0 then
                    local diffText = ""
                    local isPositive = false

                    if def.diffType == "value" then
                        local diff = inspectedValue - playerValue
                        if diff ~= 0 then
                            isPositive = diff > 0
                            diffText = (isPositive and "+" or "") .. self:ShortValue(diff, 1)
                        end
                    elseif def.diffType == "percent" then
                        local diffPercent = ((inspectedValue - playerValue) / playerValue) * 100
                        if abs(diffPercent) >= 0.1 then
                            isPositive = diffPercent > 0
                            diffText = (isPositive and "+" or "") .. format("%.1f%%", diffPercent)
                        end
                    end

                    if diffText ~= "" then
                        local colorConfig = isPositive and db.comparison.higherColor or db.comparison.lowerColor
                        inspectedText = format("%s %s", ColorText(diffText, colorConfig), inspectedText)
                    end
                end

                row.inspectedValue:SetText(inspectedText)
                row.playerValue:SetText(playerText)

                local colorTemplate = def.color or row.groupColor
                if colorTemplate then
                    row.label:SetTextColor(colorTemplate.r, colorTemplate.g, colorTemplate.b)
                else
                    row.label:SetTextColor(1, 1, 1)
                end

                local inspectedColor = inspectedValue == 0 and 0.6 or 1
                local playerColor = playerValue == 0 and 0.6 or 1
                row.inspectedValue:SetTextColor(inspectedColor, inspectedColor, inspectedColor)
                row.playerValue:SetTextColor(playerColor, playerColor, playerColor)

                inspectedWidth = row.inspectedValue:GetStringWidth()
                playerWidth = row.playerValue:GetStringWidth()
            end
        end

        if isVisible then
            tinsert(visibleRows, { row = row, groupIndex = row.groupIndex })
            maxWidths.label = max(maxWidths.label, row.label:GetStringWidth())
            maxWidths.inspected = max(maxWidths.inspected, inspectedWidth)
            maxWidths.player = max(maxWidths.player, playerWidth)
        end
    end

    maxWidths.label = maxWidths.label + 10
    maxWidths.inspected = maxWidths.inspected + 10
    maxWidths.player = maxWidths.player + 10

    local yOffset = -STAT_PADDING
    local separatorIndex = 1
    local previousGroupIndex = nil

    for _, rowData in ipairs(visibleRows) do
        local row = rowData.row
        local currentGroupIndex = rowData.groupIndex
        local needsSeparator = previousGroupIndex and currentGroupIndex ~= previousGroupIndex

        if needsSeparator and frame.separators[separatorIndex] then
            yOffset = yOffset - STAT_ROW_SEPARATOR_SPACING
            local separator = frame.separators[separatorIndex]
            separator:ClearAllPoints()
            separator:SetPoint("TOPLEFT", frame, "TOPLEFT", STAT_PADDING, yOffset)
            separator:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -STAT_PADDING, yOffset)
            separator:Show()
            separatorIndex = separatorIndex + 1
            yOffset = yOffset - 1 - STAT_ROW_SEPARATOR_SPACING
        end

        row:ClearAllPoints()
        row:SetPoint("TOPLEFT", frame, "TOPLEFT", STAT_PADDING, yOffset)
        row:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -STAT_PADDING, yOffset)

        row.label:SetWidth(maxWidths.label)
        row.inspectedValue:SetWidth(maxWidths.inspected)
        row.playerValue:SetWidth(maxWidths.player)

        row.inspectedValue:ClearAllPoints()
        row.playerValue:ClearAllPoints()
        row.inspectedValue:SetPoint("LEFT", row.label, "RIGHT", 5, 0)
        row.playerValue:SetPoint("LEFT", row.inspectedValue, "RIGHT", 5, 0)

        row:Show()
        yOffset = yOffset - STAT_ROW_HEIGHT
        previousGroupIndex = currentGroupIndex
    end

    for i = separatorIndex, #frame.separators do
        frame.separators[i]:Hide()
    end

    for _, row in ipairs(frame.rows) do
        local isVisible = false
        for _, rowData in ipairs(visibleRows) do
            if row == rowData.row then
                isVisible = true
                break
            end
        end
        if not isVisible then
            row:Hide()
        end
    end

    local contentHeight = -yOffset + STAT_PADDING
    local totalWidth = maxWidths.label + maxWidths.inspected + maxWidths.player + 40
    frame:SetSize(totalWidth, contentHeight)
    frame:Show()
end

function HLY:HideCompareStats()
    if self.compareStatsRetryTimer then
        self.compareStatsRetryTimer:Cancel()
        self.compareStatsRetryTimer = nil
    end

    if self.compareStatsFrame then
        self.compareStatsFrame:Hide()
    end
end

function HLY:ScheduleCompareStatsRetry(attempt)
    if not C_Timer or attempt >= INSPECT_RETRY_MAX_ATTEMPTS then
        return
    end

    if self.compareStatsRetryTimer then
        self.compareStatsRetryTimer:Cancel()
    end

    self.compareStatsRetryTimer = C_Timer.NewTimer(INSPECT_RETRY_DELAY, function()
        self.compareStatsRetryTimer = nil
        self:RefreshCompareStats((attempt or 0) + 1)
    end)
end

function HLY:RefreshCompareStats(attempt)
    attempt = attempt or 0

    if not self.inspectCompareHooksSet then
        self:SetupInspectCompareHooks()
    end

    if not self.db or not self.db.compareStats or not self.db.compareStats.enabled then
        self:HideCompareStats()
        return
    end

    local inspectFrame = _G.InspectFrame
    if not inspectFrame or not inspectFrame:IsShown() or not inspectFrame.unit then
        self:HideCompareStats()
        return
    end

    local inspectedUnit = inspectFrame.unit
    self.compareStatsLastUnit = inspectedUnit
    self.compareStatsLastGUID = UnitGUID and UnitGUID(inspectedUnit) or nil

    local itemLevel = self:GetUnitItemLevel(inspectedUnit)
    if type(itemLevel) ~= "number" then
        itemLevel = tonumber(itemLevel)
    end

    local primaryStatKey = GetPrimaryStatKey(inspectedUnit)
    local frame = self:CreateCompareStatsFrame()
    if frame then
        self:UpdateCompareStatsFrame(frame, inspectedUnit, itemLevel)
    end

    if (not itemLevel or itemLevel <= 0 or not primaryStatKey) and attempt < INSPECT_RETRY_MAX_ATTEMPTS then
        self:ScheduleCompareStatsRetry(attempt)
    end
end

function HLY:SetupInspectCompareHooks()
    if self.inspectCompareHooksSet then
        return
    end

    local inspectFrame = _G.InspectFrame
    if not inspectFrame then
        return
    end

    inspectFrame:HookScript("OnShow", function()
        self.compareStatsLastUnit = nil
        self.compareStatsLastGUID = nil
        self:RefreshCompareStats()
    end)

    inspectFrame:HookScript("OnHide", function()
        self:HideCompareStats()
    end)

    inspectFrame:HookScript("OnUpdate", function(frame, elapsed)
        frame.hlyCompareStatsElapsed = (frame.hlyCompareStatsElapsed or 0) + elapsed
        if frame.hlyCompareStatsElapsed < INSPECT_WATCH_INTERVAL then
            return
        end

        frame.hlyCompareStatsElapsed = 0

        if not self.db or not self.db.compareStats or not self.db.compareStats.enabled or not frame:IsShown() then
            return
        end

        local unit = frame.unit
        local guid = unit and UnitGUID and UnitGUID(unit) or nil
        if unit ~= self.compareStatsLastUnit or guid ~= self.compareStatsLastGUID then
            self:RefreshCompareStats()
        end
    end)

    self.inspectCompareHooksSet = true
end
