local addonName, RF = ...
local T = nil -- set after Theme loads

-- Lazy init theme reference
local function GetTheme()
    if not T then T = RF.Theme end
    return T
end

-------------------------------------------------------------------------------
-- ScrollList: Virtual scrolling list component
-- Only renders visible rows, pools row frames for performance
--
-- Usage:
--   local list = RF:CreateScrollList(parent, opts)
--   list:SetData(dataArray)
--   list:Refresh()
--
-- opts = {
--   rowHeight    = 22,
--   createRow    = function(parent, index) -> rowFrame,
--   updateRow    = function(rowFrame, data, index, isSelected),
--   onRowClick   = function(rowFrame, data, button),
--   onRowEnter   = function(rowFrame, data),
--   onRowLeave   = function(rowFrame, data),
-- }
-------------------------------------------------------------------------------
function RF:CreateScrollList(parent, opts)
    local T = GetTheme()
    opts = opts or {}
    local ROW_HEIGHT = opts.rowHeight or 22

    local container = CreateFrame("Frame", nil, parent)
    container.rows = {}
    container.data = {}
    container.filteredData = nil
    container.offset = 0
    container.selectedIndex = nil

    -- Scroll bar
    local scrollBar = CreateFrame("Slider", nil, container, "BackdropTemplate")
    scrollBar:SetWidth(12)
    scrollBar:SetPoint("TOPRIGHT", 0, 0)
    scrollBar:SetPoint("BOTTOMRIGHT", 0, 0)
    scrollBar:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    scrollBar:SetBackdropColor(0.06, 0.06, 0.08, 1)
    scrollBar:SetBackdropBorderColor(unpack(T.border))
    scrollBar:SetOrientation("VERTICAL")
    scrollBar:SetMinMaxValues(0, 1)
    scrollBar:SetValue(0)
    scrollBar:SetValueStep(1)
    scrollBar:SetObeyStepOnDrag(true)
    container.scrollBar = scrollBar

    -- Thumb texture
    local thumb = scrollBar:CreateTexture(nil, "OVERLAY")
    thumb:SetColorTexture(unpack(T.accent))
    thumb:SetSize(10, 40)
    scrollBar:SetThumbTexture(thumb)

    -- Content area (left of scrollbar)
    local content = CreateFrame("Frame", nil, container)
    content:SetPoint("TOPLEFT", 0, 0)
    content:SetPoint("BOTTOMRIGHT", scrollBar, "BOTTOMLEFT", -2, 0)
    content:SetClipsChildren(true)
    container.content = content

    -- Calculate visible rows
    local function GetVisibleRows()
        local h = container:GetHeight()
        return math.floor(h / ROW_HEIGHT) + 1
    end

    -- Get data source (filtered or full)
    local function GetDataSource()
        return container.filteredData or container.data
    end

    -- Create/get row frame from pool
    local function GetRow(index)
        if container.rows[index] then
            return container.rows[index]
        end
        local row
        if opts.createRow then
            row = opts.createRow(content, index)
        else
            row = CreateFrame("Button", nil, content)
        end
        row:SetHeight(ROW_HEIGHT)
        row:SetPoint("TOPLEFT", 0, -((index - 1) * ROW_HEIGHT))
        row:SetPoint("TOPRIGHT", 0, -((index - 1) * ROW_HEIGHT))

        -- Row background
        if not row.bg then
            row.bg = row:CreateTexture(nil, "BACKGROUND")
            row.bg:SetAllPoints()
        end

        -- Enable right-click and set click handler
        row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
        row:SetScript("OnClick", function(self, button)
            if opts.onRowClick then
                opts.onRowClick(self, self.dataEntry, button)
            end
        end)

        -- Hover
        row:SetScript("OnEnter", function(self)
            if self.dataEntry then
                self.bg:SetColorTexture(unpack(T.bgRowHover))
                if opts.onRowEnter then
                    opts.onRowEnter(self, self.dataEntry)
                end
            end
        end)
        row:SetScript("OnLeave", function(self)
            if self.dataEntry then
                local isSelected = (container.selectedIndex and
                    self.dataIndex == container.selectedIndex)
                if isSelected then
                    self.bg:SetColorTexture(unpack(T.bgRowSelected))
                else
                    local bgColor = (self.dataIndex % 2 == 0) and T.bgRowAlt or T.bgRow
                    self.bg:SetColorTexture(unpack(bgColor))
                end
                if opts.onRowLeave then
                    opts.onRowLeave(self, self.dataEntry)
                end
            end
        end)

        container.rows[index] = row
        return row
    end

    -- Refresh display
    function container:Refresh()
        local data = GetDataSource()
        local numData = #data
        local numVisible = GetVisibleRows()
        local maxOffset = math.max(0, numData - numVisible + 1)

        -- Update scrollbar
        if maxOffset > 0 then
            scrollBar:SetMinMaxValues(0, maxOffset)
            scrollBar:Show()
        else
            scrollBar:SetMinMaxValues(0, 1)
            scrollBar:SetValue(0)
            container.offset = 0
            scrollBar:Hide()
        end

        -- Update rows
        for i = 1, numVisible do
            local row = GetRow(i)
            local dataIndex = i + container.offset
            if dataIndex <= numData then
                local entry = data[dataIndex]
                row.dataEntry = entry
                row.dataIndex = dataIndex
                row:Show()

                -- Alternating row color
                local isSelected = (container.selectedIndex == dataIndex)
                if isSelected then
                    row.bg:SetColorTexture(unpack(T.bgRowSelected))
                else
                    local bgColor = (dataIndex % 2 == 0) and T.bgRowAlt or T.bgRow
                    row.bg:SetColorTexture(unpack(bgColor))
                end

                if opts.updateRow then
                    opts.updateRow(row, entry, dataIndex, isSelected)
                end
            else
                row.dataEntry = nil
                row.dataIndex = nil
                row:Hide()
            end
        end

        -- Hide excess rows
        for i = numVisible + 1, #container.rows do
            container.rows[i]:Hide()
        end
    end

    -- Scroll handler
    scrollBar:SetScript("OnValueChanged", function(self, value)
        container.offset = math.floor(value)
        container:Refresh()
    end)

    -- Mouse wheel
    container:EnableMouseWheel(true)
    container:SetScript("OnMouseWheel", function(self, delta)
        local newVal = scrollBar:GetValue() - (delta * 3)
        local min, max = scrollBar:GetMinMaxValues()
        newVal = math.max(min, math.min(max, newVal))
        scrollBar:SetValue(newVal)
    end)

    -- Also enable mousewheel on content area
    content:EnableMouseWheel(true)
    content:SetScript("OnMouseWheel", function(self, delta)
        local newVal = scrollBar:GetValue() - (delta * 3)
        local min, max = scrollBar:GetMinMaxValues()
        newVal = math.max(min, math.min(max, newVal))
        scrollBar:SetValue(newVal)
    end)

    -- Public API
    function container:SetData(data)
        container.data = data or {}
        container.filteredData = nil
        container.offset = 0
        scrollBar:SetValue(0)
        container:Refresh()
    end

    function container:SetFilteredData(data)
        container.filteredData = data
        container.offset = 0
        scrollBar:SetValue(0)
        container:Refresh()
    end

    function container:ClearFilter()
        container.filteredData = nil
        container.offset = 0
        scrollBar:SetValue(0)
        container:Refresh()
    end

    function container:GetSelectedData()
        if not container.selectedIndex then return nil end
        local data = GetDataSource()
        return data[container.selectedIndex]
    end

    function container:SetSelected(index)
        container.selectedIndex = index
        container:Refresh()
    end

    function container:ClearSelection()
        container.selectedIndex = nil
        container:Refresh()
    end

    function container:ScrollToTop()
        scrollBar:SetValue(0)
    end

    function container:GetScrollOffset()
        return container.offset or 0
    end

    function container:SetScrollOffset(offset)
        offset = offset or 0
        local _, maxVal = scrollBar:GetMinMaxValues()
        offset = math.max(0, math.min(maxVal or 0, offset))
        scrollBar:SetValue(offset)
    end

    function container:GetDataCount()
        local data = GetDataSource()
        return #data
    end

    -- Handle resize
    container:SetScript("OnSizeChanged", function()
        container:Refresh()
    end)

    return container
