--[[
    Commander 3.0 - Suggestions Dropdown Component

    VS Code-style popup menu for autocomplete suggestions.
    Shows below the input box, navigable with arrow keys.
]]

local ADDON_NAME, C3 = ...

-- Use shared utilities
local GetThemeColor = function(...) return C3.Colors.GetColor(...) end

-------------------------------------------------------------------------------
-- Suggestions Dropdown Class
-------------------------------------------------------------------------------

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

-- Quality colors (WoW item quality)
local QUALITY_COLORS = {
    [0] = { 0.62, 0.62, 0.62 }, -- Poor (gray)
    [1] = { 1.00, 1.00, 1.00 }, -- Common (white)
    [2] = { 0.12, 1.00, 0.00 }, -- Uncommon (green)
    [3] = { 0.00, 0.44, 0.87 }, -- Rare (blue)
    [4] = { 0.64, 0.21, 0.93 }, -- Epic (purple)
    [5] = { 1.00, 0.50, 0.00 }, -- Legendary (orange)
    [6] = { 0.90, 0.80, 0.50 }, -- Artifact (gold)
    [7] = { 0.00, 0.80, 1.00 }, -- Heirloom (cyan)
}

---Create a new suggestions dropdown
---@param parent frame Parent frame
function SuggestionsDropdown:init(parent)
    self.parent = parent
    self.suggestions = {}
    self.selectedIndex = 1
    self.scrollOffset = 0         -- First suggestion index shown in viewport (0-based)
    self.maxVisible = 9           -- Maximum visible rows
    self.stickyRow = 5            -- Center row where selection sticks (1-based, middle of 9)

    -- Dynamic row height based on zoom
    local Theme = C3.UI.Theme
    self.rowHeight = Theme and (Theme:GetEffectiveLineHeight() + 5) or 20
    self.buttons = {}

    self:_createFrame()
end

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

function SuggestionsDropdown:_createFrame()
    -- Main container frame
    self.frame = CreateFrame("Frame", nil, self.parent, "BackdropTemplate")
    self.frame:SetFrameStrata("DIALOG")
    self.frame:SetFrameLevel(100)
    self.frame:Hide()

    -- Dark background with border (using theme colors)
    self.frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    -- Use content background (darkest) for dropdown
    local contentBg = GetThemeColor("content.bg") or { 0.06, 0.07, 0.09, 0.85 }
    local borderColor = GetThemeColor("window.border") or { 0.22, 0.25, 0.31, 0.85 }
    self.frame:SetBackdropColor(contentBg[1], contentBg[2], contentBg[3], contentBg[4] or 0.85)
    self.frame:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4])

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

    -- Pre-create button pool
    for i = 1, self.maxVisible do
        self:_createButton(i)
    end
end

