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

local function CreateLootProfileSettingsHelpers(context)
  context = type(context) == "table" and context or {}
  local editor = context.editor
  local rightContent = context.rightContent

  local function SetDropdownValue(control, value, text)
    if not control then
      return
    end
    if control.__lidcouncilCustomDropdown then
      control:SetSelectedValue(value)
      control:SetText(text or "")
    elseif control.SetText then
      control:SetText(text or "")
    end
  end

  local function ClearItemRuleSelection()
    editor.__lootPointItemPage = 1
    editor.__lootSelectedRuleItemID = nil
    editor.__lootSelectedRuleItemName = nil
    editor.__lootSelectedRuleItemLink = nil
    editor.__lootSelectedRuleItemLevel = nil
    SetDropdownValue(editor.lootPointItemDropdown, nil, "(select profile-season loot item)")
  end

  local function RefreshLidTableRows(profile)
    local rows = context.GetLidTableRows(profile)
    editor.lootLidTableRows = context.EnsureLidTableEditorRows(editor.lootLidTableRows, rightContent, 15)
    for index, row in ipairs(editor.lootLidTableRows or {}) do
      local values = rows[index] or {}
      row.__rowIndex = index
      row.rankLabel:SetText(string.format("%02d", index))
      if row.pointsEdit then
        row.pointsEdit:SetText(tostring(tonumber(values.points) or index))
      end
      if row.itemLevelEdit then
        row.itemLevelEdit:SetText(values.itemLevel and tostring(values.itemLevel) or "")
      end
      if row.setPieceEdit then
        row.setPieceEdit:SetText(values.setPiecePoints and tostring(values.setPiecePoints) or "")
      end
      if row.offSpecEdit then
        row.offSpecEdit:SetText(values.offSpecPoints and tostring(values.offSpecPoints) or "")
      end
      if row.transmogEdit then
        row.transmogEdit:SetText(values.transmogPoints and tostring(values.transmogPoints) or "")
      end
      if row.disenchantEdit then
        row.disenchantEdit:SetText(values.disenchantPoints and tostring(values.disenchantPoints) or "")
      end
    end
  end

  local function CommitLidTableRow(row)
    if not row or row.__commitPending then
      return
    end
    row.__commitPending = true
    local ok, result = context.UpdateLootLidTableRow(context.GetSelectedProfileName(editor), row.__rowIndex, {
      points = row.pointsEdit and row.pointsEdit:GetText() or nil,
      itemLevel = row.itemLevelEdit and row.itemLevelEdit:GetText() or nil,
      setPiecePoints = row.setPieceEdit and row.setPieceEdit:GetText() or nil,
      offSpecPoints = row.offSpecEdit and row.offSpecEdit:GetText() or nil,
      transmogPoints = row.transmogEdit and row.transmogEdit:GetText() or nil,
      disenchantPoints = row.disenchantEdit and row.disenchantEdit:GetText() or nil,
    })
    row.__commitPending = nil
    if ok then
      if context.feedback and context.feedback.SetText then
        context.feedback:SetText("")
      end
      if editor.Refresh then
        editor:Refresh()
      end
    else
      if context.feedback and context.feedback.SetText then
        context.feedback:SetText(tostring(result or "Could not update LidTable row."))
      end
    end
  end

  local function BindLidTableEditBox(editBox, row, fieldName)
    if not editBox or not row then
      return
    end
    editBox:SetScript("OnEnterPressed", function(self)
      row.__skipNextFocusCommit = true
      CommitLidTableRow(row)
      self:ClearFocus()
    end)
    editBox:SetScript("OnEditFocusLost", function()
      if row.__skipNextFocusCommit then
        row.__skipNextFocusCommit = nil
        return
      end
      CommitLidTableRow(row)
    end)
    editBox:SetScript("OnTabPressed", function(self)
      row.__skipNextFocusCommit = true
      CommitLidTableRow(row)
      self:ClearFocus()

      local currentIndex = tonumber(row.__rowIndex) or 0
      local nextRow = type(editor.lootLidTableRows) == "table" and editor.lootLidTableRows[currentIndex + 1] or nil
      local target = type(nextRow) == "table" and fieldName and nextRow[fieldName] or nil
      if target and target.SetFocus then
        target:SetFocus()
        if target.HighlightText then
          target:HighlightText()
        end
      end
    end)
  end

  local function BindAllLidTableRows()
    for _, row in ipairs(editor.lootLidTableRows or {}) do
      BindLidTableEditBox(row.pointsEdit, row, "pointsEdit")
      BindLidTableEditBox(row.itemLevelEdit, row, "itemLevelEdit")
      BindLidTableEditBox(row.setPieceEdit, row, "setPieceEdit")
      BindLidTableEditBox(row.offSpecEdit, row, "offSpecEdit")
      BindLidTableEditBox(row.transmogEdit, row, "transmogEdit")
      BindLidTableEditBox(row.disenchantEdit, row, "disenchantEdit")
    end
  end

  local function CommitRollOptionName(row)
    if not row or not row.nameEdit or not row.__optionIndex then
      return
    end

    local currentLabel = context.TrimText(row.__option and (row.__option.label or row.__option.key) or "")
    local nextLabel = context.TrimText(row.nameEdit:GetText())
    if nextLabel == "" then
      row.nameEdit:SetText(currentLabel)
      return
    end
    if nextLabel == currentLabel then
      row.nameEdit:SetText(currentLabel)
      return
    end

    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), row.__optionIndex, {
      label = nextLabel,
    })
    if not ok then
      row.nameEdit:SetText(currentLabel)
      if context.feedback and context.feedback.SetText then
        context.feedback:SetText(tostring(result or "Could not rename roll option."))
      end
      return
    end

    if context.feedback and context.feedback.SetText then
      context.feedback:SetText("Updated roll option name.")
    end
    if editor.Refresh then
      editor:Refresh()
    end
  end

  local function BindRollOptionNameEditBox(editBox, row)
    if not editBox or not row then
      return
    end
    editBox:SetScript("OnEnterPressed", function(self)
      row.__skipNextRollNameCommit = true
      CommitRollOptionName(row)
      self:ClearFocus()
    end)
    editBox:SetScript("OnEditFocusLost", function()
      if row.__skipNextRollNameCommit then
        row.__skipNextRollNameCommit = nil
        return
      end
      CommitRollOptionName(row)
    end)
    editBox:SetScript("OnEscapePressed", function(self)
      local currentLabel = context.TrimText(row.__option and (row.__option.label or row.__option.key) or "")
      self:SetText(currentLabel)
      self:ClearFocus()
    end)
  end

  local function RefreshRollOptionRows(profile)
    local options = profile and profile.settings and profile.settings.rollOptions or {}
    editor.lootRollOptionRows = context.EnsureRollOptionRows(editor.lootRollOptionRows, editor, rightContent, #options)
    editor.__lootRollOptionRowCount = #options

    for _, row in ipairs(editor.lootRollOptionRows or {}) do
      if row.nameEdit and not row.__rollNameEditBound then
        BindRollOptionNameEditBox(row.nameEdit, row)
        row.__rollNameEditBound = true
      end
    end

    for index, row in ipairs(editor.lootRollOptionRows or {}) do
      local option = options[index]
      if option then
        row.__optionIndex = index
        row.__option = option
        row:Show()
        row.indexLabel:SetText(string.format("%d", index))
        row.nameEdit:SetText(tostring(option.label or option.key or "Option"))
        row.nameEdit:SetCursorPosition(0)

        if row.moveUpBtn and row.moveUpBtn.SetScript then
          row.moveUpBtn:SetEnabled(index > 1)
          row.moveUpBtn:SetScript("OnClick", function()
            local ok, result
            if type(context.MoveLootRollOption) == "function" then
              ok, result = context.MoveLootRollOption(context.GetSelectedProfileName(editor), index, -1)
            else
              ok, result = false, "Move API unavailable."
            end
            if not ok then
              if context.feedback and context.feedback.SetText then
                context.feedback:SetText(tostring(result or "Could not move the roll option."))
              end
              return
            end
            if editor.Refresh then
              editor:Refresh()
            end
          end)
        end
        if row.moveDownBtn and row.moveDownBtn.SetScript then
          row.moveDownBtn:SetEnabled(index < #options)
          row.moveDownBtn:SetScript("OnClick", function()
            local ok, result
            if type(context.MoveLootRollOption) == "function" then
              ok, result = context.MoveLootRollOption(context.GetSelectedProfileName(editor), index, 1)
            else
              ok, result = false, "Move API unavailable."
            end
            if not ok then
              if context.feedback and context.feedback.SetText then
                context.feedback:SetText(tostring(result or "Could not move the roll option."))
              end
              return
            end
            if editor.Refresh then
              editor:Refresh()
            end
          end)
        end

        local iconTexture = context.ResolveRollOptionIcon(option) or "Interface\\Icons\\INV_Misc_QuestionMark"
        row.iconPreview:SetTexture(iconTexture)

        if row.iconDropdown then
          local iconLabel = context.GetRollOptionIconLabel(context.ResolveRollOptionIcon(option))
          if row.iconDropdown.__lidcouncilCustomDropdown then
            row.iconDropdown:SetSelectedValue(tostring(context.ResolveRollOptionIcon(option) or ""))
            row.iconDropdown:SetText(iconLabel)
            row.iconDropdown:SetMenuBuilder(function()
              local entries = {
                {
                  text = "Default / None",
                  icon = context.ResolveDefaultRollOptionIcon(option.key, option.label),
                  checked = context.ResolveTextureAsset(option.icon) == nil,
                  func = function()
                    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { icon = false })
                    if not ok then
                      if context.feedback and context.feedback.SetText then
                        context.feedback:SetText(tostring(result or "Could not update the roll icon."))
                      end
                      return
                    end
                    if editor.Refresh then editor:Refresh() end
                  end,
                },
              }

              if type(editor.ShowIconPicker) == "function" then
                entries[#entries + 1] = {
                  text = "Browse Icon Library...",
                  icon = context.ResolveRollOptionIcon(option) or "Interface\\Icons\\INV_Misc_QuestionMark",
                  notCheckable = true,
                  func = function()
                    editor.ShowIconPicker(function(icon)
                      if not icon or icon.texture == nil then
                        return
                      end
                      local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { icon = tostring(icon.texture) })
                      if not ok then
                        if context.feedback and context.feedback.SetText then
                          context.feedback:SetText(tostring(result or "Could not update the roll icon."))
                        end
                        return
                      end
                      if editor.Refresh then editor:Refresh() end
                    end)
                  end,
                }
              end

              entries[#entries + 1] = {
                text = "Quick Picks",
                isTitle = true,
                notCheckable = true,
              }
              for _, iconEntry in ipairs(context.DEFAULT_ROLL_OPTION_ICON_CHOICES) do
                local texture = iconEntry.texture
                entries[#entries + 1] = {
                  text = tostring(iconEntry.label or "Icon"),
                  icon = texture,
                  checked = tostring(texture or "") == tostring(context.ResolveTextureAsset(option.icon) or ""),
                  func = function()
                    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { icon = texture or false })
                    if not ok then
                      if context.feedback and context.feedback.SetText then
                        context.feedback:SetText(tostring(result or "Could not update the roll icon."))
                      end
                      return
                    end
                    if editor.Refresh then editor:Refresh() end
                  end,
                }
              end
              return {
                width = 240,
                entries = entries,
              }
            end)
          elseif row.iconDropdown.SetText then
            row.iconDropdown:SetText(iconLabel)
          end
        end

        if row.pointBehaviorDropdown then
          local behaviorValue = string.upper(context.TrimText(option.pointBehavior or "ITEMLEVEL"))
          local behaviorLabel = ({
            BIS = "BiS",
            OFFSPEC = "Off-Spec",
            TRANSMOG = "Transmog",
            DISENCHANT = "Disenchant",
            NONE = "None",
          })[behaviorValue] or "Itemlevel Based"
          if row.pointBehaviorDropdown.__lidcouncilCustomDropdown then
            row.pointBehaviorDropdown:SetSelectedValue(behaviorValue)
            row.pointBehaviorDropdown:SetText(behaviorLabel)
            row.pointBehaviorDropdown:SetMenuBuilder(function()
              local entries = {}
              for _, behaviorEntry in ipairs({
                { value = "ITEMLEVEL", text = "Itemlevel Based" },
                { value = "BIS", text = "BiS" },
                { value = "OFFSPEC", text = "Off-Spec" },
                { value = "TRANSMOG", text = "Transmog" },
                { value = "DISENCHANT", text = "Disenchant" },
                { value = "NONE", text = "None" },
              }) do
                entries[#entries + 1] = {
                  text = tostring(behaviorEntry.text),
                  checked = behaviorValue == behaviorEntry.value,
                  func = function()
                    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { pointBehavior = behaviorEntry.value })
                    if not ok then
                      if context.feedback and context.feedback.SetText then
                        context.feedback:SetText(tostring(result or "Could not update the point behavior."))
                      end
                      return
                    end
                    if editor.Refresh then
                      editor:Refresh()
                    end
                  end,
                }
              end
              return {
                width = 166,
                entries = entries,
              }
            end)
          elseif row.pointBehaviorDropdown.SetText then
            row.pointBehaviorDropdown:SetText(behaviorLabel)
          end
        end

        if row.popupSectionDropdown then
          local popupSectionValue = string.upper(context.TrimText(option.popupSection or "NEED"))
          local popupSectionLabel = popupSectionValue == "GREEDPASS" and "Greed / Pass" or "Need"
          if row.popupSectionDropdown.__lidcouncilCustomDropdown then
            row.popupSectionDropdown:SetSelectedValue(popupSectionValue)
            row.popupSectionDropdown:SetText(popupSectionLabel)
            row.popupSectionDropdown:SetMenuBuilder(function()
              local entries = {
                {
                  text = "Need",
                  checked = popupSectionValue == "NEED",
                  func = function()
                    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { popupSection = "NEED" })
                    if not ok then
                      if context.feedback and context.feedback.SetText then
                        context.feedback:SetText(tostring(result or "Could not update the popup section."))
                      end
                      return
                    end
                    if editor.Refresh then
                      editor:Refresh()
                    end
                  end,
                },
                {
                  text = "Greed / Pass",
                  checked = popupSectionValue == "GREEDPASS",
                  func = function()
                    local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, { popupSection = "GREEDPASS" })
                    if not ok then
                      if context.feedback and context.feedback.SetText then
                        context.feedback:SetText(tostring(result or "Could not update the popup section."))
                      end
                      return
                    end
                    if editor.Refresh then
                      editor:Refresh()
                    end
                  end,
                },
              }
              return {
                width = 152,
                entries = entries,
              }
            end)
          elseif row.popupSectionDropdown.SetText then
            row.popupSectionDropdown:SetText(popupSectionLabel)
          end
        end

        if row.colorSwatch then
          local color = option.color or { 0.82, 0.82, 0.86 }
          row.colorSwatch:SetVertexColor(
            tonumber(color[1]) or 0.82,
            tonumber(color[2]) or 0.82,
            tonumber(color[3]) or 0.86,
            1
          )
        end

        if row.colorButton and row.colorButton.SetScript then
          row.colorButton:SetScript("OnClick", function()
            local openColorPicker = context.ResolveOptionalApiFunction("LIDCOUNCIL_OpenColorPickerRGBOnlyOnAccept")
              or context.ResolveOptionalApiFunction("LIDCOUNCIL_OpenColorPickerRGBOnly")
            if type(openColorPicker) ~= "function" then
              return
            end
            local startColor = option.color or { 0.82, 0.82, 0.86 }
            openColorPicker(startColor, function(r, g, b)
              local ok, result = context.UpdateLootRollOption(context.GetSelectedProfileName(editor), index, {
                color = { r, g, b },
              })
              if not ok then
                if context.feedback and context.feedback.SetText then
                  context.feedback:SetText(tostring(result or "Could not update the roll color."))
                end
                return
              end
              if editor.Refresh then editor:Refresh() end
            end)
          end)
        end
      else
        row.__optionIndex = nil
        row.__option = nil
        if row.moveUpBtn then row.moveUpBtn:SetEnabled(false) end
        if row.moveDownBtn then row.moveDownBtn:SetEnabled(false) end
        row:Hide()
      end
    end
  end

  local function GetResolvedRaidEntry(profile)
    return context.FindRaidEntryByProfile(profile)
  end

  local function GetSelectedProfile()
    if type(context.GetLootProfile) ~= "function" or type(context.GetSelectedProfileName) ~= "function" then
      return nil
    end
    return context.GetLootProfile(context.GetSelectedProfileName(editor))
  end

  local function GetProfileSeasonSnapshot(profile)
    local snapshot = type(profile) == "table"
      and type(profile.settings) == "table"
      and type(profile.settings.seasonSnapshot) == "table"
      and profile.settings.seasonSnapshot
      or nil
    if type(snapshot) ~= "table" or not tonumber(snapshot.tier) or type(snapshot.raids) ~= "table" or #snapshot.raids == 0 then
      return nil
    end
    return snapshot
  end

  local function CloneSeasonSnapshotContext(snapshot)
    local tier = tonumber(snapshot and snapshot.tier)
    local tierName = context.TrimText(snapshot and snapshot.tierName) ~= "" and tostring(snapshot.tierName) or "Profile Season"
    local raids = {}
    for _, raid in ipairs(type(snapshot and snapshot.raids) == "table" and snapshot.raids or {}) do
      local instanceID = tonumber(raid.instanceID)
      if instanceID then
        raids[#raids + 1] = {
          instanceID = instanceID,
          name = tostring(raid.name or instanceID),
          tier = tier,
          tierName = tierName,
        }
      end
    end
    table.sort(raids, function(a, b)
      return string.lower(tostring(a.name or "")) < string.lower(tostring(b.name or ""))
    end)
    return {
      tier = tier,
      tierName = tierName,
      raids = raids,
      frozen = true,
      capturedAt = tonumber(snapshot and snapshot.capturedAt) or nil,
    }
  end

  local function GetLootRaidTierEntries(profile)
    local snapshot = GetProfileSeasonSnapshot(profile or GetSelectedProfile())
    if snapshot then
      local tier = tonumber(snapshot.tier)
      return {
        {
          tier = tier,
          tierName = context.TrimText(snapshot.tierName) ~= "" and tostring(snapshot.tierName) or ("Season " .. tostring(tier)),
          frozen = true,
        },
      }
    end

    local raids = context.CollectRaidEntries()
    local tiers = {}
    local seen = {}
    for _, raid in ipairs(raids) do
      local tierValue = tonumber(raid.tier)
      if tierValue and not seen[tierValue] then
        seen[tierValue] = true
        tiers[#tiers + 1] = {
          tier = tierValue,
          tierName = tostring(raid.tierName or ("Season " .. tostring(tierValue))),
        }
      end
    end
    table.sort(tiers, function(a, b)
      return (tonumber(a.tier) or 0) > (tonumber(b.tier) or 0)
    end)
    return tiers
  end

  local function GetCurrentRaidTierValue(profile)
    local snapshot = GetProfileSeasonSnapshot(profile)
    if snapshot then
      return tonumber(snapshot.tier)
    end
    local currentTier = type(EJ_GetCurrentTier) == "function" and tonumber(EJ_GetCurrentTier()) or nil
    if currentTier then
      return currentTier
    end
    local tiers = GetLootRaidTierEntries(profile)
    return tiers[1] and tonumber(tiers[1].tier) or nil
  end

  local function GetCurrentLootSeasonContext(profile)
    local snapshot = GetProfileSeasonSnapshot(profile or GetSelectedProfile())
    if snapshot then
      return CloneSeasonSnapshotContext(snapshot)
    end

    local targetTier = GetCurrentRaidTierValue(profile)
    local raids = {}
    local tierName = "Current Season"

    for _, raid in ipairs(context.CollectRaidEntries() or {}) do
      if tonumber(raid.tier) == tonumber(targetTier) then
        raids[#raids + 1] = {
          instanceID = tonumber(raid.instanceID),
          name = tostring(raid.name or raid.instanceID or "?"),
          tier = tonumber(targetTier),
          tierName = context.TrimText(raid.tierName) ~= "" and tostring(raid.tierName) or tierName,
        }
        if context.TrimText(raid.tierName) ~= "" then
          tierName = tostring(raid.tierName)
        end
      end
    end

    table.sort(raids, function(a, b)
      return string.lower(tostring(a.name or "")) < string.lower(tostring(b.name or ""))
    end)

    return {
      tier = tonumber(targetTier),
      tierName = tierName,
      raids = raids,
    }
  end

  local function BuildSeasonRaidNamesText(contextValue)
    local names = {}
    for _, raid in ipairs((contextValue and contextValue.raids) or {}) do
      names[#names + 1] = tostring(raid.name or "?")
    end
    if #names == 0 then
      return "No raids available."
    end
    return table.concat(names, ", ")
  end

  local function CollectSeasonLootEntries(profile)
    local seasonContext = GetCurrentLootSeasonContext(profile)
    local items = {}
    local seen = {}

    for _, raid in ipairs(seasonContext.raids or {}) do
      for _, item in ipairs(context.CollectRaidLootEntries(raid.instanceID) or {}) do
        local itemID = tonumber(item.itemID)
        local key = itemID and ("id:" .. tostring(itemID))
          or ((context.TrimText(item.link) ~= "") and ("link:" .. tostring(item.link)))
          or string.lower(string.format("%s|%s", tostring(item.name or "?"), tostring(item.itemLevel or "")))
        if not seen[key] then
          seen[key] = true
          local clone = {}
          for field, value in pairs(item) do
            clone[field] = value
          end
          clone.sourceRaidName = tostring(raid.name or "?")
          clone.sourceInstanceID = tonumber(raid.instanceID)
          items[#items + 1] = clone
        end
      end
    end

    table.sort(items, function(a, b)
      local aLevel = tonumber(a.itemLevel) or 0
      local bLevel = tonumber(b.itemLevel) or 0
      if aLevel == bLevel then
        return string.lower(tostring(a.name or "")) < string.lower(tostring(b.name or ""))
      end
      return aLevel > bLevel
    end)

    return seasonContext, items
  end

  local function FindSeasonLootEntry(itemID, profile)
    local resolvedItemID = tonumber(itemID)
    if not resolvedItemID then
      return nil
    end
    local _, items = CollectSeasonLootEntries(profile)
    for _, item in ipairs(items or {}) do
      if tonumber(item.itemID) == resolvedItemID then
        return item
      end
    end
    return nil
  end

  local function BuildRaidSeasonDropdownMenu(dropdownControl)
    local profile = GetSelectedProfile()
    local selectedTier = dropdownControl and dropdownControl.GetSelectedValue and dropdownControl:GetSelectedValue() or editor.__lootRaidSelectedTier
    local tiers = GetLootRaidTierEntries(profile)
    local entries = {}
    local frozen = GetProfileSeasonSnapshot(profile) ~= nil

    for _, tier in ipairs(tiers) do
      local tierValue = tonumber(tier.tier)
      entries[#entries + 1] = {
        text = tostring(tier.tierName or ("Season " .. tostring(tierValue))) .. (frozen and " (Profile Season)" or ""),
        value = tierValue,
        checked = tonumber(selectedTier) == tierValue,
        disabled = frozen,
        func = function()
          if frozen then return end
          editor.__lootRaidSelectedTier = tierValue
          SetDropdownValue(editor.lootRaidSeasonDropdown, tierValue, tostring(tier.tierName or ("Season " .. tostring(tierValue))))
        end,
      }
    end

    if #entries == 0 then
      entries[#entries + 1] = {
        text = "No raid seasons available",
        disabled = true,
        notCheckable = true,
      }
    end

    return {
      entries = entries,
      width = 260,
    }
  end

  local function BuildRaidDropdownMenu(dropdownControl)
    local profile = GetSelectedProfile()
    local seasonContext = GetCurrentLootSeasonContext(profile)
    local raids = seasonContext.raids or {}
    local selectedTier = tonumber(seasonContext.tier or editor.__lootRaidSelectedTier)
    local selectedInstanceID = dropdownControl and dropdownControl.GetSelectedValue and dropdownControl:GetSelectedValue() or nil
    local entries = {}

    if #raids == 0 then
      entries[#entries + 1] = {
        text = "Adventure Guide raids unavailable",
        disabled = true,
        notCheckable = true,
      }
      return {
        entries = entries,
        width = 320,
      }
    end

    for _, raid in ipairs(raids) do
      local instanceID = tonumber(raid.instanceID)
      if not selectedTier or tonumber(raid.tier) == selectedTier then
        entries[#entries + 1] = {
          text = tostring(raid.name or instanceID or "?"),
          value = instanceID,
          checked = tonumber(selectedInstanceID) == instanceID,
          func = function()
            local ok, result = context.SetLootProfileRaid(context.GetSelectedProfileName(editor), raid.name, instanceID)
            if ok == false then
              if context.feedback and context.feedback.SetText then
                context.feedback:SetText(tostring(result or "Could not update the raid selection."))
              end
              return
            end
            editor.__lootRaidSelectedTier = tonumber(raid.tier) or editor.__lootRaidSelectedTier
            if context.feedback and context.feedback.SetText then
              context.feedback:SetText("Selected raid: " .. tostring(raid.name))
            end
            ClearItemRuleSelection()
            if editor.Refresh then
              editor:Refresh()
            end
          end,
        }
      end
    end

    if #entries == 0 then
      entries[#entries + 1] = {
        text = "No raids in this profile season",
        disabled = true,
        notCheckable = true,
      }
    end

    return {
      entries = entries,
      width = 320,
    }
  end

  local function BuildLootItemDropdownMenu(dropdownControl)
    local profile = GetSelectedProfile()
    local seasonContext, items = CollectSeasonLootEntries(profile)
    local entries = {}

    if #(seasonContext.raids or {}) == 0 then
      entries[#entries + 1] = {
        text = "No profile-season raids available",
        disabled = true,
        notCheckable = true,
      }
      return {
        entries = entries,
        width = 400,
      }
    end

    if #items == 0 then
      entries[#entries + 1] = {
        text = "No profile-season loot items found",
        disabled = true,
        notCheckable = true,
      }
      return {
        entries = entries,
        width = 400,
      }
    end

    local selectedValue = dropdownControl and dropdownControl.GetSelectedValue and dropdownControl:GetSelectedValue() or editor.__lootSelectedRuleItemID
    local totalPages = math.max(1, math.ceil(#items / context.LOOT_ITEM_MENU_PAGE_SIZE))
    local currentPage = context.ClampMenuPage(editor.__lootPointItemPage or context.GetSelectedValuePage(items, selectedValue), totalPages)
    editor.__lootPointItemPage = currentPage

    local firstIndex = ((currentPage - 1) * context.LOOT_ITEM_MENU_PAGE_SIZE) + 1
    local lastIndex = math.min(#items, firstIndex + context.LOOT_ITEM_MENU_PAGE_SIZE - 1)
    for index = firstIndex, lastIndex do
      local item = items[index]
      local itemID = tonumber(item.itemID)
      local itemText = string.format("%s | %s", context.FormatRaidLootItemLabel(item), tostring(item.sourceRaidName or "?"))
      entries[#entries + 1] = {
        text = itemText,
        value = itemID,
        icon = item.icon,
        checked = tonumber(selectedValue) == itemID,
        func = function()
          editor.__lootSelectedRuleItemID = itemID
          editor.__lootSelectedRuleItemName = item.name
          editor.__lootSelectedRuleItemLink = item.link
          editor.__lootSelectedRuleItemLevel = tonumber(item.itemLevel)
          editor.__lootPointItemPage = context.GetSelectedValuePage(items, itemID)
          SetDropdownValue(editor.lootPointItemDropdown, itemID, itemText)
        end,
      }
    end

    if totalPages > 1 then
      entries[#entries + 1] = {
        text = string.format("Page %d/%d", currentPage, totalPages),
        isTitle = true,
        notCheckable = true,
      }
      entries[#entries + 1] = {
        text = "< Prev",
        disabled = (currentPage <= 1),
        notCheckable = true,
        keepShownOnClick = true,
        func = function()
          editor.__lootPointItemPage = context.ClampMenuPage(currentPage - 1, totalPages)
        end,
      }
      entries[#entries + 1] = {
        text = "Next >",
        disabled = (currentPage >= totalPages),
        notCheckable = true,
        keepShownOnClick = true,
        func = function()
          editor.__lootPointItemPage = context.ClampMenuPage(currentPage + 1, totalPages)
        end,
      }
    end

    return {
      entries = entries,
      width = 400,
      onMouseWheel = function(delta)
        if totalPages <= 1 then
          return false
        end
        local targetPage = context.ClampMenuPage(currentPage - delta, totalPages)
        if targetPage == currentPage then
          return false
        end
        editor.__lootPointItemPage = targetPage
        return true
      end,
    }
  end

  local function BuildPointModeDropdownMenu(dropdownControl)
    local profile = context.GetLootProfile(context.GetSelectedProfileName(editor))
    local selectedValue = dropdownControl and dropdownControl.GetSelectedValue and dropdownControl:GetSelectedValue() or context.ResolvePointMode(profile)
    local entries = {}

    for _, choice in ipairs(context.POINT_MODE_CHOICES) do
      entries[#entries + 1] = {
        text = tostring(choice.text),
        value = tostring(choice.value),
        checked = tostring(selectedValue or "") == tostring(choice.value),
        func = function()
          local result = context.UpdateLootPointMode(context.GetSelectedProfileName(editor), choice.value)
          if result == false then
            if context.feedback and context.feedback.SetText then
              context.feedback:SetText("Could not update the Loot List type.")
            end
            return
          end
          if context.feedback and context.feedback.SetText then
            context.feedback:SetText("Loot list type: " .. tostring(choice.text))
          end
          if editor.Refresh then
            editor:Refresh()
          end
        end,
      }
    end

    return {
      entries = entries,
      width = 220,
    }
  end

  local function BuildRollTemplateDropdownMenu(dropdownControl)
    local profile = context.GetLootProfile(context.GetSelectedProfileName(editor))
    local selectedValue = context.ResolveRollTemplate(profile)
    local entries = {}

    if selectedValue == "CUSTOM" then
      entries[#entries + 1] = {
        text = "Current: Custom",
        isTitle = true,
        notCheckable = true,
      }
    end

    for _, choice in ipairs(context.ROLL_TEMPLATE_CHOICES or {}) do
      entries[#entries + 1] = {
        text = tostring(choice.text),
        value = tostring(choice.value),
        checked = tostring(selectedValue or "") == tostring(choice.value),
        func = function()
          if type(context.ApplyLootRollTemplate) ~= "function" then
            if context.feedback and context.feedback.SetText then
              context.feedback:SetText("Roll template API unavailable.")
            end
            return
          end

          local ok, result = context.ApplyLootRollTemplate(context.GetSelectedProfileName(editor), choice.value)
          if not ok then
            if context.feedback and context.feedback.SetText then
              context.feedback:SetText(tostring(result or "Could not update the roll template."))
            end
            return
          end

          if context.feedback and context.feedback.SetText then
            context.feedback:SetText("Roll template: " .. tostring(choice.text))
          end
          if editor.Refresh then
            editor:Refresh()
          end
        end,
      }
    end

    return {
      entries = entries,
      width = 220,
    }
  end

  local function BroadcastActiveRoleUpdate(profileName, characterKey)
    local broadcastRoleUpdate = context.ResolveOptionalApiFunction("LIDCOUNCIL_BroadcastActiveSessionRoleUpdate")
    if type(broadcastRoleUpdate) == "function" and context.TrimText(characterKey) ~= "" then
      broadcastRoleUpdate(profileName, characterKey)
    end
  end

  return {
    SetDropdownValue = SetDropdownValue,
    ClearItemRuleSelection = ClearItemRuleSelection,
    RefreshLidTableRows = RefreshLidTableRows,
    BindAllLidTableRows = BindAllLidTableRows,
    RefreshRollOptionRows = RefreshRollOptionRows,
    GetResolvedRaidEntry = GetResolvedRaidEntry,
    GetCurrentLootSeasonContext = GetCurrentLootSeasonContext,
    BuildSeasonRaidNamesText = BuildSeasonRaidNamesText,
    CollectSeasonLootEntries = CollectSeasonLootEntries,
    FindSeasonLootEntry = FindSeasonLootEntry,
    BuildRaidSeasonDropdownMenu = BuildRaidSeasonDropdownMenu,
    BuildRaidDropdownMenu = BuildRaidDropdownMenu,
    BuildLootItemDropdownMenu = BuildLootItemDropdownMenu,
    BuildRollTemplateDropdownMenu = BuildRollTemplateDropdownMenu,
    BuildPointModeDropdownMenu = BuildPointModeDropdownMenu,
    BroadcastActiveRoleUpdate = BroadcastActiveRoleUpdate,
  }
end

INTERNAL_API.LIDCOUNCIL_CreateLootProfileSettingsHelpers_Impl = CreateLootProfileSettingsHelpers
