local addonName, NS = ...

-- ============================================================================
-- VAULTSKINS: LAYOUT MANAGEMENT
-- ============================================================================

local rawTimerAfter = C_Timer and C_Timer.After
local function ScheduleTask(key, delay, callback)
    if NS.Stability and NS.Stability.Schedule then
        return NS.Stability:Schedule(key, delay, callback)
    end
    if rawTimerAfter then return rawTimerAfter(math.max(0, tonumber(delay) or 0), callback) end
    return callback()
end

local Layout = {}
NS.Layout = Layout

-- ============================= LAYOUT PROFILES ==============================
local ROW_KEYS = { "RaidFrame", "MythicFrame", "PVPFrame", "WorldFrame" }

local PROFILES = {
    classic = {
        label = "Classic",
        description = "The familiar Great Vault spacing with full category artwork.",
        scale = 1.00,
        activityOffsetX = 0,
        rowMode = "full",
        titleSize = 27,
        scene = true,
        artwork = true,
        controlInsetX = 48,
        controlOffsetY = -54,
    },
    wide = {
        label = "Wide",
        description = "More room for rewards with narrower category artwork and a wider visual grid.",
        scale = 1.03,
        activityOffsetX = -72,
        rowWidth = 286,
        rowMode = "full",
        titleSize = 27,
        scene = true,
        artwork = true,
        controlInsetX = 54,
        controlOffsetY = -54,
    },
    cinematic = {
        label = "Cinematic",
        description = "Larger presentation, stronger artwork, and more visual breathing room.",
        scale = 1.08,
        activityOffsetX = 12,
        rowMode = "full",
        titleSize = 30,
        scene = true,
        artwork = true,
        sceneBoost = 1.12,
        controlInsetX = 60,
        controlOffsetY = -58,
    },
}

NS.LayoutProfiles = PROFILES
NS.LayoutOrder = { "wide", "classic", "cinematic" }

-- =========================== REGION STATE HELPERS ===========================
local function SafeShown(region, shown)
    if not region then return end
    if shown then region:Show() else region:Hide() end
end

local function CaptureRegion(region, parent)
    if not region or not parent or not region.GetLeft or not parent.GetLeft then return nil end
    local left, top = region:GetLeft(), region:GetTop()
    local parentLeft, parentTop = parent:GetLeft(), parent:GetTop()
    if not left or not top or not parentLeft or not parentTop then return nil end
    return {
        x = left - parentLeft,
        y = top - parentTop,
        width = region:GetWidth(),
        height = region:GetHeight(),
        scale = region:GetScale(),
    }
end

local function RestoreRegion(region, parent, state)
    if not region or not parent or not state then return end
    region:SetScale(state.scale or 1)
    if state.width and state.height and state.width > 0 and state.height > 0 and region.SetSize then
        region:SetSize(state.width, state.height)
    end
    region:ClearAllPoints()
    region:SetPoint("TOPLEFT", parent, "TOPLEFT", state.x or 0, state.y or 0)
end

local function SetCollectionShown(collection, shown)
    if not collection then return end
    if collection[1] and collection[1].SetAlpha then
        for _, region in ipairs(collection) do SafeShown(region, shown) end
    else
        for _, nested in ipairs(collection) do
            for _, region in ipairs(nested) do SafeShown(region, shown) end
        end
    end
end

-- =============================== PROFILE API ================================
function Layout:GetProfile(style)
    style = style or (VaultSkinsDB and VaultSkinsDB.layoutStyle) or "classic"
    return PROFILES[style] or PROFILES.classic
end

function Layout:GetScaleMultiplier()
    return self:GetProfile().scale or 1
end

-- ======================== STATE CAPTURE AND RESTORE =========================
function Layout:Capture(frame)
    if not frame then return false end
    if not frame:GetLeft() or not frame:GetTop() then return false end

    if self.frame ~= frame or not self.original then
        self.frame = frame
        self.original = { rows = {}, activities = {}, names = {} }
    end
    local original = self.original

    for _, key in ipairs(ROW_KEYS) do
        local row = frame[key]
        if row then
            if not original.rows[key] then original.rows[key] = CaptureRegion(row, frame) end
            if row.Name and not original.names[key] then original.names[key] = CaptureRegion(row.Name, frame) end
        end
    end
    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            if not original.activities[activity] then
                local state = CaptureRegion(activity, frame)
                if state then original.activities[activity] = state end
            end
        end
    end
    if not original.headerFont and frame.HeaderFrame and frame.HeaderFrame.Text then
        local font, size, flags = frame.HeaderFrame.Text:GetFont()
        original.headerFont = { font = font, size = size, flags = flags }
    end

    return next(original.activities) ~= nil or next(original.rows) ~= nil
