-- RL Pala Tank Helper
-- Shows paladin tank stats next to the character frame.

local addonName, addon = ...

-- Version detection
local _, build, _, tocVersion = GetBuildInfo()
local isClassic = tocVersion < 20000
local isTBC = tocVersion >= 20000 and tocVersion < 30000
local isWrath = tocVersion >= 30000 and tocVersion < 40000
local isCata = tocVersion >= 40000 and tocVersion < 50000
local isRetail = tocVersion >= 50000

-- Boss-level constants based on expansion
local BOSS_LEVEL = isClassic and 63 or (isTBC and 73 or (isWrath and 83 or (isCata and 88 or (isRetail and (UnitLevel("player") + 3) or 73))))
local BASE_PLAYER_DEFENSE = isClassic and 300 or 350
local CRIT_REDUCTION_REQUIRED = isClassic and 5.6 or (isTBC and 5.6 or (isWrath and 5.6 or 0))
local CRUSH_AVOIDANCE_REQUIRED = isClassic and 102.4 or (isTBC and 102.4 or (isWrath and 102.4 or 0))
local ARMOR_CONSTANT = isClassic and (467.5 * BOSS_LEVEL - 22167.5) or (isTBC and (467.5 * BOSS_LEVEL - 22167.5) or (isWrath and (467.5 * BOSS_LEVEL - 22167.5) or (isCata and (467.5 * BOSS_LEVEL - 22167.5) or (isRetail and (467.5 * BOSS_LEVEL - 22167.5) or 11960))))

-- SavedVariables
local db = RLPalaTankHelperDB or {}
RLPalaTankHelperDB = db

-- Default position (lower than before)
if not db.point then
    db.point = "TOPLEFT"
    db.relativeTo = "CharacterFrame"
    db.relativePoint = "TOPRIGHT"
    db.xOffset = -2
    db.yOffset = -50
end

-- Main frame
local frame = CreateFrame("Frame", "RLPalaTankHelperFrame", UIParent, BackdropTemplateMixin and "BackdropTemplate" or nil)
frame:SetSize(200, 310)
frame:SetFrameStrata("HIGH")
frame:SetMovable(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", function(self)
    self:StartMoving()
end)
frame:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local point, relativeTo, relativePoint, x, y = self:GetPoint()
    db.point = point
    db.relativeTo = relativeTo and relativeTo:GetName() or "UIParent"
    db.relativePoint = relativePoint
    db.xOffset = x
    db.yOffset = y
end)
frame:SetBackdrop({
    bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    tile = true, tileSize = 32, edgeSize = 16,
    insets = { left = 4, right = 4, top = 4, bottom = 4 }
})
frame:SetBackdropColor(0, 0, 0, 0.85)
frame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
frame:SetPoint(db.point, db.relativeTo, db.relativePoint, db.xOffset, db.yOffset)
frame:Hide()

-- Logo
local logo = frame:CreateTexture(nil, "ARTWORK")
logo:SetSize(32, 32)
logo:SetPoint("TOPLEFT", frame, "TOPLEFT", 12, -10)
logo:SetTexture("Interface\\AddOns\\RLPalaTankHelper\\img\\logo.png")
frame.logo = logo

-- Title
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
title:SetPoint("LEFT", logo, "RIGHT", 8, 0)
title:SetText("|cffffd100Tank Stats|r")

-- Create text rows
local texts = {}
local rowHeight = 14
for i = 1, 15 do
    texts[i] = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    texts[i]:SetJustifyH("LEFT")
    texts[i]:SetPoint("TOPLEFT", 12, -48 - (i * rowHeight))
end

-- About Button
local aboutBtn = CreateFrame("Button", nil, frame)
aboutBtn:SetSize(80, 24)
aboutBtn:SetPoint("BOTTOM", frame, "BOTTOM", 0, 8)

local aboutBtnBg = aboutBtn:CreateTexture(nil, "BACKGROUND")
aboutBtnBg:SetAllPoints()
aboutBtnBg:SetColorTexture(0.3, 0.3, 0.35, 1)
aboutBtn.bg = aboutBtnBg

local aboutBtnText = aboutBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
aboutBtnText:SetPoint("CENTER", aboutBtn, "CENTER", 0, 0)
aboutBtnText:SetText("About")
aboutBtn.text = aboutBtnText

aboutBtn:SetScript("OnEnter", function(self)
    self.bg:SetColorTexture(0.4, 0.4, 0.5, 1)
end)
aboutBtn:SetScript("OnLeave", function(self)
    self.bg:SetColorTexture(0.3, 0.3, 0.35, 1)
end)

