-- =============================================================================
-- CustomDropdown: Taint-free dropdown replacement
-- Replaces Blizzard's UIDropDownMenu which taints secure action globals
-- =============================================================================
local ADDON_NAME, NS = ...

local ETBDropdown = {}
NS.ETBDropdown = ETBDropdown
-- NOTE: No _G.ETBDropdown - writing to _G taints the global namespace
-- All files should access via NS.ETBDropdown or local ADDON_NAME, NS = ...

-- Active menu tracking (our own, not Blizzard's)
local activeMenu = nil

-- Close any open dropdown when clicking elsewhere
-- CRITICAL: EnableMouse ONLY when closer is visible, disable when hidden
-- A FULLSCREEN_DIALOG with EnableMouse(true) taints secure input paths (FocusUnit, etc.)
local closer = CreateFrame("Frame", nil, UIParent)
closer:SetFrameStrata("FULLSCREEN_DIALOG")
closer:SetAllPoints()
closer:EnableMouse(false)  -- START disabled — only enable when we need it
closer:SetScript("OnMouseDown", function(self)
    if activeMenu then
        activeMenu:Hide()
        activeMenu = nil
    end
    self:EnableMouse(false)
    self:Hide()
end)
closer:Hide()

-- Ensure EnableMouse is synced with visibility
closer:SetScript("OnShow", function(self)
    self:EnableMouse(true)
end)
closer:SetScript("OnHide", function(self)
    self:EnableMouse(false)
end)

-- Close on ESC too.
-- v14.10.1: removed pcall(SetPropagateKeyboardInput) calls — they fired
-- ADDON_ACTION_BLOCKED on MoP Classic 5.4.8. Keep OnKeyDown but drop the
-- propagation toggle; while a dropdown is open you don't expect to type
-- into chat anyway, so the lost propagation is acceptable.
closer:SetScript("OnKeyDown", function(self, key)
    if key == "ESCAPE" then
        if activeMenu then
            activeMenu:Hide()
            activeMenu = nil
        end
        self:EnableMouse(false)
        self:Hide()
    end
end)

-- =============================================================================
-- Create a dropdown widget
-- =============================================================================
function ETBDropdown:Create(name, parent)
    local dd = CreateFrame("Frame", name, parent, "BackdropTemplate")
    dd:SetSize(180, 26)
    dd:SetBackdrop({
        bgFile = "Interface\\Buttons\\White8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 12,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    dd:SetBackdropColor(0.1, 0.1, 0.12, 0.95)
    dd:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8)

    -- Text label
    dd.text = dd:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    dd.text:SetPoint("LEFT", 8, 0)
    dd.text:SetPoint("RIGHT", -20, 0)
    dd.text:SetJustifyH("LEFT")
    dd.text:SetText("Select...")

    -- Arrow indicator
    dd.arrow = dd:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    dd.arrow:SetPoint("RIGHT", -6, 0)
    dd.arrow:SetText("▼")
    dd.arrow:SetTextColor(0.7, 0.7, 0.7)

    -- State
    dd._selectedValue = nil
    dd._initFunc = nil
    dd._width = 150

    -- Invisible Button overlay for reliable click detection in MoP Classic
    -- Plain Frames with OnMouseDown can miss clicks depending on strata/level
    dd.clickBtn = CreateFrame("Button", (name or "ETBDropdown") .. "ClickBtn", dd)
    dd.clickBtn:SetAllPoints()
    dd.clickBtn:SetFrameLevel(dd:GetFrameLevel() + 5)
    dd.clickBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    dd.clickBtn:SetScript("OnClick", function(_, button)
        if dd._menu and dd._menu:IsShown() then
            dd._menu:Hide()
            activeMenu = nil
            closer:Hide()
        else
            ETBDropdown:OpenMenu(dd)
        end
    end)

    -- Also keep Frame-level mouse handling as fallback
    dd:EnableMouse(true)
    dd:SetScript("OnMouseDown", function(self)
        if self._menu and self._menu:IsShown() then
            self._menu:Hide()
            activeMenu = nil
            closer:Hide()
        else
            ETBDropdown:OpenMenu(self)
        end
    end)

    -- Hover highlight (on both the frame and the button)
    local function onEnter()
        dd:SetBackdropBorderColor(0.6, 0.6, 0.6, 1)
    end
    local function onLeave()
        dd:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8)
    end
    dd:SetScript("OnEnter", onEnter)
    dd:SetScript("OnLeave", onLeave)
    dd.clickBtn:SetScript("OnEnter", onEnter)
    dd.clickBtn:SetScript("OnLeave", onLeave)

    -- Close menu when dropdown itself hides (menu is parented to UIParent)
    dd:HookScript("OnHide", function(self)
        if self._menu and self._menu:IsShown() then
            self._menu:Hide()
            if activeMenu == self._menu then
                activeMenu = nil
                closer:Hide()
            end
        end
    end)

    return dd
end

-- =============================================================================
-- Menu display
-- =============================================================================
function ETBDropdown:OpenMenu(dd)
    -- Close any existing
    if activeMenu then
        activeMenu:Hide()
    end

    if not dd._initFunc then return end

    -- Create or reuse menu frame
    if not dd._menu then
        -- CRITICAL: Parent to UIParent, NOT to dd.
        -- Sidebar uses SetClipsChildren(true) which would clip menus
        -- that extend beyond the sidebar bounds, making them invisible/unclickable.
        local menu = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
        menu:SetBackdrop({
            bgFile = "Interface\\Buttons\\White8x8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            edgeSize = 12,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        menu:SetBackdropColor(0.08, 0.08, 0.1, 0.98)
        menu:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.9)
        menu:SetFrameStrata("FULLSCREEN_DIALOG")
        menu:SetFrameLevel(500)
        menu:EnableMouse(true)

        -- Scroll frame for long lists
        menu.scrollFrame = CreateFrame("ScrollFrame", nil, menu)
        menu.scrollFrame:SetPoint("TOPLEFT", 4, -4)
        menu.scrollFrame:SetPoint("BOTTOMRIGHT", -4, 4)

        menu.scrollChild = CreateFrame("Frame", nil, menu.scrollFrame)
        menu.scrollChild:SetSize(1, 1)
        menu.scrollFrame:SetScrollChild(menu.scrollChild)

        menu.scrollFrame:EnableMouseWheel(true)
        menu.scrollFrame:SetScript("OnMouseWheel", function(self, delta)
            local current = self:GetVerticalScroll()
            local maxScroll = max(0, self:GetScrollChild():GetHeight() - self:GetHeight())
            local newScroll = max(0, min(maxScroll, current - delta * 20))
            self:SetVerticalScroll(newScroll)
        end)

        menu.buttons = {}
        dd._menu = menu
    end

    local menu = dd._menu

    -- Clear old buttons
    for _, btn in ipairs(menu.buttons) do
        btn:Hide()
    end

    -- Collect items from init function
    local items = {}
    dd._initFunc(dd, items)

    -- Build buttons
    local ROW_HEIGHT = 20
    local menuWidth = max(dd._width, dd:GetWidth()) + 10
    local yOffset = 0

    for i, item in ipairs(items) do
        local btn = menu.buttons[i]
        if not btn then
            btn = CreateFrame("Button", nil, menu.scrollChild)
            btn:SetHeight(ROW_HEIGHT)
            btn:RegisterForClicks("LeftButtonUp")

            btn.text = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            btn.text:SetPoint("LEFT", 8, 0)
            btn.text:SetPoint("RIGHT", -8, 0)
            btn.text:SetJustifyH("LEFT")

            btn.highlight = btn:CreateTexture(nil, "HIGHLIGHT")
            btn.highlight:SetAllPoints()
            btn.highlight:SetColorTexture(0.3, 0.5, 0.8, 0.3)

            btn.check = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            btn.check:SetPoint("LEFT", 4, 0)
            btn.check:SetText("")

            menu.buttons[i] = btn
        end

        btn:SetPoint("TOPLEFT", menu.scrollChild, "TOPLEFT", 0, -yOffset)
        btn:SetPoint("TOPRIGHT", menu.scrollChild, "TOPRIGHT", 0, -yOffset)
        btn:Show()

        -- Title/separator
        if item.isTitle then
            btn.text:SetText("|cffffd100" .. (item.text or "") .. "|r")
            btn.text:SetPoint("LEFT", 8, 0)
            btn.check:SetText("")
            btn:SetScript("OnClick", nil)
            btn.highlight:Hide()
        else
            local isSelected = (dd._selectedValue ~= nil and item.value == dd._selectedValue)
            btn.check:SetText(isSelected and "✓ " or "    ")
            btn.text:SetText(item.text or "")
            btn.text:SetPoint("LEFT", 8, 0)
            btn.highlight:Show()

            local capturedItem = item
            btn:SetScript("OnClick", function()
                -- CRITICAL: Close menu/closer BEFORE calling func
                -- If func() errors (e.g. RefreshItems), closer would stay shown
                -- at FULLSCREEN_DIALOG strata, eating ALL mouse clicks permanently
                menu:Hide()
                activeMenu = nil
                closer:Hide()
                if capturedItem.func then
                    capturedItem.func()
                end
            end)
        end

        -- Disabled state
        if item.disabled then
            btn.text:SetTextColor(0.5, 0.5, 0.5)
            btn:SetScript("OnClick", nil)
        elseif not item.isTitle then
            btn.text:SetTextColor(1, 1, 1)
        end

        yOffset = yOffset + ROW_HEIGHT
    end

    -- Size the menu
    local totalHeight = yOffset + 8
    local maxHeight = 300
    local displayHeight = min(totalHeight, maxHeight)

    menu:SetSize(menuWidth, displayHeight)
    menu.scrollChild:SetSize(menuWidth - 8, yOffset)

    -- Position below dropdown
    menu:ClearAllPoints()
    menu:SetPoint("TOPLEFT", dd, "BOTTOMLEFT", 0, -2)

    menu:Show()
    activeMenu = menu

    -- Show backdrop closer
    closer:SetFrameLevel(menu:GetFrameLevel() - 1)
    closer:Show()
    closer:SetParent(UIParent)
    closer:SetAllPoints(UIParent)
end

-- =============================================================================
-- API compatibility layer (mirrors UIDropDownMenu_* functions)
-- =============================================================================
function ETBDropdown:SetWidth(dd, width)
    dd._width = width
    dd:SetSize(width + 30, 26)
end

function ETBDropdown:SetText(dd, text)
    dd.text:SetText(text or "")
end

function ETBDropdown:SetSelectedValue(dd, value)
    dd._selectedValue = value
end

function ETBDropdown:Initialize(dd, initFunc)
    dd._initFunc = initFunc
end

-- =============================================================================
-- Helper: Create an info table (like UIDropDownMenu_CreateInfo)
-- =============================================================================
function ETBDropdown:CreateInfo()
    return {
        text = "",
        value = nil,
        func = nil,
        isTitle = false,
        disabled = false,
        checked = false,
    }
end

-- =============================================================================
-- Helper: Add item to items table (used during init callback)
-- =============================================================================
function ETBDropdown:AddButton(items, info)
    items[#items + 1] = {
        text = info.text,
        value = info.value,
        func = info.func,
        isTitle = info.isTitle,
        disabled = info.disabled,
        checked = info.checked,
    }
end
