-- VDS_Init
-- ============================================================================
-- Boot lifecycle: event subscriptions, slash command, Modules:Boot. Loads LAST.
-- ActionMeta is in VDS_ActionMeta.lua so tests can load it without these side effects.

VDS = VDS or {}

local A = VDS.Constants.ACTIONS

-- BlizzardEvents engine (invariant §14). BE is eager-init'd at file-load in
-- UI/VDS_BlizzardEvents.lua; frame is live by the time this file (last in .toc) calls Subscribe.

VDS.BlizzardEvents:Subscribe("ADDON_LOADED", {
    mode = "handler",
    handler = function(name)
        if name ~= VDS.Constants.ADDON_NAME then return end
        VDS.Store:ValidateActionMetaExhaustiveness()
        VDS.Store:LoadFromSavedVariables()
        VDS.Log:SetDebug(VDS.Store:GetState().account.config.debug)
        -- Seal WidgetTypes (validates KNOWN_FIELDS + specFields).
        -- Must run BEFORE MainFrame:Build (which calls Layout:Validate + BuildAll).
        VDS.WidgetTypes:Flush()
        VDS.Log:Info("boot", "OnInitialize complete")
    end,
    _event = "ADDON_LOADED",
})

VDS.BlizzardEvents:Subscribe("PLAYER_LOGIN", {
    mode = "handler",
    handler = function()
        VDS.Modules:Boot()
        -- Build main frame at PLAYER_LOGIN so the visibility reconciler is live
        -- for non-slash entry points (Controller_LiveBridge, minimap/compartment).
        VDS.MainFrame:Build()
        VDS.Log:Info("boot", "OnEnable complete")
    end,
    _event = "PLAYER_LOGIN",
})

-- ============================================================================
-- Slash command
-- ============================================================================