function SuggestionsDropdown:_createButton(index)
    local button = CreateFrame("Button", nil, self.content)
    button:SetHeight(self.rowHeight)
    button:SetPoint("TOPLEFT", 0, -((index - 1) * self.rowHeight))
    button:SetPoint("TOPRIGHT", 0, -((index - 1) * self.rowHeight))

    -- Highlight texture (on hover) - subtle blue tint
    local highlight = button:CreateTexture(nil, "HIGHLIGHT")
    highlight:SetAllPoints()
    highlight:SetColorTexture(0.15, 0.20, 0.28, 0.5)  -- nord1-ish hover

    -- Selection background (subtle tint) - use accent.primary
    button.selectionBg = button:CreateTexture(nil, "BACKGROUND")
    button.selectionBg:SetAllPoints()
    button.selectionBg:SetColorTexture(0.53, 0.75, 0.82, 0.08)  -- nord8 very subtle
    button.selectionBg:Hide()

    -- Selection indicator (left bar) - accent.primary (nord8)
    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.53, 0.75, 0.82, 1)  -- nord8 #88C0D0
    button.selection:Hide()

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

    -- Theme font - match input font size (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

    -- Text - left padding for selection bar + gap, alignment handled by dropdown offset
    button.text = button:CreateFontString(nil, "OVERLAY")
    button.text:SetFont(font, fontSize, "")
    button.text:SetPoint("LEFT", 7, 0)  -- 7px: 3px selection bar + 4px gap
    button.text:SetPoint("RIGHT", -80, 0)
    button.text:SetJustifyH("LEFT")
    button.text:SetTextColor(1, 1, 1)

    -- Description (right side) - use status.dim color (slightly smaller font)
    button.desc = button:CreateFontString(nil, "OVERLAY")
    button.desc:SetFont(font, math.max(fontSize - 1, 8), "")
    button.desc:SetPoint("RIGHT", -6, 0)
    button.desc:SetJustifyH("RIGHT")
    button.desc:SetTextColor(0.30, 0.34, 0.42, 1)  -- nord3 #4C566A

    -- Click handler - use suggestionIndex for actual suggestion lookup
    button:SetScript("OnClick", function()
        if button.suggestionIndex then
            self:_onButtonClick(button.suggestionIndex)
        end
    end)

    -- Hover handler - use suggestionIndex to select the actual suggestion
    button:SetScript("OnEnter", function()
        if button.suggestionIndex then
            -- Don't adjust scroll on hover, just update selection
            self.selectedIndex = button.suggestionIndex
            self:_updateDisplay()
        end
    end)

    self.buttons[index] = button
end

---Update the display based on current scrollOffset and selectedIndex
function SuggestionsDropdown:_updateDisplay()
    local total = #self.suggestions
    if total == 0 then return end

    local visibleCount = math.min(total, self.maxVisible)

    -- 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

    -- Calculate dynamic width
    local maxTextWidth = 0
    local maxDescWidth = 0
    local hasIcons = false

    -- First pass: measure widths for visible suggestions
    for i = 1, visibleCount do
        local suggIdx = self.scrollOffset + i
        local suggestion = self.suggestions[suggIdx]
        if suggestion then
            local button = self.buttons[i]
            button.text:SetText(suggestion.displayText or suggestion.text or "")
            local textWidth = button.text:GetStringWidth()
            if textWidth > maxTextWidth then maxTextWidth = textWidth end

            button.desc:SetText(suggestion.description or "")
            local descWidth = button.desc:GetStringWidth()
            if descWidth > maxDescWidth then maxDescWidth = descWidth end

            if suggestion.icon then hasIcons = true end
        end
    end

    -- Calculate total width
    local iconSpace = hasIcons and 20 or 0
    local gap = 32
    local padding = 10
    local minWidth = 150
    local calculatedWidth = iconSpace + maxTextWidth + gap + maxDescWidth + padding
    local finalWidth = math.max(minWidth, calculatedWidth)
    self.frame:SetWidth(finalWidth)

    local textRightEdge = iconSpace + maxTextWidth

    -- Populate all buttons
    for slot = 1, self.maxVisible do
        local button = self.buttons[slot]
        local suggIdx = self.scrollOffset + slot
        local suggestion = self.suggestions[suggIdx]

        if suggestion then
            button.suggestionIndex = suggIdx
            button:EnableMouse(true)

            -- Icon
            if suggestion.icon then
                button.icon:SetTexture(suggestion.icon)
                button.icon:Show()
                button.text:ClearAllPoints()
                button.text:SetPoint("LEFT", 27, 0)
            else
                button.icon:Hide()
                button.text:ClearAllPoints()
                button.text:SetPoint("LEFT", 7, 0)
            end
            button.text:SetPoint("RIGHT", button, "LEFT", textRightEdge + 7, 0)
            button.text:SetJustifyH("LEFT")

            -- Apply match highlighting if we have a partial query
            -- displayText is what's shown, text is what's inserted on selection
            -- highlightPartial overrides self.partial for highlighting (useful when displayText differs from text)
            local displayText = suggestion.displayText or suggestion.text or ""
            local highlightPartial = suggestion.highlightPartial or (not suggestion.displayText and self.partial) or nil
            if highlightPartial and highlightPartial ~= "" and C3.Intellisense and C3.Intellisense.highlightMatches then
                displayText = C3.Intellisense.highlightMatches(displayText, highlightPartial)
            end
            button.text:SetText(displayText)

            -- Color (only apply base color if no highlighting was done)
            if suggestion.quality and QUALITY_COLORS[suggestion.quality] then
                local c = QUALITY_COLORS[suggestion.quality]
                button.text:SetTextColor(c[1], c[2], c[3])
            else
                button.text:SetTextColor(1, 1, 1)
            end

            -- Description
            button.desc:ClearAllPoints()
            button.desc:SetPoint("LEFT", button, "LEFT", textRightEdge + 32, 0)
            button.desc:SetPoint("RIGHT", -6, 0)
            button.desc:SetText(suggestion.description or "")

            -- Selection
            if suggIdx == self.selectedIndex then
                button.selection:Show()
                button.selectionBg:Show()
            else
                button.selection:Hide()
                button.selectionBg:Hide()
            end

            button:Show()
        else
            button:Hide()
        end
    end

    -- Update frame height based on visible rows
    local height = (visibleCount * self.rowHeight) + 4
    self.frame:SetHeight(height)
    self.content:SetHeight(visibleCount * self.rowHeight)
end

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

---Show the dropdown with suggestions
---@param suggestions table[] Array of { text, description, icon, quality }
---@param anchor frame Frame to anchor below
---@param partial string? Search query for match highlighting
function SuggestionsDropdown:Show(suggestions, anchor, partial)
    if not suggestions or #suggestions == 0 then
        self:Hide()
        return
    end

    self.suggestions = suggestions
    self.selectedIndex = 1
    self.scrollOffset = 0  -- Reset scroll position
    self.anchor = anchor
    self.partial = partial or ""  -- Store for highlighting

    -- Position below anchor (input line), offset to align suggestion text with typed text
    local promptOffset = 0
    if anchor.GetTextInsets then
        local left = anchor:GetTextInsets()
        promptOffset = left or 0
    end
    -- Subtract: frame edge + content padding + text padding (7px) (fine-tuned)
    local totalInternalPadding = 9
    self.frame:ClearAllPoints()
    self.frame:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", promptOffset - totalInternalPadding, -2)

    -- Use the new display update method
    self:_updateDisplay()

    self.frame:Show()
end

---Hide the dropdown
function SuggestionsDropdown:Hide()
    self.frame:Hide()
    self.suggestions = {}
    self.selectedIndex = 1
    self.scrollOffset = 0
end

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

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

---Select a specific index (for mouse hover)
---@param index number
function SuggestionsDropdown:Select(index)
    if not index then return end
    if index < 1 then index = 1 end
    if index > #self.suggestions then index = #self.suggestions end

    self.selectedIndex = index

    -- Adjust scrollOffset to ensure selection is visible and centered when possible
    local total = #self.suggestions
    if total > self.maxVisible then
        -- Calculate ideal scrollOffset to center the selection at stickyRow
        local idealOffset = index - self.stickyRow
        if idealOffset < 0 then idealOffset = 0 end

        local maxOffset = total - self.maxVisible
        if idealOffset > maxOffset then idealOffset = maxOffset end

        self.scrollOffset = idealOffset
    end

    self:_updateDisplay()
end

---Select next suggestion with sticky center behavior
function SuggestionsDropdown:SelectNext()
    local total = #self.suggestions
    if self.selectedIndex >= total then return end  -- Already at bottom

    self.selectedIndex = self.selectedIndex + 1

    -- Only scroll if we have more than maxVisible items
    if total > self.maxVisible then
        -- Current visual row (1-based)
        local currentRow = self.selectedIndex - self.scrollOffset

        -- If selection would be past stickyRow, scroll to keep it at stickyRow
        if currentRow > self.stickyRow then
            local idealOffset = self.selectedIndex - self.stickyRow
            local maxOffset = total - self.maxVisible
            if idealOffset > maxOffset then idealOffset = maxOffset end
            self.scrollOffset = idealOffset
        end
    end

    self:_updateDisplay()
end

---Select previous suggestion with sticky center behavior
function SuggestionsDropdown:SelectPrev()
    if self.selectedIndex <= 1 then return end  -- Already at top

    self.selectedIndex = self.selectedIndex - 1

    -- Only scroll if we have more than maxVisible items
    local total = #self.suggestions
    if total > self.maxVisible then
        -- Current visual row (1-based)
        local currentRow = self.selectedIndex - self.scrollOffset

        -- If selection would be above stickyRow AND we can scroll up, scroll
        if currentRow < self.stickyRow and self.scrollOffset > 0 then
            local idealOffset = self.selectedIndex - self.stickyRow
            if idealOffset < 0 then idealOffset = 0 end
            self.scrollOffset = idealOffset
        end
    end

    self:_updateDisplay()
end

---Get currently selected suggestion
---@return table|nil suggestion
function SuggestionsDropdown:GetSelected()
    return self.suggestions[self.selectedIndex]
end

-------------------------------------------------------------------------------
-- Callbacks
-------------------------------------------------------------------------------

function SuggestionsDropdown:_onButtonClick(index)
    local suggestion = self.suggestions[index]
    if suggestion and self.onSelect then
        self.onSelect(suggestion)
    end
    self:Hide()
end

---Set callback for when a suggestion is selected
---@param callback function(suggestion)
function SuggestionsDropdown:SetOnSelect(callback)
    self.onSelect = callback
end

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

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

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

    -- Update row height
    self.rowHeight = Theme:GetEffectiveLineHeight() + 5

    -- Update all button heights, positions, and fonts
    for i, button in ipairs(self.buttons) do
        button:SetHeight(self.rowHeight)
        button:ClearAllPoints()
        button:SetPoint("TOPLEFT", 0, -((i - 1) * self.rowHeight))
        button:SetPoint("TOPRIGHT", 0, -((i - 1) * self.rowHeight))

        if button.text then
            button.text:SetFont(font, fontSize, "")
        end
        if button.desc then
            button.desc:SetFont(font, math.max(fontSize - 1, 8), "")
        end
    end

    -- Refresh display if visible
    if self.frame:IsVisible() then
        self:_updateDisplay()
    end
end

return SuggestionsDropdown