-- About Panel
local aboutPanel = CreateFrame("Frame", "RLPalaTankHelperAboutPanel", UIParent, BackdropTemplateMixin and "BackdropTemplate" or nil)
aboutPanel:SetSize(350, 320)
aboutPanel:SetFrameStrata("DIALOG")
aboutPanel:SetMovable(true)
aboutPanel:EnableMouse(true)
aboutPanel:RegisterForDrag("LeftButton")
aboutPanel:SetScript("OnDragStart", function(self) self:StartMoving() end)
aboutPanel:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
aboutPanel:SetBackdrop({
    bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    tile = true, tileSize = 32, edgeSize = 16,
    insets = { left = 4, right = 4, top = 4, bottom = 4 }
})
aboutPanel:SetBackdropColor(0, 0, 0, 0.9)
aboutPanel:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
aboutPanel:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
aboutPanel:Hide()

-- About Panel Logo (centered, square aspect ratio like RLRaidDungeonReset)
local aboutLogo = aboutPanel:CreateTexture(nil, "ARTWORK")
aboutLogo:SetPoint("TOP", aboutPanel, "TOP", 0, -20)
aboutLogo:SetSize(180, 180)
aboutLogo:SetTexture("Interface\\AddOns\\RLPalaTankHelper\\img\\logo.png")

-- About Panel Text
local aboutText = aboutPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
aboutText:SetPoint("TOP", aboutLogo, "BOTTOM", 0, -20)
aboutText:SetWidth(aboutPanel:GetWidth() - 32)
aboutText:SetJustifyH("CENTER")
local versionText = isClassic and "Classic" or (isTBC and "TBC" or (isWrath and "Wrath" or (isCata and "Cataclysm" or "Retail")))
aboutText:SetText("RL Pala Tank Helper\nVersion: 1.0\nAuthor: RL\n\nDisplays paladin tank stats next to the character frame.\n\nCompatible with: Classic, TBC, Wrath, Cataclysm, and Retail\nCurrent Game: " .. versionText)

-- Close Button for About Panel
local closeAboutBtn = CreateFrame("Button", nil, aboutPanel)
closeAboutBtn:SetSize(24, 24)
closeAboutBtn:SetPoint("TOPRIGHT", aboutPanel, "TOPRIGHT", -8, -8)

local closeAboutBg = closeAboutBtn:CreateTexture(nil, "BACKGROUND")
closeAboutBg:SetAllPoints()
closeAboutBg:SetColorTexture(0.3, 0.3, 0.35, 1)

local closeAboutText = closeAboutBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
closeAboutText:SetPoint("CENTER", closeAboutBtn, "CENTER", 0, 1)
closeAboutText:SetText("X")

closeAboutBtn:SetScript("OnEnter", function(self)
    self.bg:SetColorTexture(0.6, 0.2, 0.2, 1)
end)
closeAboutBtn:SetScript("OnLeave", function(self)
    self.bg:SetColorTexture(0.3, 0.3, 0.35, 1)
end)
closeAboutBtn:SetScript("OnClick", function()
    aboutPanel:Hide()
end)

aboutBtn:SetScript("OnClick", function()
    aboutPanel:Show()
end)

local function GetDefenseSkill()
    if UnitDefense then
        local base, modifier = UnitDefense("player")
        return base + modifier
    end
    return 0
end

-- Check if player is a Paladin
local function IsPaladin()
    local _, class = UnitClass("player")
    return class == "PALADIN"
end

