require "MS/SettingPanel/MS_SquareButton"

MS_PanelCollapseHelper = MS_PanelCollapseHelper or {}

local COLLAPSE_ICON = "media/ui/ModernStatus/SettingPanel/Icon_DownArrow.png"
local EXPAND_ICON = "media/ui/ModernStatus/SettingPanel/Icon_RightArrow.png"

local function tr(key, fallback)
    if key and getTextOrNull then
        local value = getTextOrNull(key)
        if value and value ~= key then return value end
    end
    if key and getText then
        local ok, value = pcall(getText, key)
        if ok and value and value ~= key then return value end
    end
    return fallback or tostring(key or "")
end

-- Returns the panel children in a way that works across B42 point releases.
local function getPanelChildren(panel)
    if not panel then return {} end
    return panel.getChildren and panel:getChildren() or panel.children or {}
end

-- Returns true for controls that must remain visible while the panel is collapsed.
local function isTitleChild(panel, child)
    if child == panel.titleLabel or child == panel.closeButton or child == panel.collapseButton then
        return true
    end

    -- Keep cross-panel title buttons available when the panel is collapsed.
    if panel and panel.msTitleBarButtons then
        for _, titleButton in ipairs(panel.msTitleBarButtons) do
            if child == titleButton then
                return true
            end
        end
    end

    return false
end

-- Stores current content visibility so a temporary preview can restore the panel exactly as it was.
local function rememberContentVisibility(panel)
    panel.msChildVisibility = panel.msChildVisibility or {}
    local children = getPanelChildren(panel)
    for _, child in pairs(children) do
        if not isTitleChild(panel, child) then
            panel.msChildVisibility[child] = child:getIsVisible()
        end
    end
end

-- Hides all content children while keeping the title-bar controls available.
local function hideContent(panel)
    local children = getPanelChildren(panel)
    for _, child in pairs(children) do
        if not isTitleChild(panel, child) then
            child:setVisible(false)
        end
    end
end

-- Restores content visibility from the last saved state.
local function restoreContent(panel)
    local children = getPanelChildren(panel)
    for _, child in pairs(children) do
        if not isTitleChild(panel, child) then
            local wasVisible = true
            if panel.msChildVisibility and panel.msChildVisibility[child] ~= nil then
                wasVisible = panel.msChildVisibility[child]
            end
            child:setVisible(wasVisible)
        end
    end
end

local function updateCollapseButton(panel)
    if not panel or not panel.collapseButton then return end
    local collapsed = panel.msCollapsed == true
    panel.collapseButton:setIcon(getTexture(collapsed and EXPAND_ICON or COLLAPSE_ICON))
    panel.collapseButton:setActive(not collapsed)
    panel.collapseButton:setActiveColor(0.95, 0.5, 0.1)
end


local DEFAULT_COLLAPSE_STATE = true

local function getPanelPlayerIndex(panel)
    if not panel then return 0 end
    if panel.playerIndex ~= nil then return panel.playerIndex end
    if panel.indicator and panel.indicator.playerIndex ~= nil then return panel.indicator.playerIndex end
    if panel.parentPanel and panel.parentPanel.playerIndex ~= nil then return panel.parentPanel.playerIndex end
    return 0
end

local function getPanelType(panel)
    return panel and panel.msPanelCollapseType or nil
end

local function getPanelCollapseSettings(playerIndex)
    if not MSConfig or not MSConfig.getGlobalConfig then return {} end
    local globalConfig = MSConfig.getGlobalConfig(playerIndex) or {}
    if type(globalConfig.panelCollapseSettings) ~= "table" then
        return {}
    end
    return globalConfig.panelCollapseSettings
end

local function getSavedCollapsedState(panel)
    local panelType = getPanelType(panel)
    if not panelType then return DEFAULT_COLLAPSE_STATE end
    local playerIndex = getPanelPlayerIndex(panel)
    local settings = getPanelCollapseSettings(playerIndex)
    local entry = settings[panelType]
    if type(entry) == "table" and entry.collapsed ~= nil then
        return entry.collapsed == true
    end
    return DEFAULT_COLLAPSE_STATE
end

local function saveCollapsedState(panel, collapsed)
    local panelType = getPanelType(panel)
    if not panelType or not MSConfig or not MSConfig.updateGlobalConfig then return end
    local playerIndex = getPanelPlayerIndex(panel)
    local settings = {}
    local current = getPanelCollapseSettings(playerIndex)
    for key, value in pairs(current) do
        if type(value) == "table" then
            settings[key] = { collapsed = value.collapsed == true }
        else
            settings[key] = value
        end
    end
    settings[panelType] = { collapsed = collapsed == true }
    MSConfig.updateGlobalConfig(playerIndex, "panelCollapseSettings", settings)
end

-- Stores current child visibility and hides panel content while keeping the title bar usable.
function MS_PanelCollapseHelper.setCollapsed(panel, collapsed)
    if not panel then return end

    if collapsed then
        if panel.msCollapsed == true and panel.msPreviewExpanded ~= true then
            return
        end
        if panel.msCollapsed ~= true then
            panel.msExpandedHeight = panel:getHeight()
            rememberContentVisibility(panel)
        elseif panel.msPreviewExpanded == true then
            rememberContentVisibility(panel)
        end
        hideContent(panel)
        panel:setHeight(panel.titleBarHeight or panel:getHeight())
        panel.msCollapsed = true
        panel.msPreviewExpanded = false
    else
        if panel.msCollapsed ~= true and panel.msPreviewExpanded ~= true then
            return
        end
        panel:setHeight(panel.msExpandedHeight or panel:getHeight())
        restoreContent(panel)
        panel.msCollapsed = false
        panel.msPreviewExpanded = false
        panel.msChildVisibility = nil
    end

    updateCollapseButton(panel)