end

function Layout:Restore(frame)
    frame = frame or self.frame
    if not frame or not self.original then return false end
    for _, key in ipairs(ROW_KEYS) do
        local row = frame[key]
        if row then
            RestoreRegion(row, frame, self.original.rows[key])
            if row.Name then RestoreRegion(row.Name, frame, self.original.names[key]) end
            SafeShown(row.Background, true)
            SafeShown(row.Border, true)
            if row.VaultSkins then
                for _, part in ipairs({ "Panel", "Shade", "Outline", "Accent", "TopLine", "BottomLine" }) do
                    SafeShown(row.VaultSkins[part], true)
                end
            end
        end
    end
    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            RestoreRegion(activity, frame, self.original.activities[activity])
        end
    end
    return true
end

-- ============================= VISUAL OVERRIDES =============================
function Layout:ApplyVisualOverrides(frame, profile)
    if not frame then return end
    profile = profile or self:GetProfile()
    local main = frame.VaultSkins

    local showArtwork = profile.artwork ~= false
    if main then
        if main.AssetArtwork then
            SafeShown(main.AssetArtwork.Crest, showArtwork)
            SafeShown(main.AssetArtwork.Header, showArtwork)
            SafeShown(main.AssetArtwork.Watermark, showArtwork)
            for _, region in ipairs(main.AssetArtwork.Corners or {}) do SafeShown(region, showArtwork) end
        end
        if main.ThemeArtwork then
            for _, collection in pairs(main.ThemeArtwork) do SetCollectionShown(collection, showArtwork) end
        end
        if main.CenterMist then main.CenterMist:SetShown(profile.rowMode ~= "minimal") end
    end

    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            local skin = activity and activity.VaultSkins
            if skin then
                local showCardArtwork = profile.rowMode == "full"
                if skin.AssetCardCorners then
                    for _, region in ipairs(skin.AssetCardCorners) do SafeShown(region, showCardArtwork) end
                end
                if skin.ThemeCardArtwork then
                    for _, region in ipairs(skin.ThemeCardArtwork) do SafeShown(region, false) end
                end
            end
        end
    end

    if NS.Skin then
        if profile.scene == false then
            if NS.Skin.StopBackgroundScene then NS.Skin:StopBackgroundScene(true, true) end
            if main and main.SceneLayer then main.SceneLayer:Hide() end
        elseif main and main.SceneLayer then
            main.SceneLayer:Show()
            if NS.Skin.ApplyBackgroundScene then
                NS.Skin:ApplyBackgroundScene(NS.Skin:GetTheme(), false)
                if profile.sceneBoost and main.BackgroundScene then
                    local boost = profile.sceneBoost
                    for _, key in ipairs({ "MistA", "MistB", "Rays", "SymbolA", "SymbolB" }) do
                        local region = main.BackgroundScene[key]
                        if region then region:SetAlpha(math.min(1, region:GetAlpha() * boost)) end
                    end
                end
            end
        end
    end
end

-- =========================== CONTROL POSITIONING ============================
function Layout:ApplySafeControlPositions(frame, profile)
    if not frame then return end
    profile = profile or self:GetProfile()
    local insetX = profile.controlInsetX or 48
    local offsetY = profile.controlOffsetY or -54

    local themeButton = frame.VaultSkinsThemeButton
    if themeButton then
        themeButton:ClearAllPoints()
        themeButton:SetPoint("TOPLEFT", frame, "TOPLEFT", insetX, offsetY)
    end

end

