local addonName, NS = ...

-- ============================================================================
-- VAULTSKINS: REWARD CARD SKINNING
-- ============================================================================
local Skin = NS.Skin
local Shared = NS.SkinShared
local ApplyBackdrop = Shared.ApplyBackdrop
local ApplyStyledBackdrop = Shared.ApplyStyledBackdrop
local BlendColor = Shared.BlendColor
local CancelOwnedTimer = Shared.CancelOwnedTimer
local ClampNumber = Shared.ClampNumber
local ConfigureThemeAsset = Shared.ConfigureThemeAsset
local CreateBackdropFrame = Shared.CreateBackdropFrame
local CreateLine = Shared.CreateLine
local CreateThemeAsset = Shared.CreateThemeAsset
local EnsureReadableColor = Shared.EnsureReadableColor
local GetEdgeOnlyBackdrop = Shared.GetEdgeOnlyBackdrop
local GetEffectiveThemeStyle = Shared.GetEffectiveThemeStyle
local HideVisual = Shared.HideVisual
local IsOwnedTimerPending = Shared.IsOwnedTimerPending
local RecordStabilityMetric = Shared.RecordStabilityMetric
local ResolveArtworkColor = Shared.ResolveArtworkColor
local RestoreVisualAlpha = Shared.RestoreVisualAlpha
local SOFT_GLOW_TEXTURE = Shared.SOFT_GLOW_TEXTURE
local SafeCall = Shared.SafeCall
local ScaleColor = Shared.ScaleColor
local ScheduleOwnedTimer = Shared.ScheduleOwnedTimer
local SetColor = Shared.SetColor
local SetVertexColor = Shared.SetVertexColor
local StyleValue = Shared.StyleValue
local ThemeAssetPath = Shared.ThemeAssetPath
local TintVisual = Shared.TintVisual
local WHITE_TEXTURE = Shared.WHITE_TEXTURE

-- ======================== ROW AND CARD CONSTRUCTION =========================
function Skin:CreateRowSkin(rowFrame)
    if not rowFrame or rowFrame.VaultSkins then
        return rowFrame and rowFrame.VaultSkins
    end

    local skin = {}
    rowFrame.VaultSkins = skin
    if rowFrame.Name then
        local r, g, b, a = rowFrame.Name:GetTextColor()
        local font, size, flags = rowFrame.Name:GetFont()
        skin.OriginalNameColor = { r = r, g = g, b = b, a = a }
        skin.OriginalNameFont = { font = font, size = size, flags = flags }
    end

    skin.Panel = CreateBackdropFrame(rowFrame, 1, -1)

    skin.Shade = rowFrame:CreateTexture(nil, "ARTWORK", nil, 1)
    skin.Shade:SetTexture(WHITE_TEXTURE)
    skin.Shade:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", 2, -2)
    skin.Shade:SetPoint("BOTTOMRIGHT", rowFrame, "BOTTOMRIGHT", -2, 2)

    skin.Outline = CreateFrame("Frame", nil, rowFrame, "BackdropTemplate")
    skin.Outline:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", 0, 0)
    skin.Outline:SetPoint("BOTTOMRIGHT", rowFrame, "BOTTOMRIGHT", 0, 0)
    skin.Outline:SetFrameLevel(rowFrame:GetFrameLevel() + 2)
    skin.Outline:EnableMouse(false)

    skin.Accent = CreateLine(rowFrame, "OVERLAY")
    skin.Accent:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", 2, -8)
    skin.Accent:SetPoint("BOTTOMLEFT", rowFrame, "BOTTOMLEFT", 2, 8)
    skin.Accent:SetWidth(4)

    skin.TopLine = CreateLine(rowFrame, "BORDER")
    skin.TopLine:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", 10, -5)
    skin.TopLine:SetPoint("TOPRIGHT", rowFrame, "TOPRIGHT", -10, -5)
    skin.TopLine:SetHeight(1)

    skin.BottomLine = CreateLine(rowFrame, "BORDER")
    skin.BottomLine:SetPoint("BOTTOMLEFT", rowFrame, "BOTTOMLEFT", 10, 5)
    skin.BottomLine:SetPoint("BOTTOMRIGHT", rowFrame, "BOTTOMRIGHT", -10, 5)
    skin.BottomLine:SetHeight(1)

    return skin
end

