local _, ns = ...
local BDT = ns.DataTexts
local L = BDT.L
BDT.BarManager = {}

----------------------------------------------------
-- Apply Lock State
----------------------------------------------------
function BDT.BarManager:ApplyLockState()
    for _, bar in pairs(BDT.bars) do
        if bar then
            if BDT.db.profile.locked then
                -- Keep mouse disabled on bar frame (can't drag) but slots stay interactive
                bar:EnableMouse(false)
                bar:SetMovable(false)
                -- Re-enable mouse on slots so tooltips work
                if bar.slots then
                    for _, slot in ipairs(bar.slots) do
                        if slot then slot:EnableMouse(true) end
                    end
                end
            else
                bar:EnableMouse(true)
                bar:SetMovable(true)
            end
            self:ApplyVisibility(bar)
        end
    end
end

----------------------------------------------------
-- Apply Visibility
----------------------------------------------------
function BDT.BarManager:ApplyVisibility(bar)
    if not bar then return end
    local barName = bar:GetName()
    local saved = BDT.db.profile.bars[barName]
    if not saved then return end

    -- Always show bars when unlocked (edit mode) so users can manage hidden bars
    if not BDT.db.profile.locked then
        bar:Show()
        return
    end

    -- Global toggle takes priority: if all panels are hidden, hide regardless of per-bar setting
    if not BDT.db.profile.showPanels then
        bar:Hide()
        return
    end

    -- Per-bar hidden flag
    if saved.hidden then
        bar:Hide()
    else
        bar:Show()
    end
end

----------------------------------------------------
-- Apply Visibility to All Bars
----------------------------------------------------
function BDT.BarManager:ApplyVisibilityAll()
    for _, bar in pairs(BDT.bars) do
        self:ApplyVisibility(bar)
    end
end

----------------------------------------------------
-- Create Data Bar
----------------------------------------------------
function BDT.BarManager:CreateDataBar(id, numSlots)
    local name = "BDT_Bar" .. id
    if not BDT.db.profile.bars[name] then
        BDT.db.profile.bars[name] = {
            numSlots = numSlots or 3,
            slots = {},
            bgOpacity = 50,
            borderName = "None",
            border = "None",
            width = 300,
            height = 22,
            name = name
        }
    end
    
    local saved = BDT.db.profile.bars[name]
    local bar = BDT.BarManager:CreateMovableFrame(name)
    BDT.bars[name] = bar

    bar:SetSize(300, 22)
    if saved.point then
        bar:SetPoint(saved.point.point, UIParent, saved.point.relativePoint, saved.point.x, saved.point.y)
    else
        bar:SetPoint("CENTER")
    end

    local scale = saved.scale or 100
    bar:SetScale(scale / 100)

    -- Always keep data text bars at the lowest strata
    bar:SetFrameStrata("BACKGROUND")

    function bar:ApplyBackground()
        local hasBackground = saved.bgOpacity and saved.bgOpacity > 0
        local hasBorder = saved.borderName and saved.borderName ~= "None"

        if hasBackground or hasBorder then
            bar:SetBackdrop({
                bgFile = "Interface/Tooltips/UI-Tooltip-Background",
                edgeFile = hasBorder and saved.border or nil,
                edgeSize = hasBorder and saved.borderSize or 0
            })
            local alpha = (saved.bgOpacity or 50) / 100
            bar:SetBackdropColor(0, 0, 0, alpha)
            if saved.borderColor then
                local color = saved.borderColor:gsub("#", "")
                local r = tonumber(color:sub(1, 2), 16) / 255
                local g = tonumber(color:sub(3, 4), 16) / 255
                local b = tonumber(color:sub(5, 6), 16) / 255
                bar:SetBackdropBorderColor(r, g, b, alpha)
            end
        else
            bar:SetBackdrop(nil)
        end
    end

    bar:ApplyBackground()
    self:RebuildSlots(bar)
    BDT.ModuleRegistry:UpdateBarStrata(bar)
    self:ApplyVisibility(bar)
    return bar
end

----------------------------------------------------
-- Create Bars from Profile
----------------------------------------------------
function BDT.BarManager:CreateFromProfile()
    for barName, barData in pairs(BDT.db.profile.bars) do
        local id = tonumber(barName:match("BDT_Bar(%d+)"))
        if id and id > 0 then
            self:CreateDataBar(id, barData.numSlots)
        end
    end
end

----------------------------------------------------
-- Create Movable Frame
----------------------------------------------------
function BDT.BarManager:CreateMovableFrame(name)
    -- Always create a fresh anonymous frame; store name in frame.GetName override
    -- This avoids WoW's global frame name collision when bars are deleted and recreated
    local f = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    -- Override GetName so BarManager code that uses bar:GetName() still works
    function f:GetName() return name end
    -- Also register in _G for compatibility with any code that looks up by name
    _G[name] = f
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetClampedToScreen(true)

    f:SetScript("OnDragStart", function(frame)
        if not BDT.db.profile.locked then
            frame:StartMoving()
        end
    end)

    f:SetScript("OnDragStop", function(frame)
        frame:StopMovingOrSizing()
        BDT.BarManager:SaveBarPosition(frame)
    end)

    return f
end

----------------------------------------------------
-- Bar ID Helper
----------------------------------------------------
-- Session counter — increments monotonically, never reuses IDs
local _barSessionCounter = 0
function BDT.BarManager:GetNextBarID()
    -- First try to find a free slot in saved data
    local n = 1
    while BDT.db.profile.bars["BDT_Bar" .. n] do
        n = n + 1
    end
    -- Ensure we never collide with a frame created this session
    _barSessionCounter = _barSessionCounter + 1
    if n < _barSessionCounter then n = _barSessionCounter end
    _barSessionCounter = n
    return n
end

----------------------------------------------------
-- Rebuild Slots
----------------------------------------------------
function BDT.BarManager:RebuildSlots(bar)
    if not bar then return end

    local barName = bar:GetName()
    local saved = BDT.db.profile.bars[barName]
    if not saved then return end

    local numSlots = saved.numSlots or 3
    local totalW = saved.width or 300
    local totalH = saved.height or 22
    local slotW = totalW / numSlots
    local slotH = totalH
    
    bar:SetSize(totalW, totalH)

    bar.slots = bar.slots or {}

    -- Hide/cleanup extra slots if we have more than needed
    for i = numSlots + 1, #bar.slots do
        local slot = bar.slots[i]
        if slot then
            if slot.moduleFrame then
                slot.moduleFrame:UnregisterAllEvents()
                slot.moduleFrame:SetScript("OnUpdate", nil)
                slot.moduleFrame:SetScript("OnEvent", nil)
                slot.moduleFrame:Hide()
                
                if BDT.UpdateTicker then
                    BDT.UpdateTicker:UnregisterPrefix(slot.module .. "_")
                end
            end
            slot:Hide()
        end
    end

    for i = 1, numSlots do
        local slot = bar.slots[i]
        
        -- Reuse existing slot frame if possible
        if not slot then
            local slotName = barName .. "_Slot" .. i
            slot = CreateFrame("Button", slotName, bar)
            slot.text = slot:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            slot.text:SetPoint("CENTER")
            
            -- Set up mouse events once
            slot:EnableMouse(true)
            slot:RegisterForClicks("AnyUp")
            slot:RegisterForDrag("LeftButton")
            
            bar.slots[i] = slot
        end
        
        -- Update slot properties
        slot.index = i
        slot:SetSize(slotW, slotH)
        slot:ClearAllPoints()
        slot:SetPoint("LEFT", bar, "LEFT", (i - 1) * slotW, 0)
        slot:Show()
        
        -- Read slot data
        local slotData = saved.slots[i]
        local assignedName, offsetX, offsetY
        
        if type(slotData) == "string" then
            assignedName = slotData
            offsetX, offsetY = 0, 0
        elseif type(slotData) == "table" then
            assignedName = slotData.module
            offsetX = slotData.offsetX or 0
            offsetY = slotData.offsetY or 0
        else
            assignedName = nil
            offsetX, offsetY = 0, 0
        end
        
        -- Only recreate module frame if the module changed
        if slot.module ~= assignedName then
            if slot.moduleFrame then
                slot.moduleFrame:UnregisterAllEvents()
                slot.moduleFrame:SetScript("OnUpdate", nil)
                slot.moduleFrame:SetScript("OnEvent", nil)
                slot.moduleFrame:Hide()
                
                if BDT.UpdateTicker then
                    BDT.UpdateTicker:UnregisterPrefix(slot.module .. "_")
                end
                slot.moduleFrame = nil
            end

            -- Clean up slot frame scripts set by previous modules
            slot:SetScript("OnEnter", nil)
            slot:SetScript("OnLeave", nil)
            slot:SetScript("OnClick", nil)

            -- Clean up secure button if it exists (used by Hearthstone and MythicPlusKey modules)
            if slot.secureButton then
                slot.secureButton:SetScript("OnEnter", nil)
                slot.secureButton:SetScript("OnLeave", nil)
                slot.secureButton:SetScript("PreClick", nil)
                slot.secureButton:SetAttribute("type", nil)
                slot.secureButton:SetAttribute("item", nil)
                slot.secureButton:Hide()
                slot.secureButton = nil
            end

            -- Clean up any icon textures
            if slot.icon then
                slot.icon:Hide()
                slot.icon = nil
            end

            -- Clean up cooldown text
            if slot.cooldownText then
                slot.cooldownText:SetText("")
                slot.cooldownText:Hide()
                slot.cooldownText = nil
            end

            if assignedName == "(spacer)" then
                slot.module = "(spacer)"
                slot.text:SetText("")
            elseif assignedName and BDT.modules[assignedName] then
                slot.module = assignedName
                slot.moduleFrame = BDT.modules[assignedName].Create(slot)
            else
                slot.module = nil
                slot.text:SetText(assignedName or L["(empty)"])
            end
        end

        -- Apply frame strata from bar's saved strata (slots always at least MEDIUM)
        -- so text remains visible even when bar is at BACKGROUND
        if assignedName and assignedName ~= "(spacer)" and BDT.modules[assignedName] then
            slot:SetFrameStrata("MEDIUM")
            if slot.moduleFrame then
                slot.moduleFrame:SetFrameStrata("MEDIUM")
            end
            if slot.secureButton then
                slot.secureButton:SetFrameStrata("MEDIUM")
            end
        end
        
        -- Apply offset
        if slot.text then
            local anchorPoint = BDT:GetModuleSetting(assignedName, "anchorPoint", "CENTER")
            slot.text:ClearAllPoints()
            slot.text:SetPoint(anchorPoint, slot, anchorPoint, offsetX, offsetY)
            -- Per-slot font/color overrides
            local slotSaved = saved.slots and saved.slots[i]
            local slotOpts  = type(slotSaved) == "table" and slotSaved or {}
            if slotOpts.fontPath and slotOpts.fontPath ~= "" then
                pcall(function()
                    slot.text:SetFont(slotOpts.fontPath, slotOpts.fontSize or 12,
                                     slotOpts.fontOutline or "NONE")
                end)
            end
            if slotOpts.colorUseClassColor then
                local cc = C_ClassColor and C_ClassColor.GetClassColor and
                           C_ClassColor.GetClassColor(select(2, UnitClass("player")))
                if cc then slot.text:SetTextColor(cc:GetRGB()) end
            elseif slotOpts.colorR then
                slot.text:SetTextColor(slotOpts.colorR, slotOpts.colorG or 1, slotOpts.colorB or 1, 1)
            end
        end
        
        -- Set up event handlers (these need to capture current values)
        slot:SetScript("OnMouseUp", function(self, btn)
            if btn == "RightButton" and IsControlKeyDown() then
                BDT.BarManager:ShowSlotDropdown(self, bar)
            end
        end)
        
        slot:SetScript("OnDragStart", function(self)
            if not BDT.db.profile.locked then
                bar:StartMoving()
            end
        end)
        
        slot:SetScript("OnDragStop", function(self)
            bar:StopMovingOrSizing()
            BDT.BarManager:SaveBarPosition(bar)
        end)
    end

    BDT.FontManager:ApplyFont()
end

----------------------------------------------------
-- Rebuild All Slots
----------------------------------------------------
function BDT.BarManager:RebuildAllSlots()
    for _, bar in pairs(BDT.bars) do
        self:RebuildSlots(bar)
    end
end

----------------------------------------------------
-- Save Bar Position
----------------------------------------------------
function BDT.BarManager:SaveBarPosition(bar)
    local point, _, relativePoint, x, y = bar:GetPoint()
    local barName = bar:GetName()
    BDT.db.profile.bars[barName].point = {
        point = point,
        relativePoint = relativePoint,
        x = x,
        y = y
    }
end

----------------------------------------------------
-- Slot Selection Dropdown
----------------------------------------------------
function BDT.BarManager:ShowSlotDropdown(slot, bar)
    local maxVisibleItems = 20 -- Maximum items to show before scrolling
    local totalItems = 2 + #BDT.cache.moduleNames -- (empty) + (spacer) + modules
    
    if totalItems <= maxVisibleItems then
        -- Use normal context menu for small lists
        MenuUtil.CreateContextMenu(slot, function(owner, root)
            -- Empty option
            root:CreateButton(L["(empty)"], function()
                BDT.db.profile.bars[bar:GetName()].slots[slot.index] = nil
                self:RebuildSlots(bar)
            end)

            -- Spacer option
            root:CreateButton(L["(spacer)"], function()
                BDT.db.profile.bars[bar:GetName()].slots[slot.index] = "(spacer)"
                self:RebuildSlots(bar)
            end)

            -- Module options
            for _, moduleName in ipairs(BDT.cache.moduleNames) do
                root:CreateButton(moduleName, function()
                    BDT.db.profile.bars[bar:GetName()].slots[slot.index] = moduleName
                    self:RebuildSlots(bar)
                end)
            end
        end)
    else
        -- Close any existing custom dropdown
        if self.customDropdown then
            self.customDropdown:Hide()
            self.customDropdown = nil
        end
        
        -- Create custom scrollable dropdown
        local dropdown = CreateFrame("Frame", "BDT_CustomSlotDropdown", UIParent, "BackdropTemplate")
        self.customDropdown = dropdown
        
        local itemHeight = 18
        local visibleHeight = maxVisibleItems * itemHeight
        local dropdownWidth = 200
        local backdropInsets = 4 -- top (2) + bottom (2) insets from backdrop
        
        dropdown:SetSize(dropdownWidth, visibleHeight + backdropInsets)
        dropdown:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 }
        })
        dropdown:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
        dropdown:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
        dropdown:SetFrameStrata("FULLSCREEN_DIALOG")
        dropdown:SetClampedToScreen(true)
        
        -- Position at cursor
        local x, y = GetCursorPosition()
        local scale = UIParent:GetEffectiveScale()
        dropdown:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / scale, y / scale)
        
        -- Close on escape
        dropdown:SetScript("OnKeyDown", function(self, key)
            if key == "ESCAPE" then
                self:Hide()
            end
        end)
        dropdown:SetPropagateKeyboardInput(false)
        
        -- Create scroll frame
        local scrollFrame = CreateFrame("ScrollFrame", nil, dropdown)
        scrollFrame:SetPoint("TOPLEFT", 2, -2)
        scrollFrame:SetPoint("BOTTOMRIGHT", -20, 2)
        
        -- Create scroll child (content container)
        local scrollChild = CreateFrame("Frame", nil, scrollFrame)
        scrollChild:SetSize(dropdownWidth - 22, totalItems * itemHeight)
        scrollFrame:SetScrollChild(scrollChild)
        
        -- Create scrollbar
        local scrollbar = CreateFrame("Slider", nil, dropdown, "UIPanelScrollBarTemplate")
        scrollbar:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", -3, -18)
        scrollbar:SetPoint("BOTTOMRIGHT", dropdown, "BOTTOMRIGHT", -3, 18)
        scrollbar:SetMinMaxValues(0, math.max(0, (totalItems * itemHeight) - visibleHeight))
        scrollbar:SetValueStep(itemHeight)
        scrollbar:SetObeyStepOnDrag(true)
        scrollbar:SetWidth(18)
        
        scrollbar:SetScript("OnValueChanged", function(self, value)
            scrollFrame:SetVerticalScroll(value)
        end)
        
        -- Mouse wheel scrolling
        scrollFrame:EnableMouseWheel(true)
        scrollFrame:SetScript("OnMouseWheel", function(self, delta)
            local current = scrollbar:GetValue()
            local minVal, maxVal = scrollbar:GetMinMaxValues()
            local newValue = current - (delta * itemHeight * 3) -- Scroll 3 items at a time
            newValue = math.max(minVal, math.min(maxVal, newValue))
            scrollbar:SetValue(newValue)
        end)
        
        -- Create buttons
        local buttons = {}
        local itemIndex = 0
        
        local function CreateButton(text, onClick)
            local btn = CreateFrame("Button", nil, scrollChild)
            btn:SetSize(dropdownWidth - 22, itemHeight)
            btn:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -(itemIndex * itemHeight))
            
            btn:SetNormalFontObject("GameFontHighlightSmall")
            btn:SetHighlightFontObject("GameFontHighlightSmall")
            
            local btnText = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            btnText:SetPoint("LEFT", btn, "LEFT", 5, 0)
            btnText:SetText(text)
            btn:SetFontString(btnText)
            
            -- Highlight texture
            local highlight = btn:CreateTexture(nil, "BACKGROUND")
            highlight:SetAllPoints(btn)
            highlight:SetColorTexture(0.3, 0.3, 0.8, 0.5)
            btn:SetHighlightTexture(highlight)
            
            btn:SetScript("OnClick", function()
                onClick()
                dropdown:Hide()
            end)
            
            itemIndex = itemIndex + 1
            return btn
        end
        
        -- Add (empty) option
        CreateButton(L["(empty)"], function()
            BDT.db.profile.bars[bar:GetName()].slots[slot.index] = nil
            self:RebuildSlots(bar)
        end)
        
        -- Add (spacer) option
        CreateButton(L["(spacer)"], function()
            BDT.db.profile.bars[bar:GetName()].slots[slot.index] = "(spacer)"
            self:RebuildSlots(bar)
        end)
        
        -- Add module options
        for _, moduleName in ipairs(BDT.cache.moduleNames) do
            CreateButton(moduleName, function()
                BDT.db.profile.bars[bar:GetName()].slots[slot.index] = moduleName
                self:RebuildSlots(bar)
            end)
        end
        
        -- Close on click outside
        dropdown:SetScript("OnHide", function()
            self.customDropdown = nil
        end)
        
        -- Close on right click anywhere
        dropdown:SetScript("OnMouseDown", function(self, button)
            if button == "RightButton" then
                self:Hide()
            end
        end)
        
        -- Create invisible close button that covers entire screen
        local closeButton = CreateFrame("Button", nil, UIParent)
        closeButton:SetFrameStrata("FULLSCREEN")
        closeButton:SetAllPoints(UIParent)
        closeButton:SetScript("OnClick", function()
            dropdown:Hide()
            closeButton:Hide()
        end)
        closeButton:Show()
        
        dropdown:SetScript("OnHide", function()
            closeButton:Hide()
            self.customDropdown = nil
        end)
        
        -- Show the dropdown after close button so it's on top
        closeButton:SetFrameLevel(dropdown:GetFrameLevel() - 1)
        dropdown:Show()
        
        -- Set initial scroll position
        scrollbar:SetValue(0)
    end
end

----------------------------------------------------
-- Toggle All Panels
----------------------------------------------------
function BDT.BarManager:ToggleAllPanels()
    BDT.db.profile.showPanels = not BDT.db.profile.showPanels

    if BDT.db.profile.showPanels then
        BDT:Print(L["Panels shown"])
    else
        BDT:Print(L["Panels hidden"])
    end

    self:ApplyVisibilityAll()
end

----------------------------------------------------
-- Lock/Unlock Panels
----------------------------------------------------
function BDT.BarManager:ToggleLock()
    BDT.db.profile.locked = not BDT.db.profile.locked
    
    if BDT.db.profile.locked then
        BDT:Print(L["Panels locked"])
    else
        BDT:Print(L["Panels unlocked"])
    end
    
    self:ApplyLockState()
end