end

function MS_PanelCollapseHelper.toggle(panel)
    MS_PanelCollapseHelper.setCollapsed(panel, not (panel and panel.msCollapsed == true))
end

function MS_PanelCollapseHelper.onCollapseButtonClick(panel)
    MS_PanelCollapseHelper.toggle(panel)
    saveCollapsedState(panel, panel and panel.msCollapsed == true)
end

-- Temporarily reveals a collapsed panel. The panel remains logically collapsed, so the arrow stays right-facing.
function MS_PanelCollapseHelper.showCollapsedPreview(panel)
    if not panel or panel.msCollapsed ~= true or panel.msPreviewExpanded == true then return end
    panel:setHeight(panel.msExpandedHeight or panel:getHeight())
    restoreContent(panel)
    panel.msPreviewExpanded = true
    updateCollapseButton(panel)
end

-- Returns a previewed collapsed panel to title-bar-only mode.
function MS_PanelCollapseHelper.hideCollapsedPreview(panel)
    if not panel or panel.msCollapsed ~= true or panel.msPreviewExpanded ~= true then return end
    rememberContentVisibility(panel)
    hideContent(panel)
    panel:setHeight(panel.titleBarHeight or panel:getHeight())
    panel.msPreviewExpanded = false
    updateCollapseButton(panel)
end

function MS_PanelCollapseHelper.isPointInTitleBar(panel, mx, my)
    if not panel then return false end
    local ax = panel:getAbsoluteX()
    local ay = panel:getAbsoluteY()
    return mx >= ax and mx <= ax + panel:getWidth() and my >= ay and my <= ay + (panel.titleBarHeight or panel:getHeight())
end

function MS_PanelCollapseHelper.isPointInPanel(panel, mx, my)
    if not panel then return false end
    local ax = panel:getAbsoluteX()
    local ay = panel:getAbsoluteY()
    return mx >= ax and mx <= ax + panel:getWidth() and my >= ay and my <= ay + panel:getHeight()
end

function MS_PanelCollapseHelper.applySavedInitialState(panel)
    if not panel or panel.msCollapseInitialStateApplied == true then return end
    panel.msCollapseInitialStateApplied = true
    local shouldCollapse = getSavedCollapsedState(panel)
    if shouldCollapse then
        -- Start logically collapsed but previewed/open. The right arrow shows that clicking outside will fold the panel.
        MS_PanelCollapseHelper.setCollapsed(panel, true)
        MS_PanelCollapseHelper.showCollapsedPreview(panel)
    else
        MS_PanelCollapseHelper.setCollapsed(panel, false)
    end
end

-- Called from panel prerender. Hovering the title bar temporarily unfolds a collapsed panel.
function MS_PanelCollapseHelper.updateCollapsedPreviewFromMouse(panel)
    MS_PanelCollapseHelper.applySavedInitialState(panel)
    if not panel or panel.msCollapsed ~= true or panel.msPreviewExpanded == true then return end
    if MS_PanelCollapseHelper.isPointInTitleBar(panel, getMouseX(), getMouseY()) then
        MS_PanelCollapseHelper.showCollapsedPreview(panel)
    end
end

-- Called from panel onMouseUpOutside. Clicking outside the preview folds it back to title-bar-only mode.
function MS_PanelCollapseHelper.onMouseUpOutside(panel)
    if panel and panel.msCollapsed == true and panel.msPreviewExpanded == true then
        -- Some child controls route mouse-up to the parent as outside; keep the preview open
        -- while the click is still inside the panel's visible bounds.
        if MS_PanelCollapseHelper.isPointInPanel(panel, getMouseX(), getMouseY()) then
            return false
        end
        MS_PanelCollapseHelper.hideCollapsedPreview(panel)
        return true
    end
    return false
end

-- Panel content is hidden only when it is collapsed and not currently previewed.
function MS_PanelCollapseHelper.isContentHidden(panel)
    return panel and panel.msCollapsed == true and panel.msPreviewExpanded ~= true
end

-- Adds a CleanUI-style title-bar collapse button, using Modern Status local icons.
function MS_PanelCollapseHelper.addTitleBarButton(panel, size, panelType)
    if not panel or panel.collapseButton then return end
    panel.msPanelCollapseType = panelType or panel.msPanelCollapseType

    local buttonSize = size or FONT_HGT_SMALL or 14
    local buttonY = ((panel.titleBarHeight or buttonSize) - buttonSize) / 2
    panel.collapseButton = MS_SquareButton:new(panel.padding or 0, buttonY, buttonSize, getTexture(EXPAND_ICON), panel, MS_PanelCollapseHelper.onCollapseButtonClick)
    panel.collapseButton:initialise()
    if panel.collapseButton.setTooltip then
        panel.collapseButton:setTooltip(tr("IGUI_ModernStatus_Tooltip_CollapsePreviewButton", "Toggle whether this panel stays open or folds back to the title bar when clicking outside."))
    end
    panel.collapseButton:setActive(true)
    panel.collapseButton:setActiveColor(0.95, 0.5, 0.1)
    panel.collapseButton.prerender = function(button)
        updateCollapseButton(panel)
    end
    panel:addChild(panel.collapseButton)
end

-- Offsets the title label so it does not overlap the collapse button.
function MS_PanelCollapseHelper.getTitleLabelX(panel, buttonSize)
    return (panel.padding or 0) + (buttonSize or FONT_HGT_SMALL or 14) + (panel.padding or 0)
end

return MS_PanelCollapseHelper