function Skin:BuildActivitySkinUnsafe(activity)
    if not activity then
        return nil
    end
    if activity.VaultSkins and activity.VaultSkins.__complete then
        return activity.VaultSkins
    end

    -- Publish the card skin only after every required region is created.
    local skin = {}
    activity.VaultSkinsBuilding = skin

    skin.Shadow = CreateFrame("Frame", nil, activity, "BackdropTemplate")
    skin.Shadow:SetPoint("TOPLEFT", activity, "TOPLEFT", -5, 5)
    skin.Shadow:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", 5, -6)
    skin.Shadow:SetFrameLevel(math.max(0, activity:GetFrameLevel() - 2))
    skin.Shadow:EnableMouse(false)

    skin.Card = CreateBackdropFrame(activity, 0, -1)
    skin.Inner = CreateBackdropFrame(activity, 4, -1)

    skin.HeaderShade = activity:CreateTexture(nil, "BACKGROUND", nil, 1)
    skin.HeaderShade:SetTexture(WHITE_TEXTURE)
    skin.HeaderShade:SetPoint("TOPLEFT", activity, "TOPLEFT", 4, -4)
    skin.HeaderShade:SetPoint("TOPRIGHT", activity, "TOPRIGHT", -4, -4)
    skin.HeaderShade:SetHeight(32)

    skin.LeftAccent = CreateLine(activity, "BORDER")
    skin.LeftAccent:SetPoint("TOPLEFT", activity, "TOPLEFT", 3, -9)
    skin.LeftAccent:SetPoint("BOTTOMLEFT", activity, "BOTTOMLEFT", 3, 9)
    skin.LeftAccent:SetWidth(3)

    skin.TopLine = CreateLine(activity, "BORDER")
    skin.TopLine:SetPoint("TOPLEFT", activity, "TOPLEFT", 10, -7)
    skin.TopLine:SetPoint("TOPRIGHT", activity, "TOPRIGHT", -10, -7)
    skin.TopLine:SetHeight(2)

    skin.BottomLine = CreateLine(activity, "BORDER")
    skin.BottomLine:SetPoint("BOTTOMLEFT", activity, "BOTTOMLEFT", 10, 7)
    skin.BottomLine:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -10, 7)
    skin.BottomLine:SetHeight(1)

    skin.Hover = activity:CreateTexture(nil, "ARTWORK", nil, 7)
    skin.Hover:SetTexture(WHITE_TEXTURE)
    skin.Hover:SetPoint("TOPLEFT", activity, "TOPLEFT", 3, -3)
    skin.Hover:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -3, 3)
    skin.Hover:SetBlendMode("ADD")
    skin.Hover:SetAlpha(0)

    skin.Glow = activity:CreateTexture(nil, "BACKGROUND", nil, 7)
    skin.Glow:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight2")
    skin.Glow:SetBlendMode("ADD")
    skin.Glow:SetPoint("TOPLEFT", activity, "TOPLEFT", 2, -2)
    skin.Glow:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -2, 2)
    skin.Glow:SetAlpha(0)

    skin.Dim = activity:CreateTexture(nil, "ARTWORK", nil, 5)
    skin.Dim:SetTexture(WHITE_TEXTURE)
    skin.Dim:SetPoint("TOPLEFT", activity, "TOPLEFT", 3, -3)
    skin.Dim:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -3, 3)
    skin.Dim:SetColorTexture(0, 0, 0, 1)
    skin.Dim:SetAlpha(0)

    skin.RewardShimmer = activity:CreateTexture(nil, "ARTWORK", nil, 4)
    skin.RewardShimmer:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight2")
    skin.RewardShimmer:SetBlendMode("ADD")
    skin.RewardShimmer:SetPoint("TOPLEFT", activity, "TOPLEFT", 2, -2)
    skin.RewardShimmer:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -2, 2)
    skin.RewardShimmer:SetAlpha(0)

    skin.RarityHalo = activity:CreateTexture(nil, "BACKGROUND", nil, 6)
    skin.RarityHalo:SetTexture(SOFT_GLOW_TEXTURE)
    skin.RarityHalo:SetBlendMode("ADD")
    skin.RarityHalo:SetPoint("TOPLEFT", activity, "TOPLEFT", 0, 0)
    skin.RarityHalo:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", 0, 0)
    skin.RarityHalo:SetAlpha(0)


    skin.SelectedGlow = activity:CreateTexture(nil, "OVERLAY", nil, 5)
    skin.SelectedGlow:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight2")
    skin.SelectedGlow:SetBlendMode("ADD")
    skin.SelectedGlow:SetPoint("TOPLEFT", activity, "TOPLEFT", 2, -2)
    skin.SelectedGlow:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -2, 2)
    skin.SelectedGlow:SetAlpha(0)

    skin.SelectedOutline = CreateFrame("Frame", nil, activity, "BackdropTemplate")
    skin.SelectedOutline:SetPoint("TOPLEFT", activity, "TOPLEFT", 2, -2)
    skin.SelectedOutline:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -2, 2)
    skin.SelectedOutline:SetFrameLevel(activity:GetFrameLevel() + 18)
    skin.SelectedOutline:EnableMouse(false)
    skin.SelectedOutline:SetAlpha(0)
    skin.SelectedOutline:Hide()

    skin.SelectedBadge = CreateFrame("Frame", nil, activity, "BackdropTemplate")
    skin.SelectedBadge:SetSize(24, 24)
    skin.SelectedBadge:SetPoint("TOPRIGHT", activity, "TOPRIGHT", -7, -7)
    skin.SelectedBadge:SetFrameLevel(activity:GetFrameLevel() + 19)
    skin.SelectedBadge:EnableMouse(false)
    skin.SelectedBadge.Icon = skin.SelectedBadge:CreateTexture(nil, "OVERLAY")
    skin.SelectedBadge.Icon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    skin.SelectedBadge.Icon:SetPoint("CENTER", 0, 0)
    skin.SelectedBadge.Icon:SetSize(22, 22)
    skin.SelectedBadge:Hide()

    skin.SelectedPulseGroup = skin.SelectedOutline:CreateAnimationGroup()
    RecordStabilityMetric("animationGroupsCreated")
    skin.SelectedPulseGroup:SetLooping("BOUNCE")
    local selectedPulse = skin.SelectedPulseGroup:CreateAnimation("Alpha")
    selectedPulse:SetFromAlpha(0.62)
    selectedPulse:SetToAlpha(1)
    selectedPulse:SetDuration(0.72)
    selectedPulse:SetSmoothing("IN_OUT")

    skin.LockPlate = CreateFrame("Frame", nil, activity, "BackdropTemplate")
    skin.LockPlate:SetSize(42, 42)
    skin.LockPlate:SetPoint("CENTER", activity, "CENTER", 0, -13)
    skin.LockPlate:SetFrameLevel(activity:GetFrameLevel() + 3)
    skin.LockPlate:EnableMouse(false)
    skin.LockPlate:Hide()

    skin.LockIcon = skin.LockPlate:CreateTexture(nil, "OVERLAY")
    skin.LockIcon:SetTexture("Interface\\Buttons\\LockButton-Locked-Up")
    skin.LockIcon:SetPoint("CENTER", 0, 0)
    skin.LockIcon:SetSize(28, 28)
    skin.LockIcon:SetDesaturated(true)

    skin.LockMist = activity:CreateTexture(nil, "ARTWORK", nil, 2)
    skin.LockMist:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight2")
    skin.LockMist:SetBlendMode("ADD")
    skin.LockMist:SetPoint("TOPLEFT", activity, "TOPLEFT", 5, -5)
    skin.LockMist:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -5, 5)
    skin.LockMist:SetAlpha(0)


    skin.AssetCardCorners = {
        CreateThemeAsset(activity, "ARTWORK", 3),
        CreateThemeAsset(activity, "ARTWORK", 3),
        CreateThemeAsset(activity, "ARTWORK", 3),
        CreateThemeAsset(activity, "ARTWORK", 3),
    }
    skin.AssetLockFrame = CreateThemeAsset(skin.LockPlate, "OVERLAY", 1)

    skin.RewardIdleGroup = skin.RewardShimmer:CreateAnimationGroup()
    RecordStabilityMetric("animationGroupsCreated")
    skin.RewardIdleGroup:SetLooping("BOUNCE")
    local idleFade = skin.RewardIdleGroup:CreateAnimation("Alpha")
    idleFade:SetFromAlpha(0.05)
    idleFade:SetToAlpha(0.22)
    idleFade:SetDuration(1.35)
    idleFade:SetSmoothing("IN_OUT")

    skin.RevealBorder = CreateFrame("Frame", nil, activity, "BackdropTemplate")
    skin.RevealBorder:SetPoint("TOPLEFT", activity, "TOPLEFT", 1, -1)
    skin.RevealBorder:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -1, 1)
    skin.RevealBorder:SetFrameLevel(activity:GetFrameLevel() + 20)
    skin.RevealBorder:EnableMouse(false)
    skin.RevealBorder:SetBackdrop(GetEdgeOnlyBackdrop(3))
    skin.RevealBorder:SetBackdropColor(0, 0, 0, 0)
    skin.RevealBorder:SetAlpha(0)
    skin.RevealBorder:Hide()

    skin.RevealFlash = activity:CreateTexture(nil, "OVERLAY", nil, 6)
    skin.RevealFlash:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight2")
    skin.RevealFlash:SetPoint("TOPLEFT", activity, "TOPLEFT", 2, -2)
    skin.RevealFlash:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -2, 2)
    skin.RevealFlash:SetBlendMode("ADD")
    skin.RevealFlash:SetAlpha(0)
    skin.RevealFlash:Hide()


    skin.__complete = true
    activity.VaultSkins = skin
    activity.VaultSkinsBuilding = nil
    activity.VaultSkinsBuildFailed = nil
    activity.VaultSkinsBuildFailures = nil
    RecordStabilityMetric("cardBuilds")

    activity:HookScript("OnEnter", function(self)
        if not Skin:IsEnabled() then return end
        local currentTheme = Skin:GetTheme()
        SetColor(self.VaultSkins.Hover, currentTheme.glow)
        self.VaultSkins.Hover:SetAlpha(StyleValue(currentTheme, "cardStyle", "hoverAlpha", 0.22))
        Skin:UpdateActivityVisual(self, true)
    end)

    activity:HookScript("OnLeave", function(self)
        if not Skin:IsEnabled() then return end
        self.VaultSkins.Hover:SetAlpha(0)
        Skin:UpdateActivityVisual(self, false)
    end)

    if not activity.VaultSkinsSelectionHooked then
        activity.VaultSkinsSelectionHooked = true
        activity:HookScript("OnMouseUp", function(self)
            if not Skin:IsEnabled() then return end
            if not self.hasRewards then return end
            Skin.userSelectedActivity = self
            Skin.pendingSelectedActivity = self
            Skin.pendingSelectionExpires = GetTime() + 0.35
            Skin.pendingSelectionRefreshQueued = true
            SafeCall("pending selection refresh", function()
                if WeeklyRewardsFrame and WeeklyRewardsFrame.Activities then
                    for _, other in ipairs(WeeklyRewardsFrame.Activities) do
                        Skin:UpdateActivityVisual(other, other:IsMouseOver())
                    end
                end
            end)
            ScheduleOwnedTimer("selection-confirm", 0.06, function()
                Skin.pendingSelectionRefreshQueued = false
                if not Skin:IsEnabled() then return end
                if Skin.pendingSelectedActivity == self and WeeklyRewardsFrame and WeeklyRewardsFrame.selectedActivity ~= self then
                    if WeeklyRewardsFrame and WeeklyRewardsFrame.Activities then
                        for _, other in ipairs(WeeklyRewardsFrame.Activities) do
                            Skin:UpdateActivityVisual(other, other:IsMouseOver())
                        end
                    end
                end
            end)
        end)
    end

    return skin