end

-------------------------------------------------------------------------------
-- SearchBar: EditBox with placeholder text and debounce
-------------------------------------------------------------------------------
function RF:CreateSearchBar(parent, opts)
    local T = GetTheme()
    opts = opts or {}
    local width = opts.width or 200
    local height = opts.height or 26

    local frame = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    frame:SetSize(width, height)
    frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    frame:SetBackdropColor(0.10, 0.10, 0.12, 1)
    frame:SetBackdropBorderColor(unpack(T.border))

    -- Search icon
    local icon = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    icon:SetPoint("LEFT", 6, 0)
    icon:SetText("|cFF666666Q|r") -- simple text icon
    icon:SetFont(icon:GetFont(), 12, "")

    -- EditBox
    local editBox = CreateFrame("EditBox", nil, frame)
    editBox:SetPoint("LEFT", 22, 0)
    editBox:SetPoint("RIGHT", -6, 0)
    editBox:SetHeight(height)
    editBox:SetFontObject("ChatFontNormal")
    editBox:SetFont(editBox:GetFont(), RF.FONT_SIZES.normal, "")
    editBox:SetTextColor(unpack(T.textPrimary))
    editBox:SetAutoFocus(false)
    editBox:SetMaxLetters(100)
    frame.editBox = editBox

    -- Placeholder
    local placeholder = editBox:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    placeholder:SetPoint("LEFT", 0, 0)
    placeholder:SetFont(placeholder:GetFont(), RF.FONT_SIZES.normal, "")
    placeholder:SetTextColor(unpack(T.textMuted))
    placeholder:SetText(opts.placeholder or "Search...")
    frame.placeholder = placeholder

    -- Debounce timer
    local debounceTimer = nil
    local debounceDelay = opts.debounce or 0.2

    editBox:SetScript("OnTextChanged", function(self, userInput)
        local text = self:GetText()
        if text and text ~= "" then
            placeholder:Hide()
        else
            placeholder:Show()
        end
        if userInput and opts.onTextChanged then
            if debounceTimer then
                debounceTimer:Cancel()
            end
            debounceTimer = C_Timer.NewTimer(debounceDelay, function()
                opts.onTextChanged(text)
            end)
        end
    end)

    editBox:SetScript("OnEscapePressed", function(self)
        self:SetText("")
        self:ClearFocus()
        if opts.onTextChanged then
            opts.onTextChanged("")
        end
    end)

    editBox:SetScript("OnEnterPressed", function(self)
        self:ClearFocus()
    end)

    -- Focus highlight
    editBox:SetScript("OnEditFocusGained", function()
        frame:SetBackdropBorderColor(unpack(T.accent))
    end)
    editBox:SetScript("OnEditFocusLost", function()
        frame:SetBackdropBorderColor(unpack(T.border))
    end)

    -- Public API
    function frame:GetText()
        return editBox:GetText()
    end

    function frame:SetText(text)
        editBox:SetText(text or "")
    end

    function frame:ClearText()
        editBox:SetText("")
    end

    function frame:SetFocus()
        editBox:SetFocus()
    end

    return frame