_G.SLASH_VAMOOSESDYESTUDIO1 = "/vds"
_G.SLASH_VAMOOSESDYESTUDIO2 = "/dyes"
_G.SlashCmdList["VAMOOSESDYESTUDIO"] = function(msg)
    msg = msg and msg:lower() or ""
    if msg == "" or msg == "toggle" then
        VDS.MainFrame:Toggle()
    elseif msg == "debug" then
        local cfg = VDS.Store:GetState().account.config
        VDS.Store:Dispatch({
            type = A.CONFIG_SET, payload = { key = "debug", value = not cfg.debug },
        })
        VDS.Log:SetDebug(not cfg.debug)
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS]" .. C.reset .. " debug = " .. tostring(not cfg.debug))
    elseif msg == "log" then
        VDS.Log:OpenWindow()
    elseif msg == "forcefail" then
        -- Debug-only: toggle account.config.forceCatalogEmpty. When ON,
        -- DecorInfoResolver:OnSearchResults treats results as empty,
        -- triggering the retry/restart path. Persists via CONFIG_SET so
        -- /reload preserves the flag and the next boot exercises retry
        -- from cold start. Toggle OFF before normal play.
        local cfg = VDS.Store:GetState().account.config
        local nextVal = not cfg.forceCatalogEmpty
        VDS.Store:Dispatch({
            type = A.CONFIG_SET, payload = { key = "forceCatalogEmpty", value = nextVal },
        })
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS]" .. C.reset .. " forceCatalogEmpty = " .. tostring(nextVal)
            .. " (persisted; /reload to test cold-start retry path)")
    elseif msg == "hardreset" then
        VDS.Store:Dispatch({ type = A.HARD_RESET })
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS]" .. C.reset .. " state reset; /reload to take effect")
    elseif msg == "diag" then
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS diag]" .. C.reset)
        print("  Apply runs:    " .. tostring(VDS.BindingEngine._applyCount or 0))
        print("  Dispatch SBL:  " .. tostring(_G._VDS_DISP_COUNT or 0))
        print("  Tooltip enter: " .. tostring(VDS.TooltipEngine._enterCount or 0))
        local cc = VDS.Selectors._callCounts or {}
        local pairs_arr = {}
        for k, v in pairs(cc) do pairs_arr[#pairs_arr + 1] = { k, v } end
        table.sort(pairs_arr, function(a, b) return a[2] > b[2] end)
        print("  Top 10 selectors by call count:")
        for i = 1, math.min(10, #pairs_arr) do
            print(string.format("    %6d  %s", pairs_arr[i][2], pairs_arr[i][1]))
        end
    elseif msg == "decorgap" then
        -- Debug: which DB decorIDs are NOT in this character's live catalog
        -- (session.canvas.dyeableDecor). The DB is a global superset; the catalog
        -- is per-character. Open the Canvas tab once first so the catalog hydrates.
        local C = VDS.Constants.COLOR_CODE
        local state = VDS.Store:GetState()
        local catalog, catCount = {}, 0
        for _, item in ipairs(state.session.canvas.dyeableDecor) do
            catalog[item.decorID] = true
            catCount = catCount + 1
        end
        if catCount == 0 then
            print(C.info .. "[VDS decorgap]" .. C.reset
                .. " catalog not loaded -- open the Canvas tab once, then re-run /vds decorgap.")
        else
            local db = _G.VDS_DecorDyeSlotsDB
            local dbCount, missing = 0, {}
            for decorID in pairs(db) do
                dbCount = dbCount + 1
                if not catalog[decorID] then missing[#missing + 1] = decorID end
            end
            table.sort(missing)
            print(C.info .. "[VDS decorgap]" .. C.reset
                .. (" DB=%d  catalog=%d  in-DB-not-catalog=%d (IDs -> names in VDS_DecorDyeSlotsDB.lua)")
                    :format(dbCount, catCount, #missing))
            for _, decorID in ipairs(missing) do
                print(("    %d  {%s}"):format(decorID, table.concat(db[decorID], ",")))
            end
        end
    elseif msg == "decorprobe" then
        -- Debug: probe each DB-only decor (in the DB, not in this character's
        -- catalog) via the resolver's GetCatalogEntryInfoByRecordID wrapper --
        -- does the API resolve it, and is it customizable (dyeable)? Open the
        -- Canvas tab once first so the catalog has hydrated.
        local C = VDS.Constants.COLOR_CODE
        local state = VDS.Store:GetState()
        local catalog = {}
        for _, item in ipairs(state.session.canvas.dyeableDecor) do
            catalog[item.decorID] = true
        end
        local db = _G.VDS_DecorDyeSlotsDB
        local missing = {}
        for decorID in pairs(db) do
            if not catalog[decorID] then missing[#missing + 1] = decorID end
        end
        table.sort(missing)
        local resolved, customizable = 0, 0
        print(C.info .. "[VDS decorprobe]" .. C.reset .. (" probing %d DB-only decor"):format(#missing))
        for _, decorID in ipairs(missing) do
            local info = VDS.DecorInfoResolver:Resolve(decorID)  -- exception(boundary): nil for a recordID not in the catalog
            if info then
                resolved = resolved + 1
                if info.canCustomize then customizable = customizable + 1 end
                print(("    %d  resolves  dyeable=%s  dbCh=%d  %s"):format(
                    decorID, tostring(info.canCustomize), #db[decorID], info.name or "?"))
            else
                print(("    %d  NOT RESOLVABLE"):format(decorID))
            end
        end
        print(("  resolved=%d/%d  dyeable=%d"):format(resolved, #missing, customizable))
    elseif msg == "alldecor" then
        -- Debug: toggle appending DB-only decor (event-gated, etc.) to the
        -- Canvas picker so they can be previewed + dyed. Tagged "[DB]".
        local cfg = VDS.Store:GetState().account.config
        local nextVal = not cfg.debugAllDecor
        VDS.Store:Dispatch({
            type = A.CONFIG_SET, payload = { key = "debugAllDecor", value = nextVal },
        })
        VDS.DecorInfoResolver:Refresh()  -- re-search -> rebuilds the picker now
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS]" .. C.reset .. " show all DB decor in picker = " .. tostring(nextVal)
            .. " (DB-only items tagged [DB]; search the decor picker to find them)")
    elseif msg == "diagreset" then
        VDS.BindingEngine._applyCount = 0
        _G._VDS_DISP_COUNT = 0
        VDS.TooltipEngine._enterCount = 0
        VDS.Selectors._callCounts = {}
        VDS.Store._dispatchCounts = {}
        print("[VDS] diag counters reset")
    elseif msg == "diag2" then
        local C = VDS.Constants.COLOR_CODE
        print(C.info .. "[VDS diag2] top dispatch actions" .. C.reset)
        local dc = VDS.Store._dispatchCounts or {}
        local arr = {}
        for k, v in pairs(dc) do arr[#arr + 1] = { k, v } end
        table.sort(arr, function(a, b) return a[2] > b[2] end)
        for i = 1, math.min(15, #arr) do
            print(string.format("  %6d  %s", arr[i][2], arr[i][1]))
        end
    else
        local C = VDS.Constants.COLOR_CODE
        print(C.dim .. "Usage: /vds [toggle | debug | hardreset]" .. C.reset)
    end
end