end

-- ========================== PARTIAL BUILD RECOVERY ==========================
local function HidePartialSkinObject(object)
    if not object then return end
    local objectType = type(object)
    if objectType ~= "table" and objectType ~= "userdata" then return end
    if object.IsPlaying and object:IsPlaying() and object.Stop then pcall(object.Stop, object) end
    if object.SetAlpha then pcall(object.SetAlpha, object, 0) end
    if object.Hide then pcall(object.Hide, object) end
end

function Skin:CleanupPartialActivitySkin(activity)
    if not activity then return end
    local skin = activity.VaultSkinsBuilding or activity.VaultSkins
    if skin then
        for key, object in pairs(skin) do
            if type(object) == "table" and object[1] then
                for _, child in ipairs(object) do HidePartialSkinObject(child) end
            else
                HidePartialSkinObject(object)
            end
        end
    end
    activity.VaultSkinsBuilding = nil
    activity.VaultSkins = nil
end

function Skin:RestoreActivityOriginal(activity)
    if not activity then return end
    RestoreVisualAlpha(activity.Background, 1)
    RestoreVisualAlpha(activity.Border, 1)
    RestoreVisualAlpha(activity.ItemGlow, 1)
    RestoreVisualAlpha(activity.UncollectedGlow, 1)
    RestoreVisualAlpha(activity.SelectedTexture, 1)
    RestoreVisualAlpha(activity.SelectionGlow, 1)
    if activity.UnselectedFrame then activity.UnselectedFrame:SetAlpha(1) end
    if activity.Threshold then activity.Threshold:Show() end
    activity:SetAlpha(1)
