-- HousingDecorGuide/Core/HDG_Migration.lua
-- Idempotent, version-gated SavedVariables migrations.
--
-- Each migration is a function {key = "vN-name", up = function(state) ... end}.
-- Migrations run in order on Store load, BEFORE the rest of the addon
-- initialises. Each migration sets state.migrations[key] = true so it never
-- runs twice. Migrations should be:
--   • Forward-only: leave a backup (don't blow away the source) until a
--     later release deletes it.
--   • Idempotent: rerunnable if the flag is missing for any reason.
--   • Pure data: no UI, no API calls, no events.
--
-- Adding a migration:
--   1. Append to MIGRATIONS list below (don't reorder — keys are stable
--      identifiers, the runner relies on the array order for sequencing).
--   2. Bump no version anywhere — the per-migration flag IS the version.

local addonName, privateDB = ...
local M = {}

-- ---------------------------------------------------------------------------
-- Migration: v1-collections — unify customStyles + projects.crates under a
-- single state.collections table with type discriminator. Foundation for
-- the shared-Decor-Collection model used by Styles, Crates, and (later)
-- Shipping Crates / Library / Plans.
--
-- After:
--   state.collections["style:<styleId>"] = {id, type="style", name, members, tier, tags, ...}
--   state.collections["crate:<roomID>:<shortID>"] = {id, type="crate", parent=roomID, name, members, ...}
--   state.collections["library:<libID>"] = {id, type="library", name, members, ...}
--
-- Source tables (state.customStyles, state.customStyleOrder, state.projects.crates)
-- are PRESERVED as backup until a later "v2-collections-cleanup" migration
-- deletes them after a release of soak time.
-- ---------------------------------------------------------------------------
local function migrate_v1_collections(state)
    state.collections = state.collections or {}

    if state.customStyles then
        for styleId, style in pairs(state.customStyles) do
            if type(style) == "table" then
                local collID = "style:" .. tostring(styleId)
                if not state.collections[collID] then
                    -- Shallow-copy the style record into the collection slot.
                    -- Keep ALL fields verbatim (members, tier, tags, theme,
                    -- etc.) — the StyleEngine will read whatever it expects.
                    local coll = { id = collID, type = "style" }
                    for k, v in pairs(style) do coll[k] = v end
                    -- Normalise: ensure members is a table (some legacy
                    -- styles may have it nil for empty collections).
                    coll.members = coll.members or {}
                    state.collections[collID] = coll
                end
            end
        end
    end

    if state.projects and state.projects.crates then
        for crateID, crate in pairs(state.projects.crates) do
            if type(crate) == "table" then
                local collID = "crate:" .. tostring(crateID)
                if not state.collections[collID] then
                    local coll = { id = collID, type = "crate", parent = crateID:match("^(house:[^:]+:f%d+:r[^:]+)") }
                    for k, v in pairs(crate) do coll[k] = v end
                    coll.members = coll.members or {}
                    state.collections[collID] = coll
                end
            end
        end
    end
end

-- ---------------------------------------------------------------------------
-- Migration: v2-customstyles-cleanup — delete the legacy state.customStyles
-- and state.customStyleOrder source tables now that all reads/writes flow
-- through state.collections.
--
-- TODO(soak): this migration is staged but inert. After at least one full
-- release of soak time on v1-collections (so any users who skipped the
-- intermediate version still get migrated when they finally update), flip
-- the body to actually nil out the legacy fields and re-add the entry to
-- the MIGRATIONS list. Do NOT enable this without a soak release in
-- between — once the source data is gone, a botched v1 migration is
-- unrecoverable.
-- ---------------------------------------------------------------------------
local function migrate_v2_customstyles_cleanup(state) -- luacheck: no unused
    -- state.customStyles = nil
    -- state.customStyleOrder = nil
end

-- ---------------------------------------------------------------------------
-- Migration: v3-curated-library — seed Vamoose-curated crate templates
-- into state.collections under "lib_curated:<key>" IDs. Player deletes
-- stick: state.curatedSeeded[key]=true blocks re-import on update. New
-- curated keys added in future versions DO seed (they aren't tracked yet).
-- ---------------------------------------------------------------------------
local function migrate_v3_curated_library(state)
    state.collections   = state.collections   or {}
    state.curatedSeeded = state.curatedSeeded or {}

    local lib = privateDB and privateDB.HDG_CrateLibrary
    if not (lib and lib.GetEntries) then return end

    local entries = lib.GetEntries() or {}
    for _, entry in ipairs(entries) do
        local key   = entry.key
        local libID = "lib_curated:" .. tostring(key)
        if key and not state.curatedSeeded[key] and not state.collections[libID] then
            local memberMap = {}
            for _, decorID in ipairs(entry.members or {}) do
                memberMap[decorID] = true
            end
            state.collections[libID] = {
                id        = libID,
                type      = "library",
                name      = entry.name or "(curated template)",
                hint      = entry.hint,
                source    = "curated",
                origin    = entry.origin,
                members   = memberMap,
                createdAt = time(),
            }
            state.curatedSeeded[key] = true
        end
    end
end

local MIGRATIONS = {
    { key = "v1-collections",     up = migrate_v1_collections },
    -- { key = "v2-customstyles-cleanup", up = migrate_v2_customstyles_cleanup },
    { key = "v3-curated-library", up = migrate_v3_curated_library },
}

-- ---------------------------------------------------------------------------
-- Public entry point: HDG.Store:LoadFromSavedVariables() calls this after
-- merging SavedVariables into self.state but before any module init.
-- ---------------------------------------------------------------------------
function M.RunAll(state)
    if type(state) ~= "table" then return end
    state.migrations = state.migrations or {}
    for _, mig in ipairs(MIGRATIONS) do
        if not state.migrations[mig.key] then
            local ok, err = pcall(mig.up, state)
            if ok then
                state.migrations[mig.key] = true
            else
                -- Don't mark failed migrations as done; they'll retry next
                -- load. Print so the user sees the failure.
                if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
                    DEFAULT_CHAT_FRAME:AddMessage(string.format(
                        "|cffff6666[HDG-migration]|r %s failed: %s",
                        mig.key, tostring(err)))
                end
            end
        end
    end
end

-- Expose for tests + Store integration
if privateDB then privateDB.HDG_Migration = M end
return M
