--[[
    Commander 3.0 - Context Menu Component

    Right-click context menu with submenu support.
    Follows the SuggestionsDropdown pattern for consistency.
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Context Menu Class
-------------------------------------------------------------------------------

local ContextMenu = C3.class()
C3.UI = C3.UI or {}
C3.UI.ContextMenu = ContextMenu

-- Constants
local MAX_VISIBLE_ITEMS = 10
local ROW_HEIGHT = 20
local SEPARATOR_HEIGHT = 8
local MIN_WIDTH = 150
local SUBMENU_DELAY = 0.15  -- Seconds before submenu appears

-------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------

---Create a new context menu
---@param parent frame Parent frame (for reference, menu anchors to UIParent)
function ContextMenu:init(parent)
    self.parent = parent
    self.items = {}
    self.selectedIndex = 0
    self.buttons = {}
    self.activeSubmenu = nil
    self.parentMenu = nil  -- For submenu chains
    self.hoverTimer = nil
    self.currentLine = nil  -- The line that triggered this menu

    self:_createFrame()
    self:_createButtonPool()
    self:_setupDismissHandler()
end

-------------------------------------------------------------------------------
-- Frame Creation
-------------------------------------------------------------------------------

function ContextMenu:_createFrame()
    -- Main container frame
    self.frame = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    self.frame:SetFrameStrata("TOOLTIP")
    self.frame:SetFrameLevel(100)
    self.frame:Hide()

    -- Dark background with border (matching terminal theme)
    self.frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    self.frame:SetBackdropColor(0.06, 0.07, 0.12, 0.95)
    self.frame:SetBackdropBorderColor(0.25, 0.30, 0.40, 1)

    -- Content frame for buttons
    self.content = CreateFrame("Frame", nil, self.frame)
    self.content:SetPoint("TOPLEFT", 2, -2)
    self.content:SetPoint("TOPRIGHT", -2, -2)

    -- Enable keyboard for navigation
    -- Note: SetPropagateKeyboardInput is protected and can only be called inside OnKeyDown
    self.frame:EnableKeyboard(true)

    self.frame:SetScript("OnKeyDown", function(_, key)
        if not self.frame:IsShown() then return end

        -- Helper to safely set propagation (protected function, blocked during combat)
        local function setPropagation(value)
            if not InCombatLockdown() then
                self.frame:SetPropagateKeyboardInput(value)
            end
        end

        if key == "ESCAPE" then
            self:Hide()
            setPropagation(false)
            return
        elseif key == "UP" then
            self:SelectPrev()
            setPropagation(false)
            return
        elseif key == "DOWN" then
            self:SelectNext()
            setPropagation(false)
            return
        elseif key == "ENTER" then
            self:ActivateSelected()
            setPropagation(false)
            return
        elseif key == "RIGHT" then
            -- Open submenu if selected item has one
            local item = self.items[self.selectedIndex]
            if item and item.subMenu then
                self:_showSubmenu(self.selectedIndex)
                setPropagation(false)
                return
            end
        elseif key == "LEFT" then
            -- Close this menu if it's a submenu
            if self.parentMenu then
                self:Hide()
                setPropagation(false)
                return
            end
        end

        setPropagation(true)
    end)
end

function ContextMenu:_createButtonPool()
    -- Theme font (with zoom support)
    local Theme = C3.UI.Theme
    local font = Theme and Theme.Font or "Fonts\\FRIZQT__.TTF"
    local fontSize = Theme and Theme:GetEffectiveFontSize() or 12
    local rowHeight = Theme and (Theme:GetEffectiveLineHeight() + 5) or ROW_HEIGHT

    for i = 1, MAX_VISIBLE_ITEMS do
        local button = CreateFrame("Button", nil, self.content)
        button:SetHeight(rowHeight)

        -- Highlight texture (on hover)
        local highlight = button:CreateTexture(nil, "HIGHLIGHT")
        highlight:SetAllPoints()
        highlight:SetColorTexture(0.3, 0.3, 0.5, 0.4)
        button.highlight = highlight

        -- Selection background
        button.selectionBg = button:CreateTexture(nil, "BACKGROUND")
        button.selectionBg:SetAllPoints()
        button.selectionBg:SetColorTexture(0.4, 0.8, 1.0, 0.1)
        button.selectionBg:Hide()

        -- Selection indicator (left bar)
        button.selection = button:CreateTexture(nil, "OVERLAY")
        button.selection:SetPoint("TOPLEFT", 0, 0)
        button.selection:SetPoint("BOTTOMLEFT", 0, 0)
        button.selection:SetWidth(3)
        button.selection:SetColorTexture(0.4, 0.8, 1.0, 1)
        button.selection:Hide()

        -- Icon
        button.icon = button:CreateTexture(nil, "ARTWORK")
        button.icon:SetPoint("LEFT", 6, 0)
        button.icon:SetSize(16, 16)
        button.icon:Hide()

        -- Text
        button.text = button:CreateFontString(nil, "OVERLAY")
        button.text:SetFont(font, fontSize, "")
        button.text:SetPoint("LEFT", 8, 0)
        button.text:SetPoint("RIGHT", -24, 0)
        button.text:SetJustifyH("LEFT")
        button.text:SetTextColor(1, 1, 1)

        -- Submenu arrow
        button.arrow = button:CreateFontString(nil, "OVERLAY")
        button.arrow:SetFont(font, fontSize, "")
        button.arrow:SetPoint("RIGHT", -6, 0)
        button.arrow:SetText(">")
        button.arrow:SetTextColor(0.6, 0.6, 0.6)
        button.arrow:Hide()

        -- Separator line (for separator items)
        button.separator = button:CreateTexture(nil, "ARTWORK")
        button.separator:SetPoint("LEFT", 6, 0)
        button.separator:SetPoint("RIGHT", -6, 0)
        button.separator:SetHeight(1)
        button.separator:SetColorTexture(0.25, 0.30, 0.40, 0.6)
        button.separator:Hide()

        -- Click handler
        button:SetScript("OnClick", function()
            self:_onButtonClick(button.itemIndex)
        end)

        -- Hover handlers
        button:SetScript("OnEnter", function()
            self:Select(button.itemIndex)
            self:_onButtonEnter(button.itemIndex)
        end)

        button:SetScript("OnLeave", function()
            self:_onButtonLeave(button.itemIndex)
        end)

        self.buttons[i] = button
    end
end

function ContextMenu:_setupDismissHandler()
    -- Click anywhere outside to dismiss
    -- We use a WorldFrame hook since it covers everything
    self.frame:SetScript("OnShow", function()
        -- Create dismiss overlay if needed
        if not self.dismissOverlay then
            self.dismissOverlay = CreateFrame("Button", nil, UIParent)
            self.dismissOverlay:SetFrameStrata("TOOLTIP")
            self.dismissOverlay:SetFrameLevel(99)  -- Just below menu
            self.dismissOverlay:SetAllPoints(UIParent)
            self.dismissOverlay:RegisterForClicks("AnyUp")
            self.dismissOverlay:SetScript("OnClick", function()
                self:HideAll()
            end)
        end
        self.dismissOverlay:Show()
    end)

    self.frame:SetScript("OnHide", function()
        if self.dismissOverlay and not self.activeSubmenu then
            -- Only hide dismiss overlay if no submenus are open
            local hasOpenSubmenu = self.activeSubmenu and self.activeSubmenu.frame:IsShown()
            if not hasOpenSubmenu then
                self.dismissOverlay:Hide()
            end
        end
        -- Cancel any pending submenu timer
        if self.hoverTimer then
            self.hoverTimer:Cancel()
            self.hoverTimer = nil
        end
    end)
end

-------------------------------------------------------------------------------
-- Display
-------------------------------------------------------------------------------

---Show the context menu at screen coordinates
---@param items table[] Array of menu items
---@param screenX number Screen X coordinate (from GetCursorPosition)
---@param screenY number Screen Y coordinate (from GetCursorPosition)
---@param line? table The BufferLine that triggered this menu
function ContextMenu:Show(items, screenX, screenY, line)
    if not items or #items == 0 then
        self:Hide()
        return
    end

    self.items = items
    self.selectedIndex = 0
    self.currentLine = line

    -- Close any existing submenu
    if self.activeSubmenu then
        self.activeSubmenu:Hide()
        self.activeSubmenu = nil
    end

    -- Convert screen coordinates
    local scale = UIParent:GetEffectiveScale()
    local x, y = screenX / scale, screenY / scale

    -- Calculate size
    local width, height = self:_calculateSize(items)

    self.frame:SetSize(width, height)
    self.content:SetHeight(height - 4)

    -- Position at cursor
    self.frame:ClearAllPoints()

    -- Screen bounds check
    local screenWidth, screenHeight = GetScreenWidth(), GetScreenHeight()

    -- Determine horizontal position
    local anchorH = "LEFT"
    if x + width > screenWidth then
        anchorH = "RIGHT"
    end

    -- Determine vertical position
    local anchorV = "TOP"
    if y - height < 0 then
        anchorV = "BOTTOM"
        y = y  -- Anchor from bottom
    end

    local anchor = anchorV .. anchorH
    local relPoint = "BOTTOMLEFT"

    if anchor == "TOPLEFT" then
        self.frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
    elseif anchor == "TOPRIGHT" then
        self.frame:SetPoint("TOPRIGHT", UIParent, "BOTTOMLEFT", x, y)
    elseif anchor == "BOTTOMLEFT" then
        self.frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x, y)
    elseif anchor == "BOTTOMRIGHT" then
        self.frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMLEFT", x, y)
    end

    -- Populate buttons
    self:_populateButtons(items)

    self.frame:Show()
end

function ContextMenu:_calculateSize(items)
    local Theme = C3.UI.Theme
    local font = Theme and Theme.Font or "Fonts\\FRIZQT__.TTF"
    local fontSize = Theme and Theme:GetEffectiveFontSize() or 12
    local rowHeight = Theme and (Theme:GetEffectiveLineHeight() + 5) or ROW_HEIGHT

    local maxWidth = MIN_WIDTH
    local totalHeight = 4  -- Padding

    -- Temporary FontString for measuring
    if not self._measureFS then
        self._measureFS = self.frame:CreateFontString(nil, "OVERLAY")
    end
    self._measureFS:SetFont(font, fontSize, "")
    self._measureFS:Hide()

    for i, item in ipairs(items) do
        if item.isSeparator then
            totalHeight = totalHeight + SEPARATOR_HEIGHT
        else
            totalHeight = totalHeight + rowHeight

            -- Measure text width
            self._measureFS:SetText(item.text or "")
            local textWidth = self._measureFS:GetStringWidth()

            -- Add padding for icon, arrow, margins
            local itemWidth = textWidth + 40  -- Left padding + right padding + arrow space
            if item.icon then
                itemWidth = itemWidth + 20
            end
            if item.subMenu then
                itemWidth = itemWidth + 16
            end

            if itemWidth > maxWidth then
                maxWidth = itemWidth
            end
        end
    end

    return maxWidth, totalHeight
end

function ContextMenu:_populateButtons(items)
    local Theme = C3.UI.Theme
    local rowHeight = Theme and (Theme:GetEffectiveLineHeight() + 5) or ROW_HEIGHT

    local yOffset = 0
    local buttonIndex = 1

    for i, item in ipairs(items) do
        if buttonIndex > MAX_VISIBLE_ITEMS then break end

        local button = self.buttons[buttonIndex]

        if item.isSeparator then
            -- Separator
            button:SetHeight(SEPARATOR_HEIGHT)
            button:SetPoint("TOPLEFT", 0, -yOffset)
            button:SetPoint("TOPRIGHT", 0, -yOffset)

            button.text:Hide()
            button.icon:Hide()
            button.arrow:Hide()
            button.separator:Show()
            button.highlight:Hide()
            button.selection:Hide()
            button.selectionBg:Hide()

            button:Disable()
            button.itemIndex = nil

            yOffset = yOffset + SEPARATOR_HEIGHT
        else
            -- Regular item
            button:SetHeight(rowHeight)
            button:SetPoint("TOPLEFT", 0, -yOffset)
            button:SetPoint("TOPRIGHT", 0, -yOffset)

            button.separator:Hide()

            -- Text
            button.text:SetText(item.text or "")
            button.text:Show()

            -- Icon
            if item.icon then
                button.icon:SetTexture(item.icon)
                button.icon:Show()
                button.text:SetPoint("LEFT", 28, 0)
            else
                button.icon:Hide()
                button.text:SetPoint("LEFT", 8, 0)
            end

            -- Submenu arrow
            if item.subMenu then
                button.arrow:Show()
            else
                button.arrow:Hide()
            end

            -- Disabled state
            if item.isDisabled then
                button.text:SetTextColor(0.4, 0.4, 0.4)
                button.highlight:SetColorTexture(0, 0, 0, 0)  -- No highlight
                button:SetScript("OnClick", nil)
            else
                -- Color
                if item.color then
                    if type(item.color) == "string" then
                        local r = tonumber(item.color:sub(1, 2), 16) / 255
                        local g = tonumber(item.color:sub(3, 4), 16) / 255
                        local b = tonumber(item.color:sub(5, 6), 16) / 255
                        button.text:SetTextColor(r, g, b)
                    else
                        button.text:SetTextColor(item.color.r or 1, item.color.g or 1, item.color.b or 1)
                    end
                else
                    button.text:SetTextColor(1, 1, 1)
                end
                button.highlight:SetColorTexture(0.3, 0.3, 0.5, 0.4)
                button:SetScript("OnClick", function()
                    self:_onButtonClick(i)
                end)
            end

            button.selection:Hide()
            button.selectionBg:Hide()

            button:Enable()
            button.itemIndex = i

            yOffset = yOffset + rowHeight
        end

        button:Show()
        buttonIndex = buttonIndex + 1
    end

    -- Hide unused buttons
    for j = buttonIndex, MAX_VISIBLE_ITEMS do
        self.buttons[j]:Hide()
    end
end

---Hide the menu (and optionally all submenus)
function ContextMenu:Hide()
    -- Close any submenu first
    if self.activeSubmenu then
        self.activeSubmenu:Hide()
        self.activeSubmenu = nil
    end

    self.frame:Hide()
    self.items = {}
    self.selectedIndex = 0
    self.currentLine = nil

    -- Hide dismiss overlay only if this is the root menu
    if not self.parentMenu and self.dismissOverlay then
        self.dismissOverlay:Hide()
    end
end

---Hide this menu and all parent/child menus
function ContextMenu:HideAll()
    -- Find root menu
    local root = self
    while root.parentMenu do
        root = root.parentMenu
    end

    -- Hide from root (cascades to children)
    root:Hide()
end

---Check if visible
---@return boolean
function ContextMenu:IsVisible()
    return self.frame:IsVisible()
end

-------------------------------------------------------------------------------
-- Selection
-------------------------------------------------------------------------------

---Select a specific index
---@param index number
function ContextMenu:Select(index)
    -- Skip separators
    local item = self.items[index]
    if item and item.isSeparator then
        return
    end

    if index < 0 then index = 0 end
    if index > #self.items then index = #self.items end

    self.selectedIndex = index
    self:_updateSelection()
end

---Select next non-separator item
function ContextMenu:SelectNext()
    local newIndex = self.selectedIndex + 1
    while newIndex <= #self.items do
        if not self.items[newIndex].isSeparator then
            self:Select(newIndex)
            return
        end
        newIndex = newIndex + 1
    end
end

---Select previous non-separator item
function ContextMenu:SelectPrev()
    local newIndex = self.selectedIndex - 1
    while newIndex >= 1 do
        if not self.items[newIndex].isSeparator then
            self:Select(newIndex)
            return
        end
        newIndex = newIndex - 1
    end
end

---Activate the currently selected item
function ContextMenu:ActivateSelected()
    if self.selectedIndex > 0 then
        self:_onButtonClick(self.selectedIndex)
    end
end

function ContextMenu:_updateSelection()
    for i, button in ipairs(self.buttons) do
        if button:IsShown() and button.itemIndex then
            if button.itemIndex == self.selectedIndex then
                button.selection:Show()
                button.selectionBg:Show()
            else
                button.selection:Hide()
                button.selectionBg:Hide()
            end
        end
    end
end

-------------------------------------------------------------------------------
-- Event Handlers
-------------------------------------------------------------------------------

function ContextMenu:_onButtonClick(index)
    local item = self.items[index]
    if not item or item.isDisabled then return end

    -- If item has submenu, show it instead of executing
    if item.subMenu then
        self:_showSubmenu(index)
        return
    end

    -- Execute action
    if item.func then
        -- Pass the line that triggered the menu
        item.func(self.currentLine)
    end

    -- Fire callback if set
    if self.onSelect then
        self.onSelect(item)
    end

    -- Close all menus
    self:HideAll()
end

function ContextMenu:_onButtonEnter(index)
    local item = self.items[index]
    if not item then return end

    -- Cancel any pending submenu timer
    if self.hoverTimer then
        self.hoverTimer:Cancel()
        self.hoverTimer = nil
    end

    -- Close current submenu if hovering different item
    if self.activeSubmenu and self.activeSubmenuIndex ~= index then
        self.activeSubmenu:Hide()
        self.activeSubmenu = nil
        self.activeSubmenuIndex = nil
    end

    -- If this item has a submenu, start timer to show it
    if item.subMenu then
        self.hoverTimer = C_Timer.NewTimer(SUBMENU_DELAY, function()
            self:_showSubmenu(index)
        end)
    end
end

function ContextMenu:_onButtonLeave(index)
    -- Timer will be cancelled if we enter another button
end

function ContextMenu:_showSubmenu(index)
    local item = self.items[index]
    if not item or not item.subMenu then return end

    -- Get submenu items (support lazy loading)
    local subItems
    if type(item.subMenu) == "function" then
        subItems = item.subMenu()
    else
        subItems = item.subMenu
    end

    if not subItems or #subItems == 0 then return end

    -- Close existing submenu
    if self.activeSubmenu then
        self.activeSubmenu:Hide()
    end

    -- Create submenu if needed
    if not self._submenu then
        self._submenu = ContextMenu:new(self.parent)
        self._submenu.parentMenu = self
    end

    self.activeSubmenu = self._submenu
    self.activeSubmenuIndex = index

    -- Calculate position (to the right of the button, or left if near edge)
    local button = nil
    for _, btn in ipairs(self.buttons) do
        if btn.itemIndex == index then
            button = btn
            break
        end
    end

    if not button then return end

    local buttonLeft = button:GetLeft()
    local buttonRight = button:GetRight()
    local buttonTop = button:GetTop()
    local menuWidth = self.frame:GetWidth()

    local scale = UIParent:GetEffectiveScale()
    local screenWidth = GetScreenWidth()

    -- Position submenu
    local subX, subY
    if buttonRight + menuWidth < screenWidth then
        -- Open to the right
        subX = buttonRight * scale
    else
        -- Open to the left
        subX = (buttonLeft - menuWidth) * scale
    end
    subY = buttonTop * scale

    -- Pass through the current line
    self._submenu.currentLine = self.currentLine
    self._submenu:Show(subItems, subX, subY, self.currentLine)
end

-------------------------------------------------------------------------------
-- Callback
-------------------------------------------------------------------------------

---Set callback for when an item is selected
---@param callback function(item)
function ContextMenu:SetOnSelect(callback)
    self.onSelect = callback
end

-------------------------------------------------------------------------------
-- Font Size Updates
-------------------------------------------------------------------------------

---Update font size when zoom changes
function ContextMenu:UpdateFontSize()
    local Theme = C3.UI.Theme
    if not Theme then return end

    local fontSize = Theme:GetEffectiveFontSize()
    local font = Theme.Font or "Fonts\\FRIZQT__.TTF"
    local rowHeight = Theme:GetEffectiveLineHeight() + 5

    -- Update all button fonts and heights
    for i, button in ipairs(self.buttons) do
        button:SetHeight(rowHeight)
        if button.text then
            button.text:SetFont(font, fontSize, "")
        end
        if button.arrow then
            button.arrow:SetFont(font, fontSize, "")
        end
    end

    -- Update measure FontString
    if self._measureFS then
        self._measureFS:SetFont(font, fontSize, "")
    end

    -- Update submenu if it exists
    if self._submenu then
        self._submenu:UpdateFontSize()
    end
end

return ContextMenu