end

function Skin:CreateActivitySkin(activity)
    if not activity then return nil end
    if activity.VaultSkins and activity.VaultSkins.__complete then return activity.VaultSkins end
    if activity.VaultSkinsBuildFailed or activity.VaultSkinsBuildInProgress then return nil end

    activity.VaultSkinsBuildInProgress = true
    local ok, result = pcall(self.BuildActivitySkinUnsafe, self, activity)
    activity.VaultSkinsBuildInProgress = nil
    if ok and result and result.__complete then return result end

    activity.VaultSkinsBuildFailures = (activity.VaultSkinsBuildFailures or 0) + 1
    activity.VaultSkinsBuildFailed = true
    RecordStabilityMetric("cardBuildFailures")
    self:CleanupPartialActivitySkin(activity)
    self:RestoreActivityOriginal(activity)
    SafeCall("activity skin build", function()
        error(tostring(result or "incomplete activity skin"), 0)
    end)
    return nil
end

-- ========================== REWARD RARITY REFRESH ===========================
function Skin:GetRewardRarityProfile(activity)
    if self.sessionDisabledFeatures and self.sessionDisabledFeatures.rewardRarity then return nil end
    if not (VaultSkinsDB and VaultSkinsDB.rewardRarityEffects ~= false) then return nil end
    return NS.RewardRarity and NS.RewardRarity:GetProfile(activity) or nil
end

function Skin:RefreshRewardRarityProfiles()
    local frame = WeeklyRewardsFrame
    if not frame or not frame.Activities or not NS.RewardRarity then return nil end
    self.rewardRaritySummary = NS.RewardRarity:AnalyzeActivities(frame.Activities)
    if self:IsEnabled() then
        for _, activity in ipairs(frame.Activities) do
            if activity then self:UpdateActivityVisual(activity, activity:IsMouseOver()) end
        end
    end
    return self.rewardRaritySummary
end

function Skin:RequestRewardRarityRefresh(delay)
    -- A full theme pass already owns rarity analysis and card redraws.
    if IsOwnedTimerPending("theme-apply") then
        self.rewardRarityToken = (self.rewardRarityToken or 0) + 1
        CancelOwnedTimer("rarity-refresh")
        RecordStabilityMetric("skippedDuplicateRefreshes")
        return
    end
    self.rewardRarityToken = (self.rewardRarityToken or 0) + 1
    local token = self.rewardRarityToken
    RecordStabilityMetric("rarityRefreshRequests")
    ScheduleOwnedTimer("rarity-refresh", delay or 0, function()
        if token ~= Skin.rewardRarityToken then return end
        SafeCall("reward rarity refresh", Skin.RefreshRewardRarityProfiles, Skin)
    end)
end

local HideNativeSelectionDecorations

