-- HDG_HT_W_DecoratorProfile.lua
-- Composite hero panel with Blizzard's housing-dashboard level ring on the
-- left (laurel + radial sweep, NATIVE SIZES — do not scale, the swipe texture
-- and laurel leaves overlay pixel-perfect at Blizzard's atlas dimensions),
-- title + tier ladder + bestowed + style affinity stacked on the top-right,
-- and trophy shelf strip below them.
--
-- Atlas + size source: Blizzard_HousingDashboardHouseUpgrade.xml
--   CurrentLevelFrame   256×158   container
--   HouseBarFrame       128×128   centered, hosts laurel + sweep
--   Cooldown sweep      130×130   reverse=true, rotation=180, offset (8,-8)
--                                  swipe = Interface\Housing\house-upgrade-radial-bar-fill
--   windowframe         native    house-upgrade-radial-windowframe (useAtlasSize)
--   leaves 1-6          native    house-upgrade-radial-leaf01..leaf06 (useAtlasSize)
--   Level number        Game48Font  CENTER (6, -10) inside container
--   BAR_PERCENTAGE_COVERED = 0.22  bottom of ring is gap; visible band = 0.78
--   finalPct = basePct * 0.78 + 0.11   maps XP fraction onto visible band

local _, privateDB = ...
privateDB.HDG_HT = privateDB.HDG_HT or {}
privateDB.HDG_HT.Widgets = privateDB.HDG_HT.Widgets or {}
local W = { id = "decoratorProfile", title = "Decorator Profile" }
privateDB.HDG_HT.Widgets.DecoratorProfile = W

-- Ring is instantiated from HDGHousingLevelRingTemplate (HDG_HT_LevelRing.xml),
-- which is a verbatim port of Blizzard's CurrentLevelFrame from
-- Blizzard_HousingDashboardHouseUpgrade.xml. Sizes/anchors are XML-defined,
-- so DO NOT scale or anchor-override the resulting frame.
local RING_W, RING_H         = 256, 158
local BAR_PERCENTAGE_COVERED = 0.22
local BAR_VISIBLE_BAND       = 1 - BAR_PERCENTAGE_COVERED   -- 0.78
local BAR_HALF_GAP           = BAR_PERCENTAGE_COVERED / 2   -- 0.11

-- Wrap a HDGHousingLevelRingTemplate frame with the methods we need. Ports
-- HouseUpgradeProgressBarMixin's UpdateFill so the sweep animates from the
-- previous percentage to the new one (1s, InOutQuartic easing) and the gold
-- flipbook sparkle plays at the leading edge during the fill.
local BAR_ANIM_TIME = 1.0
local function AdoptLevelRing(ringFrame)
    local cd = ringFrame.HouseBarFrame and ringFrame.HouseBarFrame.Bar
        and ringFrame.HouseBarFrame.Bar.BarFill
    local lead     = cd and cd.LeadEdge
    local thresh   = cd and cd.Threshold
    local flipbook = cd and cd.Flipbook

    -- Initial state matches Blizzard's OnLoad: sit at the half-gap so the
    -- ring starts visually empty (the "covered" bottom band is centered).
    ringFrame.currentPercentage = BAR_HALF_GAP
    ringFrame.targetPercentage  = BAR_HALF_GAP

    local function applyDisplayPct(finalPct)
        if cd and CooldownFrame_SetDisplayAsPercentage then
            CooldownFrame_SetDisplayAsPercentage(cd, finalPct)
        end
        local rot = (0.5 - finalPct) * 2 * math.pi
        if lead     then lead:SetRotation(rot)     end
        if thresh   then thresh:SetRotation(rot)   end
        if flipbook then flipbook:SetRotation(rot) end
    end

    function ringFrame:SetLevel(lvl)
        if self.HouseLevelText then
            self.HouseLevelText:SetText(tostring(lvl or 0))
        end
    end

    -- Cancel any in-flight animation and snap to a new percentage immediately.
    function ringFrame:StopCurrentAnimation()
        if self._cancelAnim then
            self._cancelAnim()
            self._cancelAnim = nil
        end
    end

    -- Drive the sweep animation from currentPercentage → target (final, gap-
    -- adjusted display percentage in [BAR_HALF_GAP, 1 - BAR_HALF_GAP]).
    -- Mirrors HouseUpgradeProgressBarMixin:UpdateFill.
    function ringFrame:UpdateFill()
        local startingPct = self.currentPercentage
        local endingPct   = self.targetPercentage
        if startingPct == endingPct then
            self:StopCurrentAnimation()
            return
        end

        local function UpdateBar(elapsed, duration)
            local t = elapsed / duration
            local easeFn = (EasingUtil and EasingUtil.InOutQuartic)
                        or function(x) return x end
            local newPct = math.min(startingPct + easeFn(t) * (endingPct - startingPct), 1.0)
            applyDisplayPct(newPct)
            self.currentPercentage = newPct
        end

        self:StopCurrentAnimation()
        if ScriptAnimationUtil and ScriptAnimationUtil.StartScriptAnimation then
            self._cancelAnim = ScriptAnimationUtil.StartScriptAnimation(
                self, UpdateBar, BAR_ANIM_TIME,
                function() self._cancelAnim = nil end)
            if cd and cd.BarAnimation and cd.BarAnimation.Restart then
                cd.BarAnimation:Restart()
            end
        else
            -- No animation utils? Snap to target.
            applyDisplayPct(endingPct)
            self.currentPercentage = endingPct
        end
    end

    -- Public: call from Refresh with a 0..1 base XP fraction. The first
    -- call after frame creation animates from empty (BAR_HALF_GAP) up to
    -- the player's current XP — that's the "open the dashboard" sweep.
    function ringFrame:SetProgress(basePct)
        basePct = math.max(0, math.min(1, basePct or 0))
        self.targetPercentage = basePct * BAR_VISIBLE_BAND + BAR_HALF_GAP
        self:UpdateFill()
    end

    -- Hooked from the widget's parent OnHide/OnShow so the animation
    -- restarts each time the dashboard is opened (the player closing and
    -- reopening should re-trigger the sweep, not pick up mid-animation).
    function ringFrame:ResetForReplay()
        self:StopCurrentAnimation()
        self.currentPercentage = BAR_HALF_GAP
        applyDisplayPct(BAR_HALF_GAP)
    end

    return ringFrame
end

function W.Create(parent, dims)
    local self = { dims = dims }
    self.frame = CreateFrame("Frame", nil, parent)
    self.frame:SetAllPoints(parent)

    local Theme = privateDB.HDG_HT.Theme
    local scheme = Theme.GetCurrentScheme()
    local applyFont = Theme.ApplyFont

    -- Left zone — instantiate Blizzard's exact ring template at its native
    -- 256×158 footprint. No SetSize / no manual anchoring of children — the
    -- XML-defined sweep + leaves + windowframe stay pixel-perfect.
    self.ring = AdoptLevelRing(CreateFrame("Frame", nil, self.frame, "HDGHousingLevelRingTemplate"))
    self.ring:SetPoint("LEFT", self.frame, "LEFT", 0, 0)
    self.ringZone = self.ring  -- alias for the right zone's anchor target

    -- Replay the sweep animation each time the composite is shown. The next
    -- Refresh() call sets targetPercentage and animates from the empty state
    -- back up to the player's current XP.
    self.frame:HookScript("OnShow", function()
        if self.ring and self.ring.ResetForReplay then self.ring:ResetForReplay() end
    end)

    -- Right zone — fills the remainder. Width tracks composite via LEFT/RIGHT
    -- anchors; ringZone is fixed-width so right zone gets `width - RING_W`.
    self.rightZone = CreateFrame("Frame", nil, self.frame)
    self.rightZone:SetPoint("TOPLEFT",     self.ringZone, "TOPRIGHT",   8, 0)
    self.rightZone:SetPoint("BOTTOMRIGHT", self.frame,    "BOTTOMRIGHT", 0, 0)

    -- Top sub-zone of right column: title + chip + tier ladder + bestowed +
    -- style affinity (one stack of font strings).
    self.titleZone = CreateFrame("Frame", nil, self.rightZone)
    self.titleZone:SetPoint("TOPLEFT",  self.rightZone, "TOPLEFT",  0, 0)
    self.titleZone:SetPoint("TOPRIGHT", self.rightZone, "TOPRIGHT", 0, 0)
    self.titleZone:SetHeight(80)

    -- Big uppercase title, accent colour
    self.titleLabel = self.titleZone:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
    self.titleLabel:SetPoint("TOPLEFT", self.titleZone, "TOPLEFT", 4, -2)
    self.titleLabel:SetTextColor(scheme.accent.r, scheme.accent.g, scheme.accent.b)
    self.titleLabel:SetShadowColor(0, 0, 0, 0.7)
    self.titleLabel:SetShadowOffset(1, -1)
    applyFont(self.titleLabel, scheme, "header", 18)

    -- Current-house name on the GRAND CURATOR title line, right-aligned.
    -- Sourced from the active neighborhood (HDG_DB.account.houses cache,
    -- matched against C_NeighborhoodInitiative.GetActiveNeighborhood).
    self.houseNameLabel = self.titleZone:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    self.houseNameLabel:SetPoint("TOPRIGHT", self.titleZone, "TOPRIGHT", -4, -2)
    self.houseNameLabel:SetJustifyH("RIGHT")
    self.houseNameLabel:SetWordWrap(false)
    applyFont(self.houseNameLabel, scheme, "header", 14)

    -- "X / Y (Z%)" progress chip on title row (original position — sits to
    -- the right of titleZone, slightly above the title baseline).
    self.progressChip = self.titleZone:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    self.progressChip:SetPoint("RIGHT", self.titleZone, "RIGHT", -4, 8)
    self.progressChip:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b)
    applyFont(self.progressChip, scheme, "small")

    -- Mini progress bar under title (within-tier band)
    self.bar = CreateFrame("StatusBar", nil, self.titleZone)
    self.bar:SetHeight(6)
    self.bar:SetPoint("TOPLEFT", self.titleLabel, "BOTTOMLEFT", 0, -2)
    self.bar:SetPoint("RIGHT",   self.titleZone, "RIGHT", -4, 0)
    self.bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
    self.bar:SetStatusBarColor(scheme.success.r, scheme.success.g, scheme.success.b, 1)
    self.bar.bg = self.bar:CreateTexture(nil, "BACKGROUND")
    self.bar.bg:SetAllPoints()
    self.bar.bg:SetTexture("Interface\\Buttons\\WHITE8x8")
    self.bar.bg:SetVertexColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, 0.85)

    -- Tier ladder line (prev > current > next)
    self.progressionLabel = self.titleZone:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    self.progressionLabel:SetPoint("TOPLEFT", self.titleLabel, "BOTTOMLEFT", 0, -11)
    self.progressionLabel:SetPoint("RIGHT", self.titleZone, "RIGHT", -4, 0)
    self.progressionLabel:SetJustifyH("LEFT")
    self.progressionLabel:SetWordWrap(false)
    applyFont(self.progressionLabel, scheme, "small", 13)

    -- Bestowed title + Vamoose quote line. Style affinity lives in its own
    -- card (see styleAffinity in LayoutConfig); not rendered here.
    self.bestowedLine = self.titleZone:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    self.bestowedLine:SetPoint("TOPLEFT", self.progressionLabel, "BOTTOMLEFT", 0, -14)
    self.bestowedLine:SetPoint("RIGHT", self.titleZone, "RIGHT", -4, 0)
    self.bestowedLine:SetJustifyH("LEFT")
    self.bestowedLine:SetWordWrap(false)
    applyFont(self.bestowedLine, scheme, "small", 13)

    -- Bottom sub-zone of right column: trophy shelf strip (host the existing
    -- TrophyShelf widget so we get the wood plank + scrollable icon row).
    self.trophyZone = CreateFrame("Frame", nil, self.rightZone)
    self.trophyZone:SetPoint("TOPLEFT",     self.titleZone, "BOTTOMLEFT",  0, -8)
    self.trophyZone:SetPoint("TOPRIGHT",    self.titleZone, "BOTTOMRIGHT", 0, -8)
    self.trophyZone:SetPoint("BOTTOMLEFT",  self.rightZone, "BOTTOMLEFT",  0, 0)
    self.trophyZone:SetPoint("BOTTOMRIGHT", self.rightZone, "BOTTOMRIGHT", 0, 0)

    local Widgets = privateDB.HDG_HT.Widgets
    if Widgets.TrophyShelf then
        self.trophy = Widgets.TrophyShelf.Create(self.trophyZone, dims)
    end

    -- Resolve the active house's display name from the MultiHouse cache
    -- (HDG_DB.account.houses, keyed by houseGUID). Tinted by faction.
    local function RefreshHouseNameLabel()
        if not self.houseNameLabel then return end
        local cache = HDG_DB and HDG_DB.account and HDG_DB.account.houses
        local activeGUID
        if C_NeighborhoodInitiative and C_NeighborhoodInitiative.GetActiveNeighborhood then
            activeGUID = C_NeighborhoodInitiative.GetActiveNeighborhood()
        end
        local activeHouse
        if cache then
            for _, h in pairs(cache) do
                if activeGUID and h.neighborhoodGUID == activeGUID then
                    activeHouse = h
                    break
                end
            end
            if not activeHouse then
                for _, h in pairs(cache) do activeHouse = h; break end  -- fall back to any
            end
        end
        if activeHouse and activeHouse.neighborhoodName then
            self.houseNameLabel:SetText(activeHouse.neighborhoodName)
            if activeHouse.faction == "Alliance" then
                self.houseNameLabel:SetTextColor(0.46, 0.71, 0.96)
            elseif activeHouse.faction == "Horde" then
                self.houseNameLabel:SetTextColor(0.96, 0.46, 0.46)
            else
                self.houseNameLabel:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b)
            end
            self.houseNameLabel:Show()
        else
            self.houseNameLabel:Hide()
        end
    end

    -- ----- Refresh -----
    function self:Refresh(snap)
        RefreshHouseNameLabel()

        local cAccent, cDim, cText = Theme.C_Accent(), Theme.C_Dim(), Theme.C_Text()

        -- Level ring: drive sweep + level number from cached house data
        local UI = _G.HDG_UI
        local hd = UI and UI.GetCachedHouseData and UI.GetCachedHouseData()
        if hd then
            self.ring:SetLevel(hd.level or 0)
            local thresholds = hd.thresholds or {}
            local cur = thresholds[hd.level] or 0
            local nxt = thresholds[(hd.level or 0) + 1] or thresholds[#thresholds] or cur
            local span = math.max(1, nxt - cur)
            local within = math.max(0, math.min(span, (hd.xp or 0) - cur))
            self.ring:SetProgress(within / span)
        else
            self.ring:SetLevel(0)
            self.ring:SetProgress(0)
        end

        -- Title block
        local title = (snap and snap.title) or "Decorator"
        self.titleLabel:SetText(title:upper())

        local t = snap and snap.titleTier
        local collected = (snap and snap.collectedAll) or 0
        local totalAll  = (snap and snap.totalAll) or 0

        -- Progress chip — overall collection (collected / total / pct of total).
        if totalAll > 0 then
            local pct = math.floor(collected / totalAll * 100 + 0.5)
            self.progressChip:SetText(string.format("%s%s|r %s/ %s|r  %s(%d%%)|r",
                cText, BreakUpLargeNumbers(collected), cDim,
                BreakUpLargeNumbers(totalAll), cDim, pct))
        else
            self.progressChip:SetText("")
        end

        -- Mini progress bar — keeps tier-band semantics (within current rank).
        if t and t.next then
            local target = t.next.threshold or 1
            local prev   = (t.current and t.current.threshold) or 0
            local span   = math.max(1, target - prev)
            local within = math.max(0, math.min(span, collected - prev))
            self.bar:SetMinMaxValues(0, span)
            self.bar:SetValue(within)
            self.bar:Show()
        else
            self.bar:Hide()
        end

        -- Tier ladder — without the * Vamoose original footnote (now its own row).
        if t then
            local parts = {}
            if t.prev then parts[#parts + 1] = cDim .. (t.prev.name or "") .. "|r" end
            parts[#parts + 1] = cText .. (t.current.name or "") .. "|r"
            if t.next then parts[#parts + 1] = cDim .. (t.next.name or "") .. "|r" end
            local line = table.concat(parts, "  " .. cDim .. ">|r  ")
            if not t.next then line = line .. "   " .. cAccent .. "max rank|r" end
            self.progressionLabel:SetText(line)
        else
            self.progressionLabel:SetText("")
        end

        -- Bestowed title rotates daily — disconnected from collection stats.
        -- HDG_Vamoose persists today's choice to HDG_DB.daily.bestowed.
        local Vam = privateDB.HDG_Vamoose
        if Vam and Vam.GetDailyTitle then
            local b = Vam.GetDailyTitle()
            if b and b.name then
                self.bestowedLine:SetText(string.format("%s%s|r  %s%s|r",
                    cAccent, b.name, cDim, b.quote or ""))
            else
                self.bestowedLine:SetText("")
            end
        else
            self.bestowedLine:SetText(snap and snap.persona or "")
        end

        -- Trophy shelf
        if self.trophy then self.trophy:Refresh(snap) end
    end

    function self:Resize(w, h)
        -- Ring is fixed-size so leaves+sweep stay pixel-perfect; right zone
        -- tracks composite width via LEFT/RIGHT anchors. Trophy shelf only
        -- needs Resize forwarded (its scroll content recalcs on parent size).
        if self.trophy and self.trophy.Resize then
            self.trophy:Resize(w - RING_W - 8, h - 80 - 4)
        end
    end

    return self
end

return W