local function UpdateStats()
    local defense = GetDefenseSkill()
    local dodge = GetDodgeChance() or 0
    local parry = GetParryChance() or 0
    local block = GetBlockChance() or 0

    -- Base miss 5% + defense contribution above boss base defense (only for Classic/TBC/Wrath)
    local miss = 5
    local avoid = dodge + parry
    local critRed = 0
    local crushAvoid = 0

    if isClassic or isTBC or isWrath then
        miss = 5 + (defense - BASE_PLAYER_DEFENSE) * 0.04
        avoid = miss + dodge + parry
        critRed = (defense - BASE_PLAYER_DEFENSE) * 0.04
        crushAvoid = avoid + block
    end

    local blockValue = (GetShieldBlock and GetShieldBlock()) or (GetBlockValue and GetBlockValue()) or 0
    local _, effectiveArmor = UnitArmor("player")
    local dr = effectiveArmor / (effectiveArmor + ARMOR_CONSTANT)
    local hp = UnitHealthMax("player")
    local ehp = math.floor(hp / (1 - dr))

    local sp = GetSpellBonusDamage and GetSpellBonusDamage(2) or 0
    local mh = (GetHitModifier and GetHitModifier()) or 0
    local sh = (GetSpellHitModifier and GetSpellHitModifier()) or 0

    -- Retail: Get hit chance from combat ratings
    if isRetail then
        local hitRating = GetCombatRating and GetCombatRating(CR_HIT_MELEE) or 0
        local spellHitRating = GetCombatRating and GetCombatRating(CR_HIT_SPELL) or 0
        mh = hitRating > 0 and (GetCombatRatingBonus and GetCombatRatingBonus(CR_HIT_MELEE) or 0) or 0
        sh = spellHitRating > 0 and (GetCombatRatingBonus and GetCombatRatingBonus(CR_HIT_SPELL) or 0) or 0
    end

    local row = 1
    local function FormatValue(label, current, required, isPercent)
        if required and required > 0 then
            local color = current >= required and "|cff00ff00" or "|cffff0000"
            if isPercent then
                return string.format("%s%s:|r %s%.2f%% / %.1f%%", color, label, color, current, required)
            else
                return string.format("%s%s:|r %s%d / %d", color, label, color, current, required)
            end
        else
            if isPercent then
                return string.format("|cff00ff00%s:|r %.2f%%", label, current)
            else
                return string.format("|cff00ff00%s:|r %d", label, current)
            end
        end
    end

    texts[row]:SetText(FormatValue("Dodge", dodge, nil, true))
    row = row + 1
    texts[row]:SetText(FormatValue("Parry", parry, nil, true))
    row = row + 1
    texts[row]:SetText(FormatValue("Block", block, nil, true))
    row = row + 1

    if isClassic or isTBC or isWrath then
        texts[row]:SetText(FormatValue("Miss", miss, nil, true))
        row = row + 1
        texts[row]:SetText(FormatValue("Avoid", avoid, nil, true))
        row = row + 1
        texts[row]:SetText(FormatValue("Crit Red", critRed, CRIT_REDUCTION_REQUIRED, true))
        row = row + 1
        texts[row]:SetText(FormatValue("Crush Avoid", crushAvoid, CRUSH_AVOIDANCE_REQUIRED, true))
        row = row + 1
        texts[row]:SetText(FormatValue("Defense", defense, nil, false))
        row = row + 1
    end

    texts[row]:SetText(FormatValue("Block Value", blockValue, nil, false))
    row = row + 1
    texts[row]:SetText(FormatValue("Armor", effectiveArmor, nil, false))
    row = row + 1
    texts[row]:SetText(string.format("|cffffff00DR:|r %.2f%%", dr * 100))
    row = row + 1
    texts[row]:SetText(string.format("|cffffff00EHP/HP:|r %d/%d", ehp, hp))
    row = row + 1
    texts[row]:SetText(FormatValue("Spell Power", sp, nil, false))
    row = row + 1
    texts[row]:SetText(FormatValue("Spell Hit", sh, nil, true))
    row = row + 1
    texts[row]:SetText(FormatValue("Melee Hit", mh, nil, true))
    row = row + 1

    -- Hide unused rows
    for i = row + 1, 15 do
        texts[i]:SetText("")
    end
end

local function UpdateVisibility()
    if CharacterFrame:IsShown() and IsPaladin() then
        frame:Show()
        UpdateStats()
    else
        frame:Hide()
    end
end

-- Show/hide alongside the character paper doll
CharacterFrame:HookScript("OnShow", UpdateVisibility)
CharacterFrame:HookScript("OnHide", function() frame:Hide(); aboutPanel:Hide() end)
hooksecurefunc("CharacterFrame_ShowSubFrame", function()
    UpdateVisibility()
end)

-- Also hook PaperDollFrame if it exists
if PaperDollFrame then
    PaperDollFrame:HookScript("OnShow", UpdateVisibility)
end

-- Register stat update events
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("UNIT_STATS")
frame:RegisterEvent("UNIT_DEFENSE")
frame:RegisterEvent("UNIT_RESISTANCES")
frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
frame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
frame:RegisterEvent("UNIT_AURA")
frame:RegisterEvent("COMBAT_RATING_UPDATE")
frame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
frame:RegisterEvent("PLAYER_TALENT_UPDATE")
frame:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
frame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")

frame:SetScript("OnEvent", function(self, event, arg1)
    if event == "PLAYER_TALENT_UPDATE" or event == "ACTIVE_TALENT_GROUP_CHANGED" or event == "PLAYER_SPECIALIZATION_CHANGED" then
        UpdateVisibility()
    elseif event == "UNIT_STATS" or event == "UNIT_DEFENSE" or event == "UNIT_RESISTANCES" or event == "UNIT_AURA" or event == "COMBAT_RATING_UPDATE" then
        if arg1 == "player" then
            UpdateStats()
        end
    else
        UpdateStats()
    end
end)

-- OnUpdate for real-time updates (every 0.5 seconds when visible)
local updateInterval = 0.5
local lastUpdate = 0
frame:SetScript("OnUpdate", function(self, elapsed)
    if not self:IsShown() then return end
    lastUpdate = lastUpdate + elapsed
    if lastUpdate >= updateInterval then
        UpdateStats()
        lastUpdate = 0
    end
end)