-- ========================== ACTIVITY STATE STYLING ==========================
function Skin:UpdateActivityVisual(activity, hovered)
    if not activity or not self:IsEnabled() then
        return
    end

    local skin = self:CreateActivitySkin(activity)
    if not skin or not skin.__complete then
        activity.VaultSkins = nil
        skin = self:CreateActivitySkin(activity)
    end
    if not skin or not skin.SelectedGlow or not skin.SelectedOutline or not skin.SelectedBadge then
        return
    end
    local theme = self:GetTheme()
    local cardStyle = GetEffectiveThemeStyle(theme, "cardStyle")
    local cardBorderColor = theme.cardBorder or theme.border
    local selectionColor = theme.selectionAccent or theme.accent
    local rarityProfile = self:GetRewardRarityProfile(activity)
    local specialRarityColor = nil

    if self.pendingSelectionExpires and GetTime() >= self.pendingSelectionExpires then
        self.pendingSelectedActivity = nil
        self.pendingSelectionExpires = nil
    end

    local actualSelected = WeeklyRewardsFrame and WeeklyRewardsFrame.selectedActivity or nil
    local implicitSelected = false
    local selected = self.userSelectedActivity ~= nil and self.userSelectedActivity == activity
    local hasSelection = self.userSelectedActivity ~= nil

    if not selected and self.pendingSelectedActivity and self.pendingSelectedActivity == activity and self.pendingSelectionExpires and GetTime() < self.pendingSelectionExpires then
        selected = true
        hasSelection = true
    end

    local background = theme.locked
    local border = BlendColor(cardBorderColor, theme.muted, 0.62, 1)
    local borderAlpha = cardStyle.lockedBorderAlpha or 0.52

    if activity.hasRewards then
        local rewardBlend
        if theme.customPaletteActive then
            rewardBlend = selected and 0.78 or 0.64
        else
            rewardBlend = selected and (cardStyle.selectedRewardBlend or 0.34) or (cardStyle.rewardBlend or 0.22)
        end
        background = BlendColor(theme.panel, theme.reward, rewardBlend, 0.98)
        local rewardAccent = specialRarityColor or selectionColor
        border = selected and selectionColor or BlendColor(cardBorderColor, rewardAccent, 0.42, 1)
        borderAlpha = selected and 1 or (cardStyle.rewardBorderAlpha or 0.92)
    elseif activity.unlocked then
        background = theme.unlocked
        border = cardBorderColor
        borderAlpha = cardStyle.unlockedBorderAlpha or 0.92
    end

    if selected or hovered then
        border = selectionColor
        borderAlpha = 1
    end

    local innerBlend = theme.customPaletteActive and 0.18 or (cardStyle.innerBlend or 0.34)
    local innerAlpha = cardStyle.innerAlpha or 0.98
    local innerBackground = BlendColor(background, theme.panel, innerBlend, innerAlpha)
    local shadowColor = theme.shadow or { r = 0, g = 0, b = 0, a = 0.72 }

    local accentMode = cardStyle.accentMode or "left"
    local showLeft = accentMode == "left" or accentMode == "bracket"
    local showTop = accentMode == "top" or accentMode == "double" or accentMode == "bracket"
    local showBottom = accentMode == "double" or accentMode == "bracket"
    skin.LeftAccent:SetShown(showLeft)
    skin.TopLine:SetShown(showTop)
    skin.BottomLine:SetShown(showBottom)

    skin.HeaderShade:SetHeight(cardStyle.headerHeight or 32)
    skin.LeftAccent:SetWidth(cardStyle.leftAccentWidth or 3)
    local verticalInset = cardStyle.leftAccentInset or 9
    skin.LeftAccent:ClearAllPoints()
    skin.LeftAccent:SetPoint("TOPLEFT", activity, "TOPLEFT", 3, -verticalInset)
    skin.LeftAccent:SetPoint("BOTTOMLEFT", activity, "BOTTOMLEFT", 3, verticalInset)

    local lineInset = cardStyle.lineInset or 10
    skin.TopLine:ClearAllPoints()
    skin.TopLine:SetPoint("TOPLEFT", activity, "TOPLEFT", lineInset, -7)
    skin.TopLine:SetPoint("TOPRIGHT", activity, "TOPRIGHT", -lineInset, -7)
    skin.TopLine:SetHeight(cardStyle.topLineHeight or 2)
    skin.BottomLine:ClearAllPoints()
    skin.BottomLine:SetPoint("BOTTOMLEFT", activity, "BOTTOMLEFT", lineInset, 7)
    skin.BottomLine:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -lineInset, 7)
    skin.BottomLine:SetHeight(cardStyle.bottomLineHeight or 1)

    local glowInset = ClampNumber(cardStyle.glowOutset or 2, 0, 4)
    skin.Glow:ClearAllPoints()
    skin.Glow:SetPoint("TOPLEFT", activity, "TOPLEFT", glowInset, -glowInset)
    skin.Glow:SetPoint("BOTTOMRIGHT", activity, "BOTTOMRIGHT", -glowInset, glowInset)

    ApplyBackdrop(skin.Shadow, shadowColor, {
        r = 0, g = 0, b = 0, a = cardStyle.shadowBorderAlpha or 0.72,
    }, 4)

    local edgeSize = selected and (cardStyle.selectedEdgeSize or 13) or (cardStyle.normalEdgeSize or 11)
    ApplyStyledBackdrop(skin.Card, background, {
        r = border.r,
        g = border.g,
        b = border.b,
        a = borderAlpha,
    }, cardStyle, edgeSize)

    ApplyBackdrop(skin.Inner, innerBackground, {
        r = border.r,
        g = border.g,
        b = border.b,
        a = selected and (cardStyle.selectedInnerBorderAlpha or 0.48) or (cardStyle.innerBorderAlpha or 0.22),
    }, 1)

    SetColor(skin.HeaderShade, ScaleColor(
        background,
        activity.hasRewards and (cardStyle.headerRewardMultiplier or 1.20) or (cardStyle.headerNormalMultiplier or 1.16),
        0.66
    ))
    local rewardHeaderAlpha = selected and (cardStyle.selectedHeaderAlpha or 0.38) or (cardStyle.headerRewardAlpha or 0.28)
    skin.HeaderShade:SetAlpha(activity.hasRewards and rewardHeaderAlpha or (cardStyle.headerNormalAlpha or 0.38))

    SetColor(skin.LeftAccent, selected and selectionColor or border)
    skin.LeftAccent:SetAlpha(activity.hasRewards and 0.95 or (selected and 1 or 0.58))

    SetColor(skin.TopLine, selected and selectionColor or border)
    skin.TopLine:SetAlpha(selected and 1 or (activity.hasRewards and 0.84 or 0.58))

    SetColor(skin.BottomLine, cardBorderColor)
    skin.BottomLine:SetAlpha(selected and 0.72 or 0.28)

    local glowColor = specialRarityColor or theme.glow
    SetVertexColor(skin.Glow, glowColor)
    local glowMode = cardStyle.glowMode or "soft"
    local rewardGlow = activity.hasRewards and (cardStyle.rewardGlowAlpha or 0.16) or 0
    local selectedGlow = math.max(cardStyle.selectedGlowAlpha or 0.34, 0.46)
    if rarityProfile and VaultSkinsDB.rewardItemLevelGlow ~= false then
        rewardGlow = rewardGlow * (rarityProfile.glowMultiplier or 1)
    end
    if glowMode == "none" then
        rewardGlow, selectedGlow = 0, selected and 0.18 or 0
    elseif glowMode == "focused" then
        rewardGlow = rewardGlow * 0.48
        selectedGlow = math.min(0.72, selectedGlow * 1.10)
    elseif glowMode == "halo" then
        rewardGlow = math.min(0.58, rewardGlow * 1.28)
        selectedGlow = math.min(0.78, selectedGlow * 1.18)
    end
    rewardGlow = math.min(0.62, rewardGlow)
    skin.Glow:SetAlpha(selected and selectedGlow or rewardGlow)

    SetVertexColor(skin.RarityHalo, glowColor)
    local rarityHaloAlpha = 0
    if activity.hasRewards and rarityProfile and VaultSkinsDB.rewardItemLevelGlow ~= false and not selected then
        rarityHaloAlpha = 0.018 + ((rarityProfile.levelScore or 0.5) * 0.085)
        if rarityProfile.isTier then rarityHaloAlpha = math.max(rarityHaloAlpha, 0.12) end
        if rarityProfile.isRare then rarityHaloAlpha = math.max(rarityHaloAlpha, 0.16) end
    end
    skin.RarityHalo:SetAlpha(math.min(0.22, rarityHaloAlpha))

    skin.Dim:SetAlpha(hasSelection and not selected and activity.hasRewards and 0.38 or 0)

    SetVertexColor(skin.SelectedGlow, theme.glow)
    skin.SelectedGlow:SetAlpha(selected and 0.34 or 0)
    skin.SelectedOutline:SetShown(selected)
    if selected then
        ApplyBackdrop(skin.SelectedOutline, { r = 0, g = 0, b = 0, a = 0 }, {
            r = math.min(1, selectionColor.r * 1.12 + 0.18),
            g = math.min(1, selectionColor.g * 1.12 + 0.18),
            b = math.min(1, selectionColor.b * 1.12 + 0.18),
            a = 1,
        }, 3)
        ApplyBackdrop(skin.SelectedBadge, BlendColor(theme.panel, selectionColor, 0.42, 0.98), {
            r = math.min(1, selectionColor.r + 0.18),
            g = math.min(1, selectionColor.g + 0.18),
            b = math.min(1, selectionColor.b + 0.18),
            a = 1,
        }, 2)
        skin.SelectedBadge.Icon:SetVertexColor(1, 1, 1, 1)
        if not skin.SelectedPulseGroup:IsPlaying() then skin.SelectedPulseGroup:Play() end
    else
        if skin.SelectedPulseGroup:IsPlaying() then skin.SelectedPulseGroup:Stop() end
        skin.SelectedOutline:SetAlpha(0)
    end
    skin.SelectedBadge:SetShown(selected)

    local showLock = not activity.hasRewards and not activity.unlocked
    skin.LockPlate:SetShown(showLock)
    skin.LockMist:SetAlpha(showLock and 0.08 or 0)
    if showLock then
        ApplyBackdrop(skin.LockPlate, BlendColor(theme.background, theme.panel, 0.52, 0.92), {
            r = theme.muted.r,
            g = theme.muted.g,
            b = theme.muted.b,
            a = 0.56,
        }, 2)
        skin.LockIcon:SetVertexColor(theme.muted.r, theme.muted.g, theme.muted.b, 0.74)
        SetVertexColor(skin.LockMist, theme.glow)
    end

    self:ApplyCardThemeArtwork(activity, skin, theme, showLock)

    SetVertexColor(skin.RewardShimmer, theme.glow)
    if activity.hasRewards then
        if not skin.RewardIdleGroup:IsPlaying() then skin.RewardIdleGroup:Play() end
    else
        if skin.RewardIdleGroup:IsPlaying() then skin.RewardIdleGroup:Stop() end
        skin.RewardShimmer:SetAlpha(0)
    end


    if activity.Threshold then
        activity.Threshold:SetShown(not activity.hasRewards)
        local thresholdColor = EnsureReadableColor(activity.unlocked and theme.text or theme.muted, activity.unlocked and 0.80 or 0.68)
        activity.Threshold:SetTextColor(thresholdColor.r, thresholdColor.g, thresholdColor.b, activity.unlocked and 1 or 0.90)
        local font, _, flags = activity.Threshold:GetFont()
        if font then activity.Threshold:SetFont(font, 11, flags or "OUTLINE") end
    end
    if activity.Progress then
        local progressColor = EnsureReadableColor(activity.hasRewards and theme.success or (activity.unlocked and theme.text or theme.muted), activity.hasRewards and 0.74 or 0.76)
        activity.Progress:SetTextColor(progressColor.r, progressColor.g, progressColor.b, 1)
        local font, _, flags = activity.Progress:GetFont()
        if font then activity.Progress:SetFont(font, activity.hasRewards and 12 or 11, flags or "OUTLINE") end
    end
    if HideNativeSelectionDecorations then HideNativeSelectionDecorations(activity) end
