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_CreateLootListRecommendationsViewHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}

  local ApplyFrameSkin = deps.ApplyFrameSkin
  local ApplyLootListToolbarButtonStyle = deps.ApplyLootListToolbarButtonStyle
  local BindMouseWheelScroll = deps.BindMouseWheelScroll
  local CreateCustomDropdownControl = deps.CreateCustomDropdownControl
  local CollectRaidEntries = deps.CollectRaidEntries
  local CollectRaidEncounterEntries = deps.CollectRaidEncounterEntries
  local FindRaidEntryByProfile = deps.FindRaidEntryByProfile
  local CollectRaidLootEntries = deps.CollectRaidLootEntries
  local RAID_LOOT_DIFFICULTIES = deps.RAID_LOOT_DIFFICULTIES
  local FormatRaidLootItemLabel = deps.FormatRaidLootItemLabel
  local ResolveRollOptionIcon = deps.ResolveRollOptionIcon
  local GetLootProfile = deps.GetLootProfile
  local TrimText = deps.TrimText
  local ResolveOptionalApiFunction = deps.ResolveOptionalApiFunction

  local function trim(value)
    if type(TrimText) == "function" then
      return TrimText(value)
    end
    local text = tostring(value or "")
    text = text:gsub("^%s+", "")
    text = text:gsub("%s+$", "")
    return text
  end

  local function ResolveApiFunction(name)
    if type(ResolveOptionalApiFunction) == "function" then
      local fn = ResolveOptionalApiFunction(name)
      if type(fn) == "function" then
        return fn
      end
    end
    local api = rawget(_G, "LIDCOUNCIL_API")
    if type(api) == "table" and type(api[name]) == "function" then
      return api[name]
    end
    return rawget(_G, name)
  end

  local function GetRollOptions(profile)
    local options = type(profile) == "table" and type(profile.settings) == "table" and profile.settings.rollOptions or nil
    return type(options) == "table" and options or {}
  end

  local function ResolveRecommendation(profileName, item, options, recommendationOptions)
    local resolver = ResolveApiFunction("LIDCOUNCIL_ResolvePersonalRecommendation")
    if type(resolver) ~= "function" then
      return nil, nil, "NONE"
    end
    return resolver(profileName, item and item.itemID, item and item.link, item and item.name, options, recommendationOptions)
  end

  local function SetRecommendation(profileName, item, priority, option, recommendationOptions)
    local setter = ResolveApiFunction("LIDCOUNCIL_SetPersonalRecommendation")
    if type(setter) ~= "function" then
      return false
    end
    return setter(profileName, item and item.itemID, item and item.link, item and item.name, priority, option, recommendationOptions)
  end

  local function ClearRecommendation(profileName, item, recommendationOptions)
    local clearer = ResolveApiFunction("LIDCOUNCIL_ClearPersonalRecommendation")
    if type(clearer) ~= "function" then
      return false
    end
    return clearer(profileName, item and item.itemID, item and item.link, item and item.name, recommendationOptions)
  end

  local function FormatOptionText(priority, option)
    local label = trim(type(option) == "table" and (option.label or option.key) or "")
    if label == "" then
      label = "Roll Option"
    end
    return "#" .. tostring(priority) .. " " .. label
  end

  local function SetDropdownValue(dropdown, value, text)
    if not dropdown then
      return
    end
    if dropdown.SetSelectedValue then
      dropdown:SetSelectedValue(value)
    end
    if dropdown.SetText then
      dropdown:SetText(text)
    end
  end

  local function GetDifficultyEntries()
    local entries = type(RAID_LOOT_DIFFICULTIES) == "table" and RAID_LOOT_DIFFICULTIES or nil
    if type(entries) == "table" and #entries > 0 then
      return entries
    end
    return {
      { id = 14, name = "Normal" },
      { id = 15, name = "Heroic" },
      { id = 16, name = "Mythic" },
    }
  end

  local function GetDifficultyName(difficultyID)
    local resolvedDifficultyID = tonumber(difficultyID)
    for _, entry in ipairs(GetDifficultyEntries()) do
      if tonumber(entry.id) == resolvedDifficultyID then
        return tostring(entry.name or resolvedDifficultyID)
      end
    end
    return "Normal"
  end

  local function BuildRecommendationOptions(frame, item)
    if not (frame and frame.__recommendationPerDifficulty == true and type(item) == "table") then
      return nil
    end
    local difficultyID = tonumber(item.difficultyID)
    if not difficultyID then
      return nil
    end
    return {
      perDifficulty = true,
      exactDifficulty = true,
      difficultyID = difficultyID,
      difficultyName = trim(item.difficultyName ~= nil and item.difficultyName or GetDifficultyName(difficultyID)),
    }
  end

  local function FormatRecommendationScope(frame, item)
    if not (frame and frame.__recommendationPerDifficulty == true) then
      return ""
    end
    local difficultyName = trim(type(item) == "table" and item.difficultyName or "")
    if difficultyName == "" then
      difficultyName = GetDifficultyName(type(item) == "table" and item.difficultyID or nil)
    end
    return " for " .. difficultyName
  end

  local function GetRaidTierEntries()
    local raids = type(CollectRaidEntries) == "function" and CollectRaidEntries() or {}
    local tiers = {}
    local seen = {}
    for _, raid in ipairs(raids) do
      local tier = tonumber(raid.tier)
      if tier and not seen[tier] then
        seen[tier] = true
        tiers[#tiers + 1] = {
          tier = tier,
          tierName = tostring(raid.tierName or ("Season " .. tostring(tier))),
        }
      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()
    local tiers = GetRaidTierEntries()
    if tiers[1] then
      return tonumber(tiers[1].tier)
    end
    return type(EJ_GetCurrentTier) == "function" and tonumber(EJ_GetCurrentTier()) or nil
  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 CollectCurrentSeasonRaids(profile)
    local snapshot = GetProfileSeasonSnapshot(profile)
    if snapshot then
      local raids = {}
      local tier = tonumber(snapshot.tier)
      local tierName = trim(snapshot.tierName) ~= "" and tostring(snapshot.tierName) or "Profile Season"
      for _, raid in ipairs(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,
            frozen = true,
          }
        end
      end
      table.sort(raids, function(a, b)
        return string.lower(tostring(a.name or "")) < string.lower(tostring(b.name or ""))
      end)
      return raids
    end

    local targetTier = GetCurrentRaidTierValue()
    local raids = {}
    for _, raid in ipairs(type(CollectRaidEntries) == "function" and CollectRaidEntries() or {}) do
      if tonumber(raid.tier) == tonumber(targetTier) then
        raids[#raids + 1] = raid
      end
    end
    table.sort(raids, function(a, b)
      return string.lower(tostring(a.name or "")) < string.lower(tostring(b.name or ""))
    end)
    return raids
  end

  local function FindRaidByInstanceID(raids, instanceID)
    local resolvedInstanceID = tonumber(instanceID)
    if not resolvedInstanceID then
      return nil
    end
    for _, raid in ipairs(type(raids) == "table" and raids or {}) do
      if tonumber(raid.instanceID) == resolvedInstanceID then
        return raid
      end
    end
    return nil
  end

  local function FindEncounterByID(encounters, encounterID)
    local resolvedEncounterID = tonumber(encounterID)
    if not resolvedEncounterID then
      return nil
    end
    for _, encounter in ipairs(type(encounters) == "table" and encounters or {}) do
      if tonumber(encounter.encounterID) == resolvedEncounterID then
        return encounter
      end
    end
    return nil
  end

  local function BuildSpecFilterEntries()
    local entries = {
      {
        classID = 0,
        specID = 0,
        text = "All Classes / Specs",
      },
    }
    local seen = {}
    if type(GetNumClasses) == "function"
      and type(GetClassInfo) == "function"
      and type(GetNumSpecializationsForClassID) == "function"
      and type(GetSpecializationInfoForClassID) == "function" then
      local classCount = tonumber(GetNumClasses()) or 0
      for classID = 1, classCount do
        local className = select(1, GetClassInfo(classID))
        if className then
          entries[#entries + 1] = {
            classID = classID,
            specID = 0,
            text = tostring(className) .. " - All Specs",
          }
        end
        local specCount = tonumber(GetNumSpecializationsForClassID(classID)) or 0
        for specIndex = 1, specCount do
          local specID, specName, _, icon, role = GetSpecializationInfoForClassID(classID, specIndex)
          if specID and specName and not seen[specID] then
            local text = tostring(className or "Class") .. " - " .. tostring(specName)
            if role then
              text = text .. " (" .. tostring(role) .. ")"
            end
            entries[#entries + 1] = {
              classID = classID,
              specID = tonumber(specID),
              text = text,
              icon = icon,
              iconSize = 16,
            }
            seen[specID] = true
          end
        end
      end
    end
    table.sort(entries, function(a, b)
      local aGlobal = (tonumber(a.classID) or 0) == 0 and (tonumber(a.specID) or 0) == 0
      local bGlobal = (tonumber(b.classID) or 0) == 0 and (tonumber(b.specID) or 0) == 0
      if aGlobal ~= bGlobal then
        return aGlobal
      end
      if (tonumber(a.specID) or 0) == 0 and (tonumber(b.specID) or 0) ~= 0 then
        return true
      elseif (tonumber(b.specID) or 0) == 0 and (tonumber(a.specID) or 0) ~= 0 then
        return false
      end
      return string.lower(tostring(a.text or "")) < string.lower(tostring(b.text or ""))
    end)
    return entries
  end

  local function GetCurrentSpecFilter()
    local currentSpecID
    if type(GetSpecialization) == "function" and type(GetSpecializationInfo) == "function" then
      local specIndex = GetSpecialization()
      if specIndex then
        currentSpecID = tonumber((select(1, GetSpecializationInfo(specIndex))))
      end
    end
    if not currentSpecID then
      return 0, 0, "All Classes / Specs"
    end
    for _, entry in ipairs(BuildSpecFilterEntries()) do
      if tonumber(entry.specID) == currentSpecID then
        return tonumber(entry.classID) or 0, currentSpecID, tostring(entry.text or "Current Specialization")
      end
    end
    return 0, currentSpecID, "Current Specialization"
  end

  local function FindSpecFilterEntry(classID, specID)
    local resolvedClassID = tonumber(classID) or 0
    local resolvedSpecID = tonumber(specID) or 0
    for _, entry in ipairs(BuildSpecFilterEntries()) do
      if tonumber(entry.classID) == resolvedClassID and tonumber(entry.specID) == resolvedSpecID then
        return entry
      end
    end
    if resolvedSpecID == 0 then
      return BuildSpecFilterEntries()[1]
    end
    return nil
  end

  local function RaisePopupControl(control, popup, offset)
    if not control or not popup then
      return
    end
    local level = ((popup.GetFrameLevel and popup:GetFrameLevel()) or 1) + (tonumber(offset) or 10)
    local strata = (popup.GetFrameStrata and popup:GetFrameStrata()) or "FULLSCREEN_DIALOG"
    if control.SetFrameStrata then
      control:SetFrameStrata(strata)
    end
    if control.SetFrameLevel then
      control:SetFrameLevel(level)
    end
    if control.GetChildren then
      local children = { control:GetChildren() }
      for _, child in ipairs(children) do
        if child.SetFrameStrata then
          child:SetFrameStrata(strata)
        end
        if child.SetFrameLevel then
          child:SetFrameLevel(level + 1)
        end
      end
    end
  end

  local function HideItemTooltip()
    if type(GameTooltip) == "table" then
      GameTooltip:Hide()
    end
  end

  local function ShowItemTooltip(self)
    if type(GameTooltip) ~= "table" then
      return
    end
    local item = type(self) == "table" and self.__recommendationItem or nil
    if type(item) ~= "table" then
      return
    end
    local itemLink = trim(item.link)
    local itemID = tonumber(item.itemID)
    local itemName = trim(item.name)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    if itemLink ~= "" and GameTooltip.SetHyperlink then
      local ok = pcall(GameTooltip.SetHyperlink, GameTooltip, itemLink)
      if not ok and itemID and GameTooltip.SetItemByID then
        pcall(GameTooltip.SetItemByID, GameTooltip, itemID)
      end
    elseif itemID and GameTooltip.SetItemByID then
      pcall(GameTooltip.SetItemByID, GameTooltip, itemID)
    else
      GameTooltip:SetText(itemName ~= "" and itemName or ("Item #" .. tostring(itemID or "?")), 1, 0.82, 0.28, 1, true)
    end
    if trim(item.encounterName) ~= "" then
      GameTooltip:AddLine("Boss: " .. tostring(item.encounterName), 0.82, 0.82, 0.90, true)
    end
    if trim(item.difficultyName) ~= "" then
      GameTooltip:AddLine("Difficulty: " .. tostring(item.difficultyName), 0.82, 0.82, 0.90, true)
    end
    GameTooltip:Show()
  end

  local function EnsureRecommendationPopup(editor)
    if editor and editor.lootLedgerRecommendationsPopupFrame then
      return editor.lootLedgerRecommendationsPopupFrame
    end
    if not editor then
      return nil
    end

    local frame = CreateFrame("Frame", "LIDCOUNCIL_LootListRecommendationsPopup", UIParent, "BackdropTemplate")
    frame:SetSize(860, 660)
    frame:SetPoint("CENTER", 0, 0)
    frame:SetFrameStrata("FULLSCREEN_DIALOG")
    if frame.SetFrameLevel then
      frame:SetFrameLevel(500)
    end
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:SetToplevel(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
    if type(ApplyFrameSkin) == "function" then
      ApplyFrameSkin(frame)
    end
    frame:SetScript("OnShow", function()
      if editor and editor.UpdateLootListModeButtons then
        editor.UpdateLootListModeButtons()
      end
    end)
    frame:SetScript("OnHide", function()
      if editor and editor.UpdateLootListModeButtons then
        editor.UpdateLootListModeButtons()
      end
    end)

    local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", 18, -17)
    title:SetText("Personal Recommendations")
    frame.popupTitle = title

    local closeBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate,BackdropTemplate")
    closeBtn:SetSize(78, 22)
    closeBtn:SetPoint("TOPRIGHT", -14, -14)
    closeBtn:SetText("Close")
    if type(ApplyLootListToolbarButtonStyle) == "function" then
      ApplyLootListToolbarButtonStyle(closeBtn)
    elseif type(ApplyFrameSkin) == "function" then
      ApplyFrameSkin(closeBtn)
    end
    closeBtn:SetScript("OnClick", function()
      frame:Hide()
    end)
    frame.closeBtn = closeBtn
    RaisePopupControl(closeBtn, frame, 40)

    local intro = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    intro:SetPoint("TOPLEFT", 18, -44)
    intro:SetWidth(800)
    intro:SetJustifyH("LEFT")
    intro:SetText("Local-only raid item reminders. Choose a profile-season raid, boss, and difficulty, then select the roll priority you expect to use.")
    frame.intro = intro

    local raidLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    raidLabel:SetPoint("TOPLEFT", 18, -77)
    raidLabel:SetText("Raid:")
    frame.raidLabel = raidLabel

    if type(CreateCustomDropdownControl) == "function" then
      local raidDropdown = CreateCustomDropdownControl(editor, frame, {
        width = 240,
        buttonHeight = 24,
        rowHeight = 20,
        maxVisibleEntries = 12,
        text = "Select Raid",
      })
      raidDropdown:SetPoint("LEFT", raidLabel, "RIGHT", 8, 0)
      frame.raidDropdown = raidDropdown
      RaisePopupControl(raidDropdown, frame, 35)
    end

    local bossLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    bossLabel:SetPoint("TOPLEFT", 306, -77)
    bossLabel:SetText("Boss:")
    frame.bossLabel = bossLabel

    if type(CreateCustomDropdownControl) == "function" then
      local bossDropdown = CreateCustomDropdownControl(editor, frame, {
        width = 238,
        buttonHeight = 24,
        rowHeight = 20,
        maxVisibleEntries = 12,
        text = "All Bosses",
      })
      bossDropdown:SetPoint("LEFT", bossLabel, "RIGHT", 8, 0)
      frame.bossDropdown = bossDropdown
      RaisePopupControl(bossDropdown, frame, 35)
    end

    local difficultyLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    difficultyLabel:SetPoint("TOPLEFT", 596, -77)
    difficultyLabel:SetText("Difficulty:")
    frame.difficultyLabel = difficultyLabel

    if type(CreateCustomDropdownControl) == "function" then
      local difficultyDropdown = CreateCustomDropdownControl(editor, frame, {
        width = 120,
        buttonHeight = 24,
        rowHeight = 20,
        maxVisibleEntries = 6,
        text = "Normal",
      })
      difficultyDropdown:SetPoint("LEFT", difficultyLabel, "RIGHT", 8, 0)
      frame.difficultyDropdown = difficultyDropdown
      RaisePopupControl(difficultyDropdown, frame, 35)
    end

    local specFilterLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    specFilterLabel:SetPoint("TOPLEFT", 18, -107)
    specFilterLabel:SetText("Class / Spec:")
    frame.specFilterLabel = specFilterLabel

    if type(CreateCustomDropdownControl) == "function" then
      local specFilterDropdown = CreateCustomDropdownControl(editor, frame, {
        width = 310,
        buttonHeight = 24,
        rowHeight = 20,
        maxVisibleEntries = 14,
        text = "All Classes / Specs",
      })
      specFilterDropdown:SetPoint("LEFT", specFilterLabel, "RIGHT", 8, 0)
      frame.specFilterDropdown = specFilterDropdown
      RaisePopupControl(specFilterDropdown, frame, 35)
    end

    local perDifficultyCheck = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate")
    perDifficultyCheck:SetSize(24, 24)
    perDifficultyCheck:SetPoint("LEFT", frame.specFilterDropdown or specFilterLabel, "RIGHT", 18, 0)
    if perDifficultyCheck.Text then
      perDifficultyCheck.Text:SetText("Per Difficulty")
    else
      perDifficultyCheck.label = perDifficultyCheck:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      perDifficultyCheck.label:SetPoint("LEFT", perDifficultyCheck, "RIGHT", 2, 0)
      perDifficultyCheck.label:SetText("Per Difficulty")
    end
    perDifficultyCheck:SetScript("OnClick", function(self)
      frame.__recommendationPerDifficulty = self:GetChecked() and true or false
      if type(frame.__recommendationRefresh) == "function" then
        frame.__recommendationRefresh()
      end
    end)
    frame.perDifficultyCheck = perDifficultyCheck
    RaisePopupControl(perDifficultyCheck, frame, 35)

    local scroll = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 14, -142)
    scroll:SetPoint("BOTTOMRIGHT", -32, 14)
    if scroll.SetFrameLevel then
      scroll:SetFrameLevel(((frame.GetFrameLevel and frame:GetFrameLevel()) or 1) + 5)
    end
    if type(BindMouseWheelScroll) == "function" then
      BindMouseWheelScroll(scroll, 20)
    end
    frame.scroll = scroll

    local content = CreateFrame("Frame", nil, scroll)
    content:SetSize(790, 1)
    if content.SetFrameLevel then
      content:SetFrameLevel(((scroll.GetFrameLevel and scroll:GetFrameLevel()) or 1) + 1)
    end
    scroll:SetScrollChild(content)
    frame.content = content

    local emptyText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    emptyText:SetPoint("CENTER", frame, "CENTER", 0, -10)
    emptyText:SetWidth(680)
    emptyText:SetJustifyH("CENTER")
    emptyText:SetText("No raid loot entries are available for the selected filters.")
    emptyText:Hide()
    frame.emptyText = emptyText

    editor.lootLedgerRecommendationsPopupFrame = frame
    frame:Hide()
    return frame
  end

  local function EnsureRows(frame, count)
    frame.recommendationRows = frame.recommendationRows or {}
    while #frame.recommendationRows < count do
      local row = CreateFrame("Frame", nil, frame.content, "BackdropTemplate")
      row:SetSize(770, 56)
      if type(ApplyFrameSkin) == "function" then
        ApplyFrameSkin(row)
      end
      if row.SetFrameLevel and frame.content and frame.content.GetFrameLevel then
        row:SetFrameLevel((frame.content:GetFrameLevel() or 1) + 1)
      end

      local icon = row:CreateTexture(nil, "ARTWORK")
      icon:SetSize(34, 34)
      icon:SetPoint("LEFT", 10, 0)
      row.icon = icon

      local item = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      item:SetPoint("TOPLEFT", 54, -9)
      item:SetWidth(480)
      item:SetJustifyH("LEFT")
      if item.SetWordWrap then
        item:SetWordWrap(false)
      end
      row.item = item

      local status = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      status:SetPoint("TOPLEFT", 54, -32)
      status:SetWidth(480)
      status:SetJustifyH("LEFT")
      row.status = status

      local tooltipButton = CreateFrame("Button", nil, row)
      tooltipButton:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
      tooltipButton:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -245, 0)
      tooltipButton:EnableMouse(true)
      tooltipButton:SetScript("OnEnter", ShowItemTooltip)
      tooltipButton:SetScript("OnLeave", HideItemTooltip)
      row.tooltipButton = tooltipButton

      if type(CreateCustomDropdownControl) == "function" then
        local dropdown = CreateCustomDropdownControl(nil, row, {
          width = 220,
          buttonHeight = 24,
          rowHeight = 20,
          maxVisibleEntries = 12,
          text = "No recommendation",
        })
        dropdown:SetPoint("RIGHT", row, "RIGHT", -12, 0)
        row.dropdown = dropdown
        RaisePopupControl(dropdown, frame, 30)
      end

      frame.recommendationRows[#frame.recommendationRows + 1] = row
    end
    return frame.recommendationRows
  end

  local function RefreshRecommendationsPopup(editor, profileName)
    local frame = EnsureRecommendationPopup(editor)
    if not frame then
      return false
    end

    local resolvedProfileName = trim(profileName)
    if frame.__recommendationProfileName ~= resolvedProfileName then
      frame.__recommendationProfileName = resolvedProfileName
      frame.__recommendationRaidInstanceID = nil
      frame.__recommendationEncounterID = nil
      frame.__recommendationDifficultyID = 14
      frame.__recommendationPerDifficulty = false
      local classID, specID = GetCurrentSpecFilter()
      frame.__recommendationFilterClassID = classID
      frame.__recommendationFilterSpecID = specID
    elseif frame.__recommendationFilterClassID == nil and frame.__recommendationFilterSpecID == nil then
      local classID, specID = GetCurrentSpecFilter()
      frame.__recommendationFilterClassID = classID
      frame.__recommendationFilterSpecID = specID
    end
    frame.__recommendationRefresh = function()
      RefreshRecommendationsPopup(editor, profileName)
    end

    local profile = type(GetLootProfile) == "function" and GetLootProfile(profileName) or nil
    local profileRaid = type(FindRaidEntryByProfile) == "function" and FindRaidEntryByProfile(profile) or nil
    local raids = CollectCurrentSeasonRaids(profile)
    local selectedRaid = FindRaidByInstanceID(raids, frame.__recommendationRaidInstanceID)
    if not selectedRaid and profileRaid then
      selectedRaid = FindRaidByInstanceID(raids, profileRaid.instanceID)
    end
    if not selectedRaid then
      selectedRaid = raids[1]
    end

    frame.__recommendationRaidInstanceID = selectedRaid and tonumber(selectedRaid.instanceID) or nil
    frame.__recommendationDifficultyID = tonumber(frame.__recommendationDifficultyID) or 14
    frame.__recommendationFilterClassID = tonumber(frame.__recommendationFilterClassID) or 0
    frame.__recommendationFilterSpecID = tonumber(frame.__recommendationFilterSpecID) or 0
    frame.__recommendationPerDifficulty = frame.__recommendationPerDifficulty == true

    local encounters = selectedRaid and type(CollectRaidEncounterEntries) == "function"
      and CollectRaidEncounterEntries(selectedRaid.instanceID)
      or {}
    local selectedEncounter = FindEncounterByID(encounters, frame.__recommendationEncounterID)
    frame.__recommendationEncounterID = selectedEncounter and tonumber(selectedEncounter.encounterID) or nil

    local items = selectedRaid and type(CollectRaidLootEntries) == "function"
      and CollectRaidLootEntries(
        selectedRaid.instanceID,
        frame.__recommendationEncounterID,
        frame.__recommendationDifficultyID,
        frame.__recommendationFilterClassID,
        frame.__recommendationFilterSpecID
      )
      or {}
    local options = GetRollOptions(profile)
    EnsureRows(frame, math.max(1, #items))

    if frame.popupTitle then
      frame.popupTitle:SetText("Personal Recommendations Cockpit")
    end
    if frame.emptyText then
      if #raids == 0 then
        frame.emptyText:SetText("No profile-season raids were found in the Adventure Guide.")
      elseif selectedRaid and #items == 0 then
        frame.emptyText:SetText("No loot entries were found for the selected raid, boss, and difficulty.")
      else
        frame.emptyText:SetText("No raid loot entries are available for the selected filters.")
      end
      frame.emptyText:SetShown(#items == 0)
    end

    if frame.raidDropdown then
      SetDropdownValue(frame.raidDropdown, frame.__recommendationRaidInstanceID, selectedRaid and tostring(selectedRaid.name or selectedRaid.instanceID or "?") or "Select Raid")
      if frame.raidDropdown.SetEnabled then
        frame.raidDropdown:SetEnabled(#raids > 0)
      end
      frame.raidDropdown:SetMenuBuilder(function()
        local entries = {}
        for _, raid in ipairs(raids) do
          local instanceID = tonumber(raid.instanceID)
          entries[#entries + 1] = {
            text = tostring(raid.name or instanceID or "?"),
            value = instanceID,
            checked = tonumber(frame.__recommendationRaidInstanceID) == instanceID,
            func = function()
              frame.__recommendationRaidInstanceID = instanceID
              frame.__recommendationEncounterID = nil
              RefreshRecommendationsPopup(editor, profileName)
            end,
          }
        end
        if #entries == 0 then
          entries[#entries + 1] = {
            text = "No profile-season raids",
            disabled = true,
            notCheckable = true,
          }
        end
        return {
          entries = entries,
          width = 290,
          maxVisibleEntries = 12,
          enableSearch = #entries > 12,
          searchPlaceholder = "Search raids",
        }
      end)
    end

    if frame.bossDropdown then
      SetDropdownValue(frame.bossDropdown, frame.__recommendationEncounterID, selectedEncounter and tostring(selectedEncounter.name or selectedEncounter.encounterID or "?") or "All Bosses")
      if frame.bossDropdown.SetEnabled then
        frame.bossDropdown:SetEnabled(selectedRaid ~= nil)
      end
      frame.bossDropdown:SetMenuBuilder(function()
        local entries = {
          {
            text = "All Bosses",
            checked = frame.__recommendationEncounterID == nil,
            func = function()
              frame.__recommendationEncounterID = nil
              RefreshRecommendationsPopup(editor, profileName)
            end,
          },
        }
        for _, encounter in ipairs(encounters) do
          local encounterID = tonumber(encounter.encounterID)
          entries[#entries + 1] = {
            text = tostring(encounter.name or encounterID or "?"),
            searchText = tostring(encounter.name or encounterID or "?"),
            value = encounterID,
            checked = tonumber(frame.__recommendationEncounterID) == encounterID,
            func = function()
              frame.__recommendationEncounterID = encounterID
              RefreshRecommendationsPopup(editor, profileName)
            end,
          }
        end
        if #entries == 1 and not selectedRaid then
          entries[1] = {
            text = "Select a raid first",
            disabled = true,
            notCheckable = true,
          }
        end
        return {
          entries = entries,
          width = 290,
          maxVisibleEntries = 12,
          enableSearch = #entries > 12,
          searchPlaceholder = "Search bosses",
        }
      end)
    end

    if frame.difficultyDropdown then
      SetDropdownValue(frame.difficultyDropdown, frame.__recommendationDifficultyID, GetDifficultyName(frame.__recommendationDifficultyID))
      frame.difficultyDropdown:SetMenuBuilder(function()
        local entries = {}
        for _, difficulty in ipairs(GetDifficultyEntries()) do
          local difficultyID = tonumber(difficulty.id)
          entries[#entries + 1] = {
            text = tostring(difficulty.name or difficultyID or "?"),
            value = difficultyID,
            checked = tonumber(frame.__recommendationDifficultyID) == difficultyID,
            func = function()
              frame.__recommendationDifficultyID = difficultyID
              RefreshRecommendationsPopup(editor, profileName)
            end,
          }
        end
        return {
          entries = entries,
          width = 150,
          maxVisibleEntries = 6,
        }
      end)
    end

    if frame.perDifficultyCheck then
      frame.perDifficultyCheck:SetChecked(frame.__recommendationPerDifficulty)
    end

    if frame.specFilterDropdown then
      local specFilterEntry = FindSpecFilterEntry(frame.__recommendationFilterClassID, frame.__recommendationFilterSpecID)
      SetDropdownValue(frame.specFilterDropdown, frame.__recommendationFilterSpecID, specFilterEntry and tostring(specFilterEntry.text or "All Classes / Specs") or "All Classes / Specs")
      frame.specFilterDropdown:SetMenuBuilder(function()
        local entries = {}
        for _, filterEntry in ipairs(BuildSpecFilterEntries()) do
          local classID = tonumber(filterEntry.classID) or 0
          local specID = tonumber(filterEntry.specID) or 0
          entries[#entries + 1] = {
            text = tostring(filterEntry.text or "Spec"),
            searchText = tostring(filterEntry.text or "Spec"),
            icon = filterEntry.icon,
            iconSize = filterEntry.iconSize,
            checked = tonumber(frame.__recommendationFilterClassID) == classID and tonumber(frame.__recommendationFilterSpecID) == specID,
            func = function()
              frame.__recommendationFilterClassID = classID
              frame.__recommendationFilterSpecID = specID
              RefreshRecommendationsPopup(editor, profileName)
            end,
          }
        end
        return {
          entries = entries,
          width = 360,
          maxVisibleEntries = 14,
          enableSearch = #entries > 14,
          searchPlaceholder = "Search class or spec",
        }
      end)
    end

    for index, row in ipairs(frame.recommendationRows or {}) do
      local item = items[index]
      if type(item) == "table" then
        row:ClearAllPoints()
        row:SetPoint("TOPLEFT", frame.content, "TOPLEFT", 0, -((index - 1) * 62))
        row:SetSize(770, 56)
        if row.SetFrameLevel and frame.content and frame.content.GetFrameLevel then
          row:SetFrameLevel((frame.content:GetFrameLevel() or 1) + 1)
        end
        if row.dropdown then
          RaisePopupControl(row.dropdown, frame, 30)
        end
        if row.tooltipButton then
          row.tooltipButton.__recommendationItem = item
          if row.tooltipButton.SetFrameLevel and row.GetFrameLevel then
            row.tooltipButton:SetFrameLevel((row:GetFrameLevel() or 1) + 1)
          end
        end

        if row.icon then
          row.icon:SetTexture(item.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
        end
        if row.item then
          local label = type(FormatRaidLootItemLabel) == "function" and FormatRaidLootItemLabel(item) or trim(item.name)
          row.item:SetText(label ~= "" and label or "Unknown Item")
        end

        local recommendationOptions = BuildRecommendationOptions(frame, item)
        local recommendationScope = FormatRecommendationScope(frame, item)
        local recommendation, option, status = ResolveRecommendation(profileName, item, options, recommendationOptions)
        local priority = tonumber(type(recommendation) == "table" and recommendation.priority) or nil
        if row.status then
          if status == "OK" and type(option) == "table" then
            row.status:SetText("Recommended" .. recommendationScope .. ": " .. FormatOptionText(priority or 0, option))
            row.status:SetTextColor(1.00, 0.86, 0.26, 1)
          elseif status == "MISSING" then
            local storedLabel = trim(type(recommendation) == "table" and recommendation.optionLabel or "")
            row.status:SetText("Missing" .. recommendationScope .. ": priority #" .. tostring(priority or "?") .. (storedLabel ~= "" and (" " .. storedLabel) or "") .. " no longer exists")
            row.status:SetTextColor(1.00, 0.38, 0.28, 1)
          else
            row.status:SetText("No recommendation" .. recommendationScope)
            row.status:SetTextColor(0.72, 0.72, 0.76, 1)
          end
        end

        if row.dropdown then
          if status == "OK" and type(option) == "table" then
            row.dropdown:SetText(FormatOptionText(priority or 0, option))
          elseif status == "MISSING" then
            local storedLabel = trim(type(recommendation) == "table" and recommendation.optionLabel or "")
            row.dropdown:SetText("Missing #" .. tostring(priority or "?") .. (storedLabel ~= "" and (": " .. storedLabel) or ""))
          else
            row.dropdown:SetText("No recommendation")
          end
          row.dropdown:SetMenuBuilder(function()
            local entries = {
              {
                text = "No recommendation",
                checked = status == "NONE",
                notCheckable = false,
                func = function()
                  ClearRecommendation(profileName, item, recommendationOptions)
                  RefreshRecommendationsPopup(editor, profileName)
                end,
              },
            }
            for optionIndex, rollOption in ipairs(options) do
              entries[#entries + 1] = {
                text = FormatOptionText(optionIndex, rollOption),
                searchText = FormatOptionText(optionIndex, rollOption),
                icon = type(ResolveRollOptionIcon) == "function" and ResolveRollOptionIcon(rollOption) or nil,
                checked = status == "OK" and tonumber(priority) == optionIndex,
                func = function()
                  SetRecommendation(profileName, item, optionIndex, rollOption, recommendationOptions)
                  RefreshRecommendationsPopup(editor, profileName)
                end,
              }
            end
            if #options == 0 then
              entries[#entries + 1] = {
                text = "No roll options configured",
                disabled = true,
                notCheckable = true,
              }
            end
            return {
              entries = entries,
              width = 245,
              maxVisibleEntries = 12,
              enableSearch = #entries > 12,
              searchPlaceholder = "Search options",
            }
          end)
        end
        row:Show()
      else
        row:Hide()
      end
    end

    local height = math.max(1, (#items * 62) + 8)
    frame.content:SetSize(790, height)
    return true
  end

  local function OpenLootListRecommendationsPopup(editor, profileName)
    local frame = EnsureRecommendationPopup(editor)
    if not frame then
      return false
    end
    RefreshRecommendationsPopup(editor, profileName)
    frame:Show()
    if frame.Raise then
      frame:Raise()
    end
    return true
  end

  return {
    OpenLootListRecommendationsPopup = OpenLootListRecommendationsPopup,
    RefreshRecommendationsPopup = RefreshRecommendationsPopup,
    IsLootListRecommendationsPopupShown = function(editor)
      return editor and editor.lootLedgerRecommendationsPopupFrame and editor.lootLedgerRecommendationsPopupFrame:IsShown() or false
    end,
  }
end
