local API = rawget(_G, "LIDCOUNCIL_API")
local INTERNAL_API = type(API) == "table" and type(API.__internal) == "table" and API.__internal or nil
if type(INTERNAL_API) ~= "table" then
  error("LidCouncil missing internal API registry.")
end

INTERNAL_API.LIDCOUNCIL_CreatePanelIconPickerHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}

  local editor = deps.editor
  local ApplyFrameSkin = deps.ApplyFrameSkin
  local HideOtherPickers = deps.HideOtherPickers
  local LogSilent = deps.LogSilent

  local function BuildIconList(force)
    if editor and editor.iconListCache and not force then
      return editor.iconListCache
    end
    local iconNames = _G.LIDCOUNCIL_ICON_FILE_NAMES
    local api = _G.LIDCOUNCIL_API
    if type(iconNames) ~= "table" and type(api) == "table" then
      iconNames = api.LIDCOUNCIL_ICON_FILE_NAMES
    end
    local list, seen = {}, {}

    local function labelFromTexture(texture)
      local s = tostring(texture or "")
      local name = s:match("([^\\]+)$") or s
      local num = tonumber(name)
      if num and iconNames and iconNames[num] then
        return iconNames[num]
      end
      return name
    end

    local function add(label, texture, cat)
      if not label or label == "" or not texture then return end
      local key = tostring(texture) .. ":" .. tostring(label)
      if seen[key] then return end
      seen[key] = true
      list[#list + 1] = {
        label = label,
        texture = texture,
        search = string.lower(label),
        cat = cat or "ALL",
      }
    end

    if type(iconNames) == "table" then
      for id, name in pairs(iconNames) do
        add(tostring(name), tonumber(id) or id, "ALL")
      end
    elseif LogSilent and not editor.__iconNamesWarned then
      editor.__iconNamesWarned = true
      LogSilent("LIDCOUNCIL icon name table missing; icon picker limited to macro icons.")
    end

    local macroIconsFn = (C_Macro and C_Macro.GetMacroIcons) or GetMacroIcons
    local macroItemIconsFn = (C_Macro and C_Macro.GetMacroItemIcons) or GetMacroItemIcons
    if macroIconsFn then
      for _, tex in ipairs(macroIconsFn()) do
        add(labelFromTexture(tex), tex, "SPELL")
      end
    end
    if macroItemIconsFn then
      for _, tex in ipairs(macroItemIconsFn()) do
        add(labelFromTexture(tex), tex, "ITEM")
      end
    end

    table.sort(list, function(a, b) return a.label < b.label end)
    editor.iconListCache = list
    return list
  end

  local function ShowIconPicker(onSelect)
    HideOtherPickers("iconPicker")
    if not editor.iconPicker then
      local picker = CreateFrame("Frame", "LIDCOUNCIL_IconPicker", UIParent, "BackdropTemplate")
      picker:SetFrameStrata("DIALOG")
      picker:SetSize(760, 480)
      picker:SetPoint("CENTER")
      picker:SetClampedToScreen(true)
      picker:SetMovable(true)
      picker:EnableMouse(true)
      picker:RegisterForDrag("LeftButton")
      picker:SetScript("OnDragStart", function(self) self:StartMoving() end)
      picker:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
      ApplyFrameSkin(picker)

      local title = picker:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
      title:SetPoint("TOPLEFT", 12, -10)
      title:SetText("Select Icon")

      local close = CreateFrame("Button", nil, picker, "UIPanelCloseButton")
      close:SetPoint("TOPRIGHT", -2, -2)
      close:SetScript("OnClick", function() picker:Hide() end)

      local filterFrame = CreateFrame("Frame", nil, picker, "BackdropTemplate")
      filterFrame:SetPoint("TOPLEFT", 12, -64)
      filterFrame:SetPoint("BOTTOMLEFT", 12, 42)
      filterFrame:SetWidth(110)
      ApplyFrameSkin(filterFrame)
      picker.filterFrame = filterFrame

      local catTitle = filterFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      catTitle:SetPoint("TOPLEFT", 10, -6)
      catTitle:SetText("Categories")
      picker.catTitle = catTitle

      local subFilterFrame = CreateFrame("Frame", nil, picker, "BackdropTemplate")
      subFilterFrame:SetPoint("TOPLEFT", filterFrame, "TOPRIGHT", 8, 0)
      subFilterFrame:SetPoint("BOTTOMLEFT", filterFrame, "BOTTOMRIGHT", 8, 0)
      subFilterFrame:SetWidth(160)
      ApplyFrameSkin(subFilterFrame)
      picker.subFilterFrame = subFilterFrame

      local catScroll = CreateFrame("ScrollFrame", nil, filterFrame, "UIPanelScrollFrameTemplate")
      catScroll:SetPoint("TOPLEFT", 6, -24)
      catScroll:SetPoint("BOTTOMRIGHT", -22, 6)
      filterFrame.catScroll = catScroll

      local catContent = CreateFrame("Frame", nil, catScroll)
      catContent:SetPoint("TOPLEFT", 0, 0)
      catContent:SetSize(1, 1)
      catScroll:SetScrollChild(catContent)
      filterFrame.catContent = catContent

      local subScroll = CreateFrame("ScrollFrame", nil, subFilterFrame, "UIPanelScrollFrameTemplate")
      subScroll:SetPoint("TOPLEFT", 6, -6)
      subScroll:SetPoint("BOTTOMRIGHT", -22, 6)
      subFilterFrame.subScroll = subScroll

      local subContent = CreateFrame("Frame", nil, subScroll)
      subContent:SetPoint("TOPLEFT", 0, 0)
      subContent:SetSize(1, 1)
      subScroll:SetScrollChild(subContent)
      subFilterFrame.subContent = subContent

      local categoryButtons = {}
      picker.categoryButtons = categoryButtons
      editor.iconCategoryButtons = categoryButtons

      local subButtons = {}
      picker.subButtons = subButtons
      editor.iconSubButtons = subButtons

      local categories = {
        { key = "ALL", label = "All" },
        { key = "SPELLS", label = "Spells" },
        { key = "ITEMS", label = "Items" },
        { key = "CLASSES", label = "Classes" },
        { key = "PROF", label = "Profession" },
        { key = "EXP", label = "Expansion" },
        { key = "MISC", label = "Misc" },
        { key = "TEX", label = "Textures" },
      }

      local function EnsureCatButton(idx)
        local b = categoryButtons[idx]
        if not b then
          b = CreateFrame("Button", nil, catContent, "UIPanelButtonTemplate")
          b:SetSize(90, 22)
          categoryButtons[idx] = b
        end
        return b
      end

      local y = -4
      for i, cat in ipairs(categories) do
        local b = EnsureCatButton(i)
        b:ClearAllPoints()
        b:SetPoint("TOPLEFT", 0, y)
        b:SetText(cat.label)
        b.__key = cat.key
        y = y - 26
      end
      catContent:SetSize(90, math.max(1, -y + 6))

      local subTitle = subFilterFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      subTitle:SetPoint("TOPLEFT", 10, -6)
      subTitle:SetText("Preset Filters")
      picker.subTitle = subTitle

      local function EnsureSubButton(idx)
        local b = subButtons[idx]
        if not b then
          b = CreateFrame("Button", nil, subContent, "UIPanelButtonTemplate")
          b:SetSize(120, 22)
          subButtons[idx] = b
        end
        return b
      end

      if editor.ApplyTheme then editor:ApplyTheme() end

      local iconFrame = CreateFrame("Frame", nil, picker, "BackdropTemplate")
      iconFrame:SetPoint("TOPLEFT", subFilterFrame, "TOPRIGHT", 8, 0)
      iconFrame:SetPoint("BOTTOMRIGHT", -12, 42)
      ApplyFrameSkin(iconFrame)
      picker.iconFrame = iconFrame

      local scroll = CreateFrame("ScrollFrame", nil, iconFrame, "UIPanelScrollFrameTemplate")
      scroll:SetPoint("TOPLEFT", 6, -34)
      scroll:SetPoint("BOTTOMRIGHT", -28, 32)
      scroll:EnableMouseWheel(true)
      picker.scroll = scroll

      local content = CreateFrame("Frame", nil, scroll)
      content:SetPoint("TOPLEFT", 0, 0)
      content:SetSize(1, 1)
      scroll:SetScrollChild(content)
      picker.content = content

      picker.buttons = {}
      picker.iconList = BuildIconList()
      picker.filter = "ALL"
      picker.filterCache = {}
      picker.pageSize = 130
      picker.page = 1
      picker.search = ""

      local searchBox = CreateFrame("EditBox", nil, picker, "InputBoxTemplate")
      searchBox:SetSize(260, 20)
      searchBox:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 8, -8)
      searchBox:SetAutoFocus(false)
      searchBox:SetText("")
      picker.searchBox = searchBox

      local searchLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      searchLabel:SetPoint("RIGHT", searchBox, "LEFT", -6, 0)
      searchLabel:SetText("Search:")

      local prevBtn = CreateFrame("Button", nil, picker, "UIPanelButtonTemplate")
      prevBtn:SetSize(28, 20)
      prevBtn:SetPoint("BOTTOMLEFT", iconFrame, "BOTTOMLEFT", 6, 6)
      prevBtn:SetText("<")
      picker.prevBtn = prevBtn
      editor.iconPrevBtn = prevBtn

      local nextBtn = CreateFrame("Button", nil, picker, "UIPanelButtonTemplate")
      nextBtn:SetSize(28, 20)
      nextBtn:SetPoint("LEFT", prevBtn, "RIGHT", 6, 0)
      nextBtn:SetText(">")
      picker.nextBtn = nextBtn
      editor.iconNextBtn = nextBtn

      local pageLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      pageLabel:SetPoint("LEFT", nextBtn, "RIGHT", 10, 0)
      pageLabel:SetText("Page 1 / 1")
      picker.pageLabel = pageLabel

      local pageEdit = CreateFrame("EditBox", nil, picker, "InputBoxTemplate")
      pageEdit:SetSize(40, 20)
      pageEdit:SetPoint("LEFT", pageLabel, "RIGHT", 8, 0)
      pageEdit:SetAutoFocus(false)
      pageEdit:SetNumeric(true)
      pageEdit:SetText("1")
      picker.pageEdit = pageEdit

      local function EnsureButton(idx, size)
        local b = picker.buttons[idx]
        if not b then
          b = CreateFrame("Button", nil, content)
          b:SetSize(size, size)
          local t = b:CreateTexture(nil, "ARTWORK")
          t:SetAllPoints(true)
          b.tex = t
          b:SetScript("OnEnter", function(self)
            if self.__label then
              GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
              GameTooltip:SetText(self.__label)
              GameTooltip:Show()
            end
          end)
          b:SetScript("OnLeave", function() GameTooltip:Hide() end)
          picker.buttons[idx] = b
        end
        return b
      end

      local SUBFILTERS = {
        CLASSES = {
          { label = "Warrior", keywords = { "warrior" } },
          { label = "Hunter", keywords = { "hunter" } },
          { label = "Priest", keywords = { "priest" } },
          { label = "Mage", keywords = { "mage" } },
          { label = "Monk", keywords = { "monk" } },
          { label = "Demonhunter", keywords = { "demonhunter", "demon_hunter" } },
          { label = "Evoker", keywords = { "evoker" } },
          { label = "Paladin", keywords = { "paladin" } },
          { label = "Rogue", keywords = { "rogue" } },
          { label = "Shaman", keywords = { "shaman" } },
          { label = "Warlock", keywords = { "warlock" } },
          { label = "Druid", keywords = { "druid" } },
          { label = "Deathknight", keywords = { "deathknight", "death_knight" } },
        },
        PROF = {
          { label = "Blacksmith", keywords = { "blacksmith", "blacksmithing" } },
          { label = "Inscription", keywords = { "inscription", "inscribe" } },
          { label = "Enchantment", keywords = { "enchant", "enchanting" } },
          { label = "Jewelry", keywords = { "jewel", "jewelcraft" } },
          { label = "Leatherworking", keywords = { "leatherwork", "leatherworking" } },
          { label = "Skinning", keywords = { "skinning" } },
          { label = "Fishing", keywords = { "fishing" } },
          { label = "Tailoring", keywords = { "tailor", "tailoring" } },
          { label = "Cooking", keywords = { "cooking" } },
          { label = "Engineering", keywords = { "engineer", "engineering" } },
          { label = "Mining", keywords = { "mining" } },
        },
        EXP = {
          { label = "Ardenweald", keywords = { "ardenweald" } },
          { label = "Revendreth", keywords = { "revendreth" } },
          { label = "Maldraxxus", keywords = { "maldraxxus" } },
          { label = "Kyrian", keywords = { "kyrian" } },
        },
        MISC = {
          { label = "PVP", keywords = { "achievement_pvp" } },
          { label = "Challengemode", keywords = { "achievement_challengemode" } },
          { label = "Zone", keywords = { "achievement_zone" } },
          { label = "Mounts", keywords = { "mount" } },
          { label = "Pets", keywords = { "pet" } },
        },
        TEX = {
          { label = "Walls", keywords = { "wall" } },
          { label = "Stairs", keywords = { "stair" } },
          { label = "Iron", keywords = { "iron" } },
          { label = "Leather", keywords = { "leather" } },
          { label = "Garrison", keywords = { "garrison" } },
        },
      }

      local SPELL_KEYWORDS = { "ability_", "spell_", "spell", "magic" }
      local ITEM_KEYWORDS = { "inv_", "item_", "artifact", "trade_" }

      local function FilterByKeywords(list, keywords)
        if not keywords then return {} end
        local filtered = {}
        for _, icon in ipairs(list) do
          local s = icon.search
          if s then
            for _, k in ipairs(keywords) do
              if s:find(k, 1, true) then
                filtered[#filtered + 1] = icon
                break
              end
            end
          end
        end
        return filtered
      end

      local RefreshResults

      local function RenderSubFilters(catKey)
        local list = SUBFILTERS[catKey]
        if not list then
          picker.subFilterKeywords = nil
          picker.subFilterLabel = nil
          subTitle:SetText("Filters")
          for i = 1, #subButtons do
            subButtons[i]:Hide()
          end
          return
        end

        subTitle:SetText("Filters")
        local yPos = -34
        for i, entry in ipairs(list) do
          local b = EnsureSubButton(i)
          b:ClearAllPoints()
          b:SetPoint("TOPLEFT", 10, yPos)
          b:SetText(entry.label)
          b.__keywords = entry.keywords
          b:SetScript("OnClick", function()
            picker.subFilterKeywords = entry.keywords
            picker.subFilterLabel = entry.label
            if RefreshResults then RefreshResults(true) end
          end)
          b:Show()
          yPos = yPos - 26
        end
        for i = #list + 1, #subButtons do
          subButtons[i]:Hide()
        end
        picker.subFilterKeywords = list[1] and list[1].keywords or nil
        picker.subFilterLabel = list[1] and list[1].label or nil
        subContent:SetSize(120, math.max(1, -yPos + 6))
        if editor.ApplyTheme then editor:ApplyTheme() end
      end

      local function GetFilteredList(cat)
        local base = picker.iconList or {}
        if not cat or cat == "ALL" then
          return base
        end
        if cat == "SPELLS" then
          return FilterByKeywords(base, SPELL_KEYWORDS)
        end
        if cat == "ITEMS" then
          return FilterByKeywords(base, ITEM_KEYWORDS)
        end
        if picker.subFilterKeywords then
          return FilterByKeywords(base, picker.subFilterKeywords)
        end
        return {}
      end

      local function BuildSearchList(list, term)
        if not term or term == "" then return list end
        local filtered = {}
        for _, icon in ipairs(list) do
          if icon.search and icon.search:find(term, 1, true) then
            filtered[#filtered + 1] = icon
          end
        end
        return filtered
      end

      local function GetFilterCacheKey()
        local subKey = ""
        if picker.filter ~= "ALL" and picker.filter ~= "SPELLS" and picker.filter ~= "ITEMS" then
          subKey = tostring(picker.subFilterLabel or "")
        end
        return table.concat({
          tostring(picker.filter or "ALL"),
          subKey,
          tostring(picker.search or ""),
        }, "\31")
      end

      local function LayoutPage(results)
        local availW = (scroll:GetWidth() or 1) - 12
        local availH = (scroll:GetHeight() or 1) - 12
        local rows, cols = 13, 10
        local pad = 4
        local size = math.max(10, math.floor((availW - pad * (cols - 1)) / cols))
        local rowH = size + pad
        local visibleRows = math.max(1, math.floor(availH / rowH))
        if visibleRows < rows then rows = visibleRows end
        local effectivePageSize = rows * cols
        if effectivePageSize < 1 then effectivePageSize = cols end
        picker.pageSize = effectivePageSize
        local neededH = rows * rowH + 6
        local startIndex = (picker.page - 1) * picker.pageSize + 1
        local x0, y0 = 6, -6
        for i = 1, picker.pageSize do
          local icon = results[startIndex + i - 1]
          local b = EnsureButton(i, size)
          if icon then
            local col = (i - 1) % cols
            local row = math.floor((i - 1) / cols)
            b:ClearAllPoints()
            b:SetPoint("TOPLEFT", x0 + col * (size + pad), y0 - row * rowH)
            b.tex:SetTexture(icon.texture)
            b.__label = icon.label
            b:SetScript("OnClick", function()
              if picker.onSelect then picker.onSelect(icon) end
              picker:Hide()
            end)
            b:Show()
          else
            b:Hide()
          end
        end
        content:SetSize(availW, neededH)
        scroll:SetVerticalScroll(0)
      end

      RefreshResults = function(resetPage)
        if resetPage then picker.page = 1 end
        local cacheKey = GetFilterCacheKey()
        local results = picker.filterCache[cacheKey]
        if not results then
          local filtered = GetFilteredList(picker.filter)
          results = BuildSearchList(filtered, picker.search)
          picker.filterCache[cacheKey] = results
        end
        local total = #results
        local pages = math.max(1, math.ceil(total / math.max(1, picker.pageSize)))
        if picker.page > pages then picker.page = pages end
        if picker.page < 1 then picker.page = 1 end
        pageLabel:SetText("Page " .. picker.page .. " / " .. pages)
        if pageEdit and pageEdit.HasFocus and not pageEdit:HasFocus() then
          pageEdit:SetText(tostring(picker.page))
        end
        LayoutPage(results)
      end

      for _, b in ipairs(categoryButtons) do
        b:SetScript("OnClick", function()
          picker.filter = b.__key
          RenderSubFilters(picker.filter)
          RefreshResults(true)
        end)
      end

      searchBox:SetScript("OnTextChanged", function(self)
        picker.search = string.lower(self:GetText() or "")
        RefreshResults(true)
      end)
      searchBox:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)

      prevBtn:SetScript("OnClick", function()
        picker.page = math.max(1, picker.page - 1)
        RefreshResults(false)
      end)
      nextBtn:SetScript("OnClick", function()
        picker.page = picker.page + 1
        RefreshResults(false)
      end)
      pageEdit:SetScript("OnEnterPressed", function(self)
        local v = tonumber(self:GetText() or "")
        if v then picker.page = v end
        RefreshResults(false)
        self:ClearFocus()
      end)
      pageEdit:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)

      scroll:EnableMouseWheel(true)
      if scroll.ScrollBar then scroll.ScrollBar:Show() end
      scroll:HookScript("OnSizeChanged", function() RefreshResults(false) end)

      picker.RefreshResults = RefreshResults
      picker:HookScript("OnShow", function() RefreshResults(true) end)
      RefreshResults(true)
      editor.iconPicker = picker
      if editor.ApplyTheme then editor:ApplyTheme() end
    end

    editor.iconPicker.onSelect = onSelect
    editor.iconPicker.iconList = BuildIconList()
    editor.iconPicker.filterCache = {}
    editor.iconPicker:Show()
    if editor.iconPicker.RefreshResults then
      editor.iconPicker:RefreshResults()
    end
  end

  return {
    ShowIconPicker = ShowIconPicker,
  }
end