end

-- ============================== THEME ARTWORK ===============================
local function ProfileSize(profile, key, fallbackWidth, fallbackHeight)
    local value = profile and profile[key]
    if type(value) == "table" then
        return tonumber(value[1]) or fallbackWidth, tonumber(value[2]) or fallbackHeight
    end
    return fallbackWidth, fallbackHeight
end

function Skin:ApplyMainThemeArtwork(frame, skin, theme)
    if not frame or not skin then return end
    local profile = theme.artworkProfile or { id = "midnight" }
    local profileID = profile.id or "midnight"
    local intensity = profile.alpha or 1
    local primary = ResolveArtworkColor(theme, profile, "primaryColor", "accent")
    local secondary = ResolveArtworkColor(theme, profile, "secondaryColor", "silver")
    local tertiary = ResolveArtworkColor(theme, profile, "tertiaryColor", "glow")
    local assets = skin.AssetArtwork
    if not assets then return end

    local crestWidth, crestHeight = ProfileSize(profile, "crestSize", 174, 86)
    local headerWidth, headerHeight = ProfileSize(profile, "headerSize", 440, 54)
    local watermarkWidth, watermarkHeight = ProfileSize(profile, "watermarkSize", 220, 220)
    local cornerWidth, cornerHeight = ProfileSize(profile, "cornerSize", 96, 96)

    ConfigureThemeAsset(
        assets.Crest,
        ThemeAssetPath(profileID, "crest"),
        "TOP", frame, "TOP", profile.crestX or 0, profile.crestY or 16,
        crestWidth, crestHeight,
        primary, (profile.crestAlpha or 0.58) * intensity
    )

    ConfigureThemeAsset(
        assets.Header,
        ThemeAssetPath(profileID, "header"),
        "TOP", frame, "TOP", profile.headerX or 0, profile.headerY or -76,
        headerWidth, headerHeight,
        secondary, (profile.headerAlpha or 0.30) * intensity
    )

    local watermarkAlpha = (profile.watermarkAlpha or 0.032) * (profile.backgroundAlpha or 1)
    if watermarkAlpha > 0 then
        ConfigureThemeAsset(
            assets.Watermark,
            ThemeAssetPath(profileID, "watermark"),
            "CENTER", frame, "CENTER", profile.watermarkX or 145, profile.watermarkY or -40,
            watermarkWidth, watermarkHeight,
            tertiary, watermarkAlpha
        )
        assets.Watermark:SetBlendMode("ADD")
    else
        assets.Watermark:SetAlpha(0)
        assets.Watermark:Hide()
    end

    local inset = profile.cornerInset or 12
    local cornerAlpha = (profile.cornerAlpha or 0.42) * intensity
    local bottomCornerAlpha = (profile.bottomCornerAlpha or cornerAlpha * 0.72)
    local corners = {
        { "TOPLEFT", "TOPLEFT", inset, -inset, { 0, 1, 0, 1 }, cornerAlpha },
        { "TOPRIGHT", "TOPRIGHT", -inset, -inset, { 1, 0, 0, 1 }, cornerAlpha },
        { "BOTTOMLEFT", "BOTTOMLEFT", inset, inset, { 0, 1, 1, 0 }, bottomCornerAlpha },
        { "BOTTOMRIGHT", "BOTTOMRIGHT", -inset, inset, { 1, 0, 1, 0 }, bottomCornerAlpha },
    }
    for index, config in ipairs(corners) do
        ConfigureThemeAsset(
            assets.Corners[index],
            ThemeAssetPath(profileID, "corner"),
            config[1], frame, config[2], config[3], config[4],
            cornerWidth, cornerHeight,
            secondary, config[6],
            config[5]
        )
    end
