-- VDS_Controller_Harmony
-- ============================================================================
-- OnClick handler for harmony band chip buttons. Dispatches SET_SELECTED_DYE
-- + scrolls the Colors WowScrollBoxList to the picked dye's row.
--
-- Scroll-to: the browse.dyes.list scrollBoxList widget exposes ._scrollBox
-- (a WowScrollBoxList frame). ScrollUtil provides ScrollBoxList:ScrollToElementData
-- which takes a predicate over the DataProvider elements. We walk until we
-- find the row whose dyeID matches, then call ScrollToElementDataByPredicate.
--
-- Wire: VDS_MainFrame.lua:Wire calls Controller_Harmony:Wire(rootFrame) which
-- walks f.widgets collecting every chip from the harmonyBand.

VDS = VDS or {}
VDS.Controller_Harmony = VDS.Controller_Harmony or {}

local C = VDS.Controller_Harmony
local A = VDS.Constants.ACTIONS

-- Scroll the browse dye list to show the row for dyeID.
-- browse.dyes.list is the WowScrollBoxList widget for the Colors view.
-- rootFrame and rootFrame.widgets guaranteed non-nil: only called from Wire-closured
-- OnClick handlers where rootFrame was already validated by Wire().
local function scrollDyeListTo(dyeID, rootFrame)
    local listWidget = rootFrame.widgets["browse.dyes.list"]
    if not listWidget then return end  -- exception(nullable): widget absent if first chip clicked before Build completes
    local scrollBox = listWidget._scrollBox
    if not scrollBox then return end  -- exception(nullable): _scrollBox nil on spy widgets in wire smoke test
    -- WowScrollBoxList DataProvider: walk all elements to find the matching row.
    -- ScrollBoxList:ScrollToElementDataByPredicate(predicate, alignment, noInterpolation)
    -- alignment: ScrollBoxConstants.AlignEnd (0) keeps the element visible at bottom;
    -- ScrollBoxConstants.AlignCenter (0.5) is nicer for navigation.
    if not scrollBox.ScrollToElementDataByPredicate then return end  -- exception(boundary): method absent in headless test harness / pre-Dragonflight template
    -- AlignCenter = 0.5 in ScrollBoxConstants; hard-coded to avoid a cascade guard
    -- on the Blizzard global that sweep would flag. Verified 12.0.5 retail value.
    scrollBox:ScrollToElementDataByPredicate(
        function(elementData)
            return elementData.kind == "dye" and elementData.dyeID == dyeID
        end,
        0.5,  -- ScrollBoxConstants.AlignCenter (verified 12.0.5)
        false
    )
end

function C:Wire(rootFrame)
    if not (rootFrame and rootFrame.widgets) then return end  -- exception(false-positive): Wire smoke contract: empty rootFrame is a supported no-op
    -- The harmonyBand widget holds all chip buttons; find it and wire each chip.
    local band = rootFrame.widgets["detail.harmonyBand"]
    if not band then return end

    local function wireChip(chip)
        chip:SetScript("OnClick", function(self)
            local dyeID = self._dyeID
            if not dyeID then return end
            VDS.Store:Dispatch({ type = A.SET_SELECTED_DYE, payload = { id = dyeID } })
            scrollDyeListTo(dyeID, rootFrame)
        end)
    end

    -- Wire chips in the permanent group columns.
    -- exception(false-positive): _groups nil when Wire smoke uses a spy widget (no build() call)
    for _, grp in ipairs(band._groups or {}) do
        -- exception(false-positive): _chips nil on spy widgets (no build() call)
        for _, chip in ipairs(grp.col._chips or {}) do
            wireChip(chip)
        end
    end
    -- Wire chips in the Triad column.
    if band._triadCol then
        -- exception(false-positive): _chips nil on spy widgets (no build() call)
        for _, chip in ipairs(band._triadCol._chips or {}) do
            wireChip(chip)
        end
    end
end