end

-------------------------------------------------------------------------------
-- TabBar: Horizontal tabs with accent underline
-------------------------------------------------------------------------------
function RF:CreateTabBar(parent, tabs, onTabSelected)
    local T = GetTheme()
    local tabBar = CreateFrame("Frame", nil, parent)
    tabBar:SetHeight(32)
    tabBar.tabs = {}
    tabBar.activeTab = nil

    local xOffset = 0
    for i, tabDef in ipairs(tabs) do
        local tab = CreateFrame("Button", nil, tabBar)
        tab:SetHeight(32)
        tab.tabKey = tabDef.key or tabDef.label

        local label = tab:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        label:SetFont(label:GetFont(), RF.FONT_SIZES.medium, "")
        label:SetText(tabDef.label)
        label:SetPoint("CENTER", 0, 0)
        tab.label = label

        -- Size to text
        local textWidth = label:GetStringWidth() + 24
        tab:SetWidth(textWidth)
        tab:SetPoint("BOTTOMLEFT", xOffset, 0)
        xOffset = xOffset + textWidth

        -- Underline (accent indicator)
        local underline = tab:CreateTexture(nil, "OVERLAY")
        underline:SetHeight(2)
        underline:SetPoint("BOTTOMLEFT", 0, 0)
        underline:SetPoint("BOTTOMRIGHT", 0, 0)
        underline:SetColorTexture(unpack(T.accent))
        underline:Hide()
        tab.underline = underline

        -- Default style
        label:SetTextColor(unpack(T.textSecondary))

        tab:SetScript("OnClick", function()
            tabBar:SetActive(tab.tabKey)
            if onTabSelected then
                onTabSelected(tab.tabKey)
            end
        end)

        tab:SetScript("OnEnter", function()
            if tabBar.activeTab ~= tab.tabKey then
                label:SetTextColor(unpack(T.textPrimary))
            end
        end)

        tab:SetScript("OnLeave", function()
            if tabBar.activeTab ~= tab.tabKey then
                label:SetTextColor(unpack(T.textSecondary))
            end
        end)

        tabBar.tabs[tab.tabKey] = tab
    end

    function tabBar:SetActive(key)
        tabBar.activeTab = key
        for k, tab in pairs(tabBar.tabs) do
            if k == key then
                tab.label:SetTextColor(unpack(T.textHighlight))
                tab.underline:Show()
            else
                tab.label:SetTextColor(unpack(T.textSecondary))
                tab.underline:Hide()
            end
        end
    end

    return tabBar
end