end

function Skin:ApplyCardThemeArtwork(activity, skin, theme, showLock)
    if not activity or not skin then return end
    local profile = theme.artworkProfile or { id = "midnight" }
    local profileID = profile.id or "midnight"
    local primary = ResolveArtworkColor(theme, profile, "primaryColor", "accent")
    local secondary = ResolveArtworkColor(theme, profile, "secondaryColor", "silver")
    local intensity = (profile.alpha or 1) * (profile.cardAlpha or 1)


    if skin.AssetCardCorners then
        local cardWidth, cardHeight = ProfileSize(profile, "cardCornerSize", 22, 22)
        local cardAlpha = (profile.cardCornerAlpha or 0.30) * intensity
        local cardInset = profile.cardCornerInset or 5
        local mode = profile.cardCornerMode or "diagonal"
        local configs = {
            { "TOPLEFT", "TOPLEFT", cardInset, -cardInset, { 0, 1, 0, 1 } },
            { "TOPRIGHT", "TOPRIGHT", -cardInset, -cardInset, { 1, 0, 0, 1 } },
            { "BOTTOMLEFT", "BOTTOMLEFT", cardInset, cardInset, { 0, 1, 1, 0 } },
            { "BOTTOMRIGHT", "BOTTOMRIGHT", -cardInset, cardInset, { 1, 0, 1, 0 } },
        }
        for index, config in ipairs(configs) do
            local show = mode == "all" or (mode == "top" and index <= 2) or (mode == "diagonal" and (index == 1 or index == 4))
            if show then
                ConfigureThemeAsset(
                    skin.AssetCardCorners[index],
                    ThemeAssetPath(profileID, "card_corner"),
                    config[1], activity, config[2], config[3], config[4],
                    cardWidth, cardHeight,
                    primary, cardAlpha,
                    config[5]
                )
            else
                skin.AssetCardCorners[index]:SetAlpha(0)
                skin.AssetCardCorners[index]:Hide()
            end
        end
    end

    if skin.AssetLockFrame then
        if showLock then
            local lockWidth, lockHeight = ProfileSize(profile, "lockSize", 58, 58)
            ConfigureThemeAsset(
                skin.AssetLockFrame,
                ThemeAssetPath(profileID, "lock_frame"),
                "CENTER", skin.LockPlate, "CENTER", profile.lockX or 0, profile.lockY or 0,
                lockWidth, lockHeight,
                secondary, (profile.lockAlpha or 0.44)
            )
        else
            skin.AssetLockFrame:SetAlpha(0)
            skin.AssetLockFrame:Hide()
        end
    end
end

HideNativeSelectionDecorations = function(activity)
    if not activity then return end
    local function HideIf(obj)
        if obj and obj.SetAlpha then obj:SetAlpha(0) end
        if obj and obj.Hide then obj:Hide() end
    end
    HideIf(activity.SelectedTexture)
    HideIf(activity.SelectionGlow)
    if activity.UnselectedFrame then activity.UnselectedFrame:SetAlpha(0) end
    local itemFrame = activity.ItemFrame or activity.Item or activity.Reward or nil
    if itemFrame then
        for _, key in ipairs({ "SelectedTexture", "SelectionGlow", "SelectionFrame", "SelectedFrame", "Glow", "Checkmark", "Check", "Highlight", "HighlightTexture", "SelectedIcon", "IconGlow", "NewItemTexture", "Overlay", "Sheen", "Sparkle", "SpellHighlight" }) do
            HideIf(itemFrame[key])
        end
        if itemFrame.Icon then
            for _, key in ipairs({ "SelectedTexture", "SelectionGlow", "SelectionFrame", "SelectedFrame", "Glow", "Checkmark", "Check", "Highlight", "HighlightTexture", "SelectedIcon", "IconGlow", "NewItemTexture", "Overlay", "Sheen", "Sparkle", "SpellHighlight" }) do
                HideIf(itemFrame.Icon[key])
            end
        end
    end
end