-- ============================ LAYOUT APPLICATION ============================
function Layout:ApplyCurrent(frame)
    frame = frame or WeeklyRewardsFrame
    if not frame or not frame:IsShown() then return false end
    if not self:Capture(frame) then
        ScheduleTask("layout-deferred-apply", 0, function()
            if WeeklyRewardsFrame and WeeklyRewardsFrame:IsShown() then Layout:ApplyCurrent(WeeklyRewardsFrame) end
        end)
        return false
    end

    self:Restore(frame)
    local profile = self:GetProfile()

    for _, key in ipairs(ROW_KEYS) do
        local row = frame[key]
        local state = self.original.rows[key]
        if row and state then
            if profile.rowWidth then row:SetWidth(profile.rowWidth) end
            if row.Name then
                local labelInset = profile.rowMode == "full" and 26 or 16
                row.Name:ClearAllPoints()
                row.Name:SetPoint("LEFT", row, "LEFT", labelInset, 0)
                row.Name:SetWidth(math.max(96, row:GetWidth() - labelInset - 18))
                if row.Name.SetWordWrap then row.Name:SetWordWrap(false) end
                if row.Name.SetMaxLines then row.Name:SetMaxLines(1) end
                if row.Name.SetJustifyH then row.Name:SetJustifyH("LEFT") end
                local font, _, flags = row.Name:GetFont()
                local labelSize = profile.rowMode == "full" and 21 or (profile.rowMode == "compact" and 18 or 17)
                row.Name:SetFont(font or STANDARD_TEXT_FONT, labelSize, flags or "OUTLINE")
            end

            if profile.rowMode == "compact" then
                SafeShown(row.Background, false)
                SafeShown(row.Border, false)
                if row.VaultSkins then
                    SafeShown(row.VaultSkins.Panel, true)
                    SafeShown(row.VaultSkins.Shade, true)
                    SafeShown(row.VaultSkins.Outline, true)
                    SafeShown(row.VaultSkins.Accent, true)
                    SafeShown(row.VaultSkins.TopLine, false)
                    SafeShown(row.VaultSkins.BottomLine, false)
                end
            elseif profile.rowMode == "minimal" then
                SafeShown(row.Background, false)
                SafeShown(row.Border, false)
                if row.VaultSkins then
                    SafeShown(row.VaultSkins.Panel, false)
                    SafeShown(row.VaultSkins.Shade, false)
                    SafeShown(row.VaultSkins.Outline, false)
                    SafeShown(row.VaultSkins.Accent, true)
                    SafeShown(row.VaultSkins.TopLine, false)
                    SafeShown(row.VaultSkins.BottomLine, false)
                end
            end
        end
    end

    if frame.Activities then
        for _, activity in ipairs(frame.Activities) do
            local state = self.original.activities[activity]
            if state then
                activity:ClearAllPoints()
                activity:SetPoint("TOPLEFT", frame, "TOPLEFT", state.x + (profile.activityOffsetX or 0), state.y)
            end
        end
    end

    if frame.HeaderFrame and frame.HeaderFrame.Text then
        local originalFont = self.original.headerFont or {}
        frame.HeaderFrame.Text:SetFont(originalFont.font or STANDARD_TEXT_FONT, profile.titleSize or originalFont.size or 27, originalFont.flags or "OUTLINE")
    end

    self:ApplyVisualOverrides(frame, profile)
    self:ApplySafeControlPositions(frame, profile)
    self.activeStyle = VaultSkinsDB and VaultSkinsDB.layoutStyle or "classic"
    if NS.Skin and NS.Skin.ApplyScale then NS.Skin:ApplyScale() end
    return true
end

-- ============================ PUBLIC LAYOUT API =============================
function Layout:SetStyle(style)
    if not PROFILES[style] then return false end
    VaultSkinsDB.layoutStyle = style
    if WeeklyRewardsFrame and WeeklyRewardsFrame:IsShown() then
        if NS.Skin then NS.Skin:RequestThemeApply("layout style", 0) else self:ApplyCurrent(WeeklyRewardsFrame) end
    end
    if NS.Options then NS.Options:Refresh() end
    return true
end

function Layout:GetDiagnostics()
    local style = VaultSkinsDB and VaultSkinsDB.layoutStyle or "classic"
    local profile = self:GetProfile(style)
    return { id = style, label = profile.label, scale = profile.scale, rowMode = profile.rowMode }
end