-------------------------------------------------------------------------------
-- ContextMenu: Wraps MenuUtil.CreateContextMenu (WoW 11.0+)
-------------------------------------------------------------------------------
function RF:ShowContextMenu(owner, menuItems)
    if not MenuUtil or not MenuUtil.CreateContextMenu then
        -- Fallback for older clients
        RF:Print("Context menus require WoW 11.0+")
        return
    end

    MenuUtil.CreateContextMenu(owner, function(ownerRegion, rootDescription)
        for _, item in ipairs(menuItems) do
            if item.separator then
                rootDescription:CreateDivider()
            elseif item.isTitle then
                if rootDescription.CreateTitle then
                    rootDescription:CreateTitle(item.text)
                else
                    local title = rootDescription:CreateButton(item.text)
                    title:SetEnabled(false)
                end
            elseif item.submenu then
                local sub = rootDescription:CreateButton(item.text)
                for _, subItem in ipairs(item.submenu) do
                    sub:CreateButton(subItem.text, subItem.func)
                end
            else
                local btn = rootDescription:CreateButton(item.text, item.func)
                if item.disabled then
                    btn:SetEnabled(false)
                end
                if item.color then
                    btn:AddInitializer(function(button)
                        local fontString = button.fontString
                        if fontString then
                            local c = item.color
                            if c.r then
                                fontString:SetTextColor(c.r, c.g, c.b)
                            elseif c[1] then
                                fontString:SetTextColor(c[1], c[2], c[3])
                            end
                        end
                    end)
                end
            end
        end
    end)
end

-------------------------------------------------------------------------------
-- PillButton: Rounded filter toggle chip
-------------------------------------------------------------------------------
function RF:CreatePillButton(parent, text, opts)
    local T = GetTheme()
    opts = opts or {}
    local pill = CreateFrame("Button", nil, parent, "BackdropTemplate")
    pill:SetHeight(opts.height or 22)

    local label = pill:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetFont(label:GetFont(), RF.FONT_SIZES.small, "")
    label:SetText(text)
    label:SetPoint("CENTER", 0, 0)
    pill.label = label

    local textWidth = label:GetStringWidth() + 16
    pill:SetWidth(math.max(textWidth, 40))

    pill:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })

    pill.active = false

    local function UpdateStyle()
        if pill.active then
            pill:SetBackdropColor(unpack(T.accent))
            pill:SetBackdropBorderColor(unpack(T.accent))
            label:SetTextColor(unpack(T.textHighlight))
        else
            pill:SetBackdropColor(unpack(T.bgTertiary))
            pill:SetBackdropBorderColor(unpack(T.border))
            label:SetTextColor(unpack(T.textSecondary))
        end
    end

    function pill:SetActive(active)
        pill.active = active
        UpdateStyle()
    end

    function pill:IsActive()
        return pill.active
    end

    function pill:Toggle()
        pill.active = not pill.active
        UpdateStyle()
    end

    pill:SetScript("OnEnter", function()
        if not pill.active then
            pill:SetBackdropColor(0.20, 0.20, 0.24, 1)
        end
    end)
    pill:SetScript("OnLeave", function()
        UpdateStyle()
    end)

    UpdateStyle()
    return pill
end

-------------------------------------------------------------------------------
-- Dropdown: Simple dropdown selector
-------------------------------------------------------------------------------
function RF:CreateDropdown(parent, opts)
    local T = GetTheme()
    opts = opts or {}
    local width = opts.width or 120
    local height = opts.height or 24

    local frame = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    frame:SetSize(width, height)
    frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    frame:SetBackdropColor(unpack(T.bgTertiary))
    frame:SetBackdropBorderColor(unpack(T.border))

    local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetFont(label:GetFont(), RF.FONT_SIZES.small, "")
    label:SetPoint("LEFT", 6, 0)
    label:SetPoint("RIGHT", -16, 0)
    label:SetJustifyH("LEFT")
    label:SetTextColor(unpack(T.textPrimary))
    label:SetText(opts.defaultText or "Select...")
    frame.label = label

    local arrow = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    arrow:SetFont(arrow:GetFont(), 10, "")
    arrow:SetPoint("RIGHT", -4, 0)
    arrow:SetText("v")
    arrow:SetTextColor(unpack(T.textMuted))

    frame.value = opts.defaultValue
    frame.items = opts.items or {}

    local btn = CreateFrame("Button", nil, frame)
    btn:SetAllPoints()
    btn:SetScript("OnClick", function()
        local menuItems = {}
        for _, item in ipairs(frame.items) do
            tinsert(menuItems, {
                text = item.label,
                func = function()
                    frame.value = item.value
                    label:SetText(item.label)
                    if opts.onChanged then
                        opts.onChanged(item.value, item.label)
                    end
                end,
            })
        end
        RF:ShowContextMenu(btn, menuItems)
    end)

    function frame:SetValue(value)
        frame.value = value
        for _, item in ipairs(frame.items) do
            if item.value == value then
                label:SetText(item.label)
                return
            end
        end
    end

    function frame:GetValue()
        return frame.value
    end

    function frame:SetItems(items)
        frame.items = items
    end

    return frame
end