-- ======================= SELECTION AND REVEAL PRIVACY =======================
function Skin:ClearAutomaticSelection()
    local frame = WeeklyRewardsFrame
    if not frame or not frame.Activities or self.userSelectedActivity then return end
    if frame.selectedActivity then
        frame.selectedActivity = nil
    end
    if frame.UpdateSelection then
        SafeCall("force clear vault selection", function() frame:UpdateSelection() end)
    end
    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            if HideNativeSelectionDecorations then HideNativeSelectionDecorations(activity) end
            self:UpdateActivityVisual(activity, activity:IsMouseOver())
        end
    end
end

function Skin:PrimeOpeningRevealPrivacy()
    local frame = WeeklyRewardsFrame
    local shouldConceal = frame and frame:IsShown() and self:IsEnabled()
        and VaultSkinsDB and VaultSkinsDB.animations ~= false

    if not shouldConceal then
        self.pendingOpeningRevealPrivacy = false
        return false
    end

    self.pendingOpeningRevealPrivacy = true
    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            if activity and activity:IsShown() then
                activity:SetAlpha(0)
            end
        end
    end
    return true
end

-- ======================= PUBLIC STYLING ENTRY POINTS ========================
function Skin:StyleActivity(activity)
    if not activity then return end
    if not self:IsEnabled() then
        RestoreVisualAlpha(activity.Background, 1)
        RestoreVisualAlpha(activity.Border, 1)
        RestoreVisualAlpha(activity.ItemGlow, 1)
        RestoreVisualAlpha(activity.UncollectedGlow, 1)
        RestoreVisualAlpha(activity.SelectedTexture, 1)
        RestoreVisualAlpha(activity.SelectionGlow, 1)
        if activity.UnselectedFrame then activity.UnselectedFrame:SetAlpha(1) end
        if activity.Threshold then activity.Threshold:Show() end
        activity:SetAlpha(1)
        return
    end

    HideVisual(activity.Background)
    HideVisual(activity.Border)
    HideVisual(activity.ItemGlow)
    HideVisual(activity.UncollectedGlow)
    HideVisual(activity.SelectedTexture)
    HideVisual(activity.SelectionGlow)

    if activity.UnselectedFrame then
        activity.UnselectedFrame:SetAlpha(0)
    end
    if HideNativeSelectionDecorations then HideNativeSelectionDecorations(activity) end

    local skin = self:CreateActivitySkin(activity)
    if not skin then
        self:RestoreActivityOriginal(activity)
        return
    end
    self:UpdateActivityVisual(activity, activity:IsMouseOver())

    -- Blizzard may refresh a card mid-reveal; keep scheduled cards concealed.
    if self.pendingOpeningRevealPrivacy then
        activity:SetAlpha(0)
    elseif self.revealActive and self.revealedActivities and not self.revealedActivities[activity] then
        activity:SetAlpha(0)
    elseif not (skin.CardRevealGroup and skin.CardRevealGroup:IsPlaying()) then
        activity:SetAlpha(1)
    end
end

function Skin:StyleRow(rowFrame)
    if not rowFrame then return end
    if not self:IsEnabled() then
        TintVisual(rowFrame.Background, { r = 1, g = 1, b = 1 }, 1)
        TintVisual(rowFrame.Border, { r = 1, g = 1, b = 1 }, 1)
        return
    end

    local theme = self:GetTheme()
    local rowStyle = theme.rowStyle or {}
    local frameBorderColor = theme.frameBorder or theme.border
    local frameAccentColor = theme.frameAccent or theme.accent
    local skin = self:CreateRowSkin(rowFrame)
    local showArtwork = rowStyle.showArtwork ~= false

    if showArtwork then
        TintVisual(rowFrame.Background, theme.artTint or theme.text, rowStyle.artworkAlpha or 0.88)
        TintVisual(rowFrame.Border, frameBorderColor, rowStyle.artworkBorderAlpha or 0.34)
    else
        HideVisual(rowFrame.Background)
        HideVisual(rowFrame.Border)
    end

    ApplyBackdrop(skin.Panel, {
        r = theme.panel.r,
        g = theme.panel.g,
        b = theme.panel.b,
        a = rowStyle.panelAlpha or 0.38,
    }, {
        r = frameBorderColor.r,
        g = frameBorderColor.g,
        b = frameBorderColor.b,
        a = rowStyle.panelBorderAlpha or 0.44,
    }, 1)

    local shadeAlpha = rowStyle.shadeAlpha or 0.34
    SetColor(skin.Shade, {
        r = theme.background.r,
        g = theme.background.g,
        b = theme.background.b,
        a = shadeAlpha,
    })
    skin.Shade:SetAlpha(shadeAlpha)

    skin.Outline:SetBackdrop(GetEdgeOnlyBackdrop(2))
    skin.Outline:SetBackdropColor(0, 0, 0, 0)
    local metal = frameBorderColor
    skin.Outline:SetBackdropBorderColor(metal.r, metal.g, metal.b, rowStyle.outlineAlpha or 0.52)

    SetColor(skin.Accent, frameAccentColor)
    skin.Accent:SetAlpha(rowStyle.accentAlpha or 0.94)
    SetColor(skin.TopLine, metal)
    skin.TopLine:SetAlpha(rowStyle.topLineAlpha or 0.40)
    SetColor(skin.BottomLine, frameBorderColor)
    skin.BottomLine:SetAlpha(rowStyle.bottomLineAlpha or 0.28)

    if rowFrame.Name then
        local rowText = EnsureReadableColor(theme.text, 0.82)
        rowFrame.Name:SetTextColor(rowText.r, rowText.g, rowText.b, 1)
        local font, _, flags = rowFrame.Name:GetFont()
        rowFrame.Name:SetFont(font or STANDARD_TEXT_FONT, 21, flags or "OUTLINE")
    end
end


Shared.HideNativeSelectionDecorations = HideNativeSelectionDecorations
Shared.ProfileSize = ProfileSize
