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 shared = INTERNAL_API.LIDCOUNCIL_LootRuntimeUiShared_Impl
if type(shared) ~= "table" then
  error("LidCouncil missing loot runtime UI shared helpers.")
end

local createBoardFrame = INTERNAL_API.LIDCOUNCIL_CreateBoardFrame_Impl
local applyPopupBoardLayout = INTERNAL_API.LIDCOUNCIL_ApplyPopupBoardLayout_Impl
local setBoardCollapsed = INTERNAL_API.LIDCOUNCIL_SetBoardCollapsed_Impl
if type(createBoardFrame) ~= "function" or type(applyPopupBoardLayout) ~= "function" or type(setBoardCollapsed) ~= "function" then
  error("LidCouncil missing loot runtime board helpers.")
end

local GetLootProfile = shared.GetLootProfile
local GetActiveLootSession = shared.GetActiveLootSession
local ApplyFrameSkin = shared.ApplyFrameSkin
local SendSessionComm = shared.SendSessionComm
local ResolveOptionalFunction = shared.ResolveOptionalFunction
local Print = shared.Print
local DeepCopy = shared.DeepCopy
local DEFAULT_OPTIONS = shared.DEFAULT_OPTIONS
local MAX_SELECTION_NOTE_LENGTH = shared.MAX_SELECTION_NOTE_LENGTH
local REAL_SIMULATION_ITEM_COUNT = shared.REAL_SIMULATION_ITEM_COUNT
local RuntimeState = shared.RuntimeState
local UIState = shared.UIState
local TrimText = shared.TrimText
local IsRealSimulationItemKey = shared.IsRealSimulationItemKey
local ExtractItemID = shared.ExtractItemID
local NormalizeSelectionNote = shared.NormalizeSelectionNote
local ResolveOptionIconTexture = shared.ResolveOptionIconTexture
local ResolveRollOptions = shared.ResolveRollOptions
local GetSelfKey = shared.GetSelfKey
local IsSelfHost = shared.IsSelfHost
local GetJoinedSessionState = shared.GetJoinedSessionState
local GetBroadcastDistribution = shared.GetBroadcastDistribution
local EnsureRuntimeSession = shared.EnsureRuntimeSession
local RefreshBoardAndEditor = shared.RefreshBoardAndEditor
local RecordChoice = shared.RecordChoice
local BuildPendingSelection = shared.BuildPendingSelection
local BuildSimulationBoardState = shared.BuildSimulationBoardState
local BuildRealSimulationItemDefs = shared.BuildRealSimulationItemDefs
local SetBoardPreviewState = shared.SetBoardPreviewState
local PersistBoardState = shared.PersistBoardState
local ApplySimulationChoice = shared.ApplySimulationChoice
local RefreshChoicePopup

local function SaveChoicePopupPosition(frame)
  if not frame or not frame.GetPoint then
    return
  end

  local point, _, relativePoint, xOfs, yOfs = frame:GetPoint(1)
  UIState.choicePopupPosition = {
    point = TrimText(point) ~= "" and point or "CENTER",
    relativePoint = TrimText(relativePoint) ~= "" and relativePoint or (TrimText(point) ~= "" and point or "CENTER"),
    x = tonumber(xOfs) or 0,
    y = tonumber(yOfs) or 0,
  }
end

local function ApplyChoicePopupPosition(frame)
  if not frame or not frame.ClearAllPoints then
    return
  end

  local position = type(UIState.choicePopupPosition) == "table" and UIState.choicePopupPosition or nil
  frame:ClearAllPoints()
  if position then
    frame:SetPoint(
      TrimText(position.point) ~= "" and position.point or "CENTER",
      UIParent,
      TrimText(position.relativePoint) ~= "" and position.relativePoint or "CENTER",
      tonumber(position.x) or 0,
      tonumber(position.y) or 0
    )
    return
  end

  frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
end

local function NormalizePopupCharacterKey(value)
  local text = TrimText(value)
  text = text:gsub("%s*%-%s*", "-")
  text = text:gsub("%s+", "")
  return string.lower(text)
end

local function SplitPopupCharacterKey(value)
  local normalized = NormalizePopupCharacterKey(value)
  if normalized == "" then
    return "", ""
  end
  local name, realm = normalized:match("^([^%-]+)%-(.+)$")
  if name and name ~= "" then
    return name, realm or ""
  end
  return normalized, ""
end

local function ArePopupCharacterKeysSame(left, right)
  local leftKey = NormalizePopupCharacterKey(left)
  local rightKey = NormalizePopupCharacterKey(right)
  if leftKey == "" or rightKey == "" then
    return false
  end
  if leftKey == rightKey then
    return true
  end
  local leftName, leftRealm = SplitPopupCharacterKey(leftKey)
  local rightName, rightRealm = SplitPopupCharacterKey(rightKey)
  return leftName ~= "" and leftName == rightName and (leftRealm == "" or rightRealm == "")
end

local function GetSessionCommProfileName(activeSession, fallbackProfileName)
  return TrimText(type(activeSession) == "table" and activeSession.remoteProfileName) ~= ""
    and TrimText(activeSession.remoteProfileName)
    or TrimText(fallbackProfileName)
end

local function RemoveQueuedPopupItem(itemKey)
  local targetKey = TrimText(itemKey)
  if targetKey == "" then
    return
  end
  local queue = {}
  local removed = false
  for _, queuedKey in ipairs(RuntimeState.popupQueue or {}) do
    if not removed and TrimText(queuedKey) == targetKey then
      removed = true
    else
      queue[#queue + 1] = queuedKey
    end
  end
  RuntimeState.popupQueue = queue
end

local function ResolvePopupEntrySelection(entry, memberKey)
  if type(entry) ~= "table" or type(entry.selections) ~= "table" then
    return nil
  end
  local direct = entry.selections[memberKey]
  if type(direct) == "table" then
    return direct
  end
  for selectionKey, selection in pairs(entry.selections) do
    if ArePopupCharacterKeysSame(selectionKey, memberKey) and type(selection) == "table" then
      return selection
    end
  end
  return nil
end

local function ResolvePopupSelfMemberKey(activeSession)
  local selfKey = GetSelfKey()
  local normalizedSelf = NormalizePopupCharacterKey(selfKey)
  if normalizedSelf == "" then
    return ""
  end

  if type(activeSession) == "table" and type(activeSession.members) == "table" then
    for memberKey, member in pairs(activeSession.members) do
      if ArePopupCharacterKeysSame(memberKey, selfKey) and tostring(type(member) == "table" and member.status or "JOINED") == "JOINED" then
        return tostring(memberKey)
      end
    end
  end

  if type(activeSession) == "table" and ArePopupCharacterKeysSame(activeSession.host, selfKey) then
    return tostring(activeSession.host or selfKey)
  end

  return selfKey
end

local function ResolveActivePopupSessionState()
  local joined, activeSession, profileName = GetJoinedSessionState()
  if joined and type(activeSession) == "table" and TrimText(profileName) ~= "" then
    return true, activeSession, profileName
  end

  activeSession, profileName = GetActiveLootSession()
  if type(activeSession) ~= "table" or TrimText(profileName) == "" then
    return false, nil, nil
  end
  if string.upper(TrimText(activeSession.status or "ACTIVE")) ~= "ACTIVE" then
    return false, nil, nil
  end

  local selfKey = GetSelfKey()
  local normalizedSelf = NormalizePopupCharacterKey(selfKey)
  if normalizedSelf == "" then
    return false, nil, nil
  end
  if ArePopupCharacterKeysSame(activeSession.host, selfKey) then
    return true, activeSession, profileName
  end

  if type(activeSession.members) == "table" then
    for memberKey, member in pairs(activeSession.members) do
      if ArePopupCharacterKeysSame(memberKey, selfKey) and tostring(type(member) == "table" and member.status or "JOINED") == "JOINED" then
        return true, activeSession, profileName
      end
    end
  end
  return false, nil, nil
end

local function RaisePopupFrameAboveEditor(frame)
  if not frame then
    return
  end
  if frame.SetFrameStrata then
    frame:SetFrameStrata("TOOLTIP")
  end
  if frame.SetToplevel then
    frame:SetToplevel(true)
  end
  local editor = rawget(_G, "LIDCOUNCIL_Editor")
  local targetFrameLevel = 0
  if editor and editor.GetFrameLevel then
    targetFrameLevel = math.max(targetFrameLevel, tonumber(editor:GetFrameLevel()) or 0)
  end
  if UIState and UIState.boardFrame and UIState.boardFrame.GetFrameLevel then
    targetFrameLevel = math.max(targetFrameLevel, tonumber(UIState.boardFrame:GetFrameLevel()) or 0)
  end
  if frame.SetFrameLevel then
    frame:SetFrameLevel(targetFrameLevel + 140)
  end
  if frame.SetClampedToScreen then
    frame:SetClampedToScreen(true)
  end
  if frame.Raise then
    frame:Raise()
  end
end

local function CountNonSimulationRuntimeItems()
  local count = 0
  for _, itemKey in ipairs(RuntimeState.itemOrder or {}) do
    if type(RuntimeState.items[itemKey]) == "table" and not IsRealSimulationItemKey(itemKey) then
      count = count + 1
    end
  end
  return count
end

local function ClearRealSimulationRuntimeItems()
  local removeKeys = {}
  for _, itemKey in ipairs(RuntimeState.itemOrder or {}) do
    if IsRealSimulationItemKey(itemKey) then
      removeKeys[itemKey] = true
      RuntimeState.items[itemKey] = nil
    end
  end

  local itemOrder = {}
  for _, itemKey in ipairs(RuntimeState.itemOrder or {}) do
    if not removeKeys[itemKey] and type(RuntimeState.items[itemKey]) == "table" then
      itemOrder[#itemOrder + 1] = itemKey
    end
  end
  RuntimeState.itemOrder = itemOrder

  local popupQueue = {}
  for _, itemKey in ipairs(RuntimeState.popupQueue or {}) do
    if not removeKeys[itemKey] and type(RuntimeState.items[itemKey]) == "table" then
      popupQueue[#popupQueue + 1] = itemKey
    end
  end
  RuntimeState.popupQueue = popupQueue

  if removeKeys[RuntimeState.selectedItemKey] then
    RuntimeState.selectedItemKey = RuntimeState.itemOrder[1]
  end
  if #RuntimeState.itemOrder == 0 then
    RuntimeState.selectedMemberKey = nil
  end
end

local function ResolveChoicePopupSectionValue(option)
  local section = string.upper(TrimText(option and option.popupSection))
  if section == "NEED" or section == "GREEDPASS" then
    return section
  end

  local pointBehavior = string.upper(TrimText(option and option.pointBehavior))
  if pointBehavior == "OFFSPEC" or pointBehavior == "TRANSMOG" or pointBehavior == "DISENCHANT" or pointBehavior == "NONE" then
    return "GREEDPASS"
  end

  local hint = string.lower(TrimText(option and ((option.key ~= "" and option.key) or option.label)))
  hint = hint:gsub("[%s%-%_]+", "")
  if hint == "greed" or hint == "pass" or hint == "offspec" or hint == "transmog" or hint == "mog" or hint == "disenchant" or hint == "de" then
    return "GREEDPASS"
  end
  return "NEED"
end

local function EnsureChoicePopupButton(frame, index)
  frame.optionButtons = frame.optionButtons or {}
  if frame.optionButtons[index] then
    return frame.optionButtons[index]
  end

  local button = CreateFrame("Button", nil, frame, "BackdropTemplate")
  ApplyFrameSkin(button)
  button:SetSize(188, 28)
  if button.SetBackdropColor then
    button.__normalBackdrop = { 0.09, 0.12, 0.16, 0.96 }
    button.__hoverBackdrop = { 0.15, 0.20, 0.28, 0.98 }
    button:SetBackdropColor(button.__normalBackdrop[1], button.__normalBackdrop[2], button.__normalBackdrop[3], button.__normalBackdrop[4])
  end
  button:SetScript("OnEnter", function(self)
    if self.SetBackdropColor and self.__hoverBackdrop then
      self:SetBackdropColor(self.__hoverBackdrop[1], self.__hoverBackdrop[2], self.__hoverBackdrop[3], self.__hoverBackdrop[4])
    end
  end)
  button:SetScript("OnLeave", function(self)
    if self.SetBackdropColor and self.__normalBackdrop then
      self:SetBackdropColor(self.__normalBackdrop[1], self.__normalBackdrop[2], self.__normalBackdrop[3], self.__normalBackdrop[4])
    end
  end)
  local icon = button:CreateTexture(nil, "ARTWORK")
  icon:SetSize(18, 18)
  icon:SetPoint("LEFT", 8, 0)
  button.optionIcon = icon
  local label = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  label:SetPoint("LEFT", button, "LEFT", 32, 0)
  label:SetPoint("RIGHT", button, "RIGHT", -8, 0)
  label:SetJustifyH("LEFT")
  if label.SetWordWrap then
    label:SetWordWrap(false)
  end
  button.label = label

  local recommendationGlow = button:CreateTexture(nil, "BACKGROUND")
  recommendationGlow:SetTexture("Interface\\Buttons\\WHITE8x8")
  recommendationGlow:SetAllPoints(button)
  recommendationGlow:SetVertexColor(1.00, 0.82, 0.10, 0.18)
  recommendationGlow:Hide()
  button.recommendationGlow = recommendationGlow

  local recommendationText = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  recommendationText:SetPoint("RIGHT", button, "RIGHT", -8, 0)
  recommendationText:SetText("Recommended")
  recommendationText:SetTextColor(1.00, 0.86, 0.18, 1)
  recommendationText:Hide()
  button.recommendationText = recommendationText

  frame.optionButtons[index] = button
  return button
end

local function CollectOrderedChoiceOptions(options)
  local orderedOptions = {}
  if type(options) ~= "table" then
    return orderedOptions
  end

  for _, option in ipairs(options) do
    if type(option) == "table" then
      orderedOptions[#orderedOptions + 1] = option
    end
  end

  if #orderedOptions > 0 then
    return orderedOptions
  end

  local keyedOptions = {}
  for key, value in pairs(options) do
    if type(value) == "table" then
      keyedOptions[#keyedOptions + 1] = {
        sortNumber = tonumber(key),
        sortText = tostring(key or ""),
        option = value,
      }
    end
  end

  table.sort(keyedOptions, function(a, b)
    local aNumber = tonumber(a.sortNumber)
    local bNumber = tonumber(b.sortNumber)
    if aNumber and bNumber and aNumber ~= bNumber then
      return aNumber < bNumber
    end
    if aNumber and not bNumber then
      return true
    end
    if bNumber and not aNumber then
      return false
    end
    return string.lower(tostring(a.sortText or "")) < string.lower(tostring(b.sortText or ""))
  end)

  for _, entry in ipairs(keyedOptions) do
    orderedOptions[#orderedOptions + 1] = entry.option
  end
  return orderedOptions
end

local function ApplyChoicePopupButtonOption(button, option, recommended)
  if not button then
    return
  end

  local resolvedIcon = ResolveOptionIconTexture(option and option.icon, option and option.key, option and option.label)

  if button.label then
    button.label:ClearAllPoints()
    button.label:SetPoint("LEFT", button, "LEFT", 32, 0)
    button.label:SetPoint("RIGHT", button, "RIGHT", recommended and -104 or -8, 0)
    button.label:SetText(tostring(option and (option.label or option.key) or "Option"))
  end
  if button.optionIcon then
    if resolvedIcon ~= nil and resolvedIcon ~= "" then
      button.optionIcon:SetTexture(resolvedIcon)
      button.optionIcon:Show()
    else
      button.optionIcon:Hide()
    end
  end

  local fontString = button.label
  if fontString then
    fontString:SetTextColor(
      tonumber(option and option.color and option.color[1]) or 1,
      tonumber(option and option.color and option.color[2]) or 1,
      tonumber(option and option.color and option.color[3]) or 1,
      1
    )
  end

  if button.recommendationGlow then
    button.recommendationGlow:SetShown(recommended and true or false)
  end
  if button.recommendationText then
    button.recommendationText:SetShown(recommended and true or false)
  end
end

local function ResolvePersonalRecommendationForPopup(profileName, entry, options)
  local api = rawget(_G, "LIDCOUNCIL_API")
  local resolver = type(api) == "table" and api.LIDCOUNCIL_ResolvePersonalRecommendation or nil
  if type(resolver) ~= "function" or type(entry) ~= "table" then
    return nil, nil, "NONE"
  end
  local difficultyID = entry.difficultyID or entry.lootDifficultyID
  local difficultyName = entry.difficultyName
  if not difficultyID and type(GetInstanceInfo) == "function" then
    local _, _, currentDifficultyID, currentDifficultyName = GetInstanceInfo()
    difficultyID = currentDifficultyID
    difficultyName = difficultyName or currentDifficultyName
  end
  return resolver(profileName, entry.itemID, entry.link, entry.name, options, {
    difficultyID = difficultyID,
    difficultyName = difficultyName,
  })
end

local function DoesOptionMatchRecommendation(option, recommendedOption)
  if type(option) ~= "table" or type(recommendedOption) ~= "table" then
    return false
  end
  if option == recommendedOption then
    return true
  end
  local optionKey = string.lower(TrimText(option.key or option.optionKey or option.label))
  local recommendedKey = string.lower(TrimText(recommendedOption.key or recommendedOption.optionKey or recommendedOption.label))
  return optionKey ~= "" and optionKey == recommendedKey
end

local function LayoutChoicePopupButtons(frame, options, showCloseButton)
  local groupedOptions = {
    NEED = {},
    GREEDPASS = {},
  }
  local orderedOptions = {}
  for _, option in ipairs(CollectOrderedChoiceOptions(options)) do
    local section = ResolveChoicePopupSectionValue(option)
    groupedOptions[section][#groupedOptions[section] + 1] = option
  end
  for _, option in ipairs(groupedOptions.NEED) do
    orderedOptions[#orderedOptions + 1] = option
  end
  for _, option in ipairs(groupedOptions.GREEDPASS) do
    orderedOptions[#orderedOptions + 1] = option
  end

  local popupWidth = 460
  local sectionWidth = popupWidth - 32
  local needRows = math.max(1, #groupedOptions.NEED)
  local greedRows = math.max(1, #groupedOptions.GREEDPASS)
  local needSectionHeight = 44 + (needRows * 34)
  local greedSectionHeight = 44 + (greedRows * 34)
  local popupHeight = math.max(368, 200 + needSectionHeight + greedSectionHeight + (showCloseButton and 56 or 24))
  frame:SetWidth(popupWidth)
  frame:SetHeight(popupHeight)

  local function layoutSection(sectionFrame, titleText, bucket, startIndex, topOffset)
    if not sectionFrame then
      return startIndex, topOffset
    end
    sectionFrame:ClearAllPoints()
    local bucketRows = math.max(1, #bucket)
    local currentSectionHeight = 44 + (bucketRows * 34)
    sectionFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, topOffset)
    sectionFrame:SetSize(sectionWidth, currentSectionHeight)
    if sectionFrame.title then
      sectionFrame.title:SetText(titleText)
    end
    if sectionFrame.emptyText then
      if #bucket == 0 then
        sectionFrame.emptyText:Show()
      else
        sectionFrame.emptyText:Hide()
      end
    end

    local nextIndex = startIndex
    for rowIndex, option in ipairs(bucket) do
      local button = EnsureChoicePopupButton(frame, nextIndex)
      button:SetParent(frame)
      if button.SetFrameStrata then
        button:SetFrameStrata("TOOLTIP")
      end
      if button.SetFrameLevel then
        button:SetFrameLevel((frame:GetFrameLevel() or 1) + 6)
      end
      button:ClearAllPoints()
      button:SetPoint("TOPLEFT", frame, "TOPLEFT", 28, topOffset - 28 - ((rowIndex - 1) * 34))
      button:SetWidth(sectionWidth - 24)
      button:Show()
      nextIndex = nextIndex + 1
    end
    return nextIndex, topOffset - currentSectionHeight - 10
  end

  local nextIndex = 1
  local topOffset = -184
  if frame.needSectionFrame then
    frame.needSectionFrame:Show()
    nextIndex, topOffset = layoutSection(frame.needSectionFrame, "Need", groupedOptions.NEED, nextIndex, topOffset)
  end
  if frame.greedPassSectionFrame then
    frame.greedPassSectionFrame:Show()
    nextIndex, topOffset = layoutSection(frame.greedPassSectionFrame, "Greed / Pass", groupedOptions.GREEDPASS, nextIndex, topOffset)
  end

  for i = nextIndex, #(frame.optionButtons or {}) do
    frame.optionButtons[i]:Hide()
  end
  frame.__layoutOptions = orderedOptions
  return orderedOptions
end

local function CreateChoicePopup()
  if UIState.choicePopup then
    return UIState.choicePopup
  end

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

  local function ShowChoicePopupItemTooltip(self)
    local frame = self and self.__choicePopup
    if not frame or type(GameTooltip) ~= "table" then
      return
    end

    local itemLink = TrimText(frame.__itemTooltipLink)
    local itemID = ExtractItemID(frame.__itemTooltipItemID or itemLink)
    local itemName = TrimText(frame.__itemTooltipName)
    if itemLink == "" and not itemID and itemName == "" then
      return
    end

    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    if itemLink ~= "" and GameTooltip.SetHyperlink then
      GameTooltip:SetHyperlink(itemLink)
    elseif itemID and GameTooltip.SetItemByID then
      GameTooltip:SetItemByID(itemID)
    else
      GameTooltip:SetText(itemName ~= "" and itemName or ("Item #" .. tostring(itemID or "?")), 1, 0.82, 0.28, 1, true)
    end
    GameTooltip:Show()
  end

  local function SetChoicePopupItemTooltipData(frame, itemLink, itemID, itemName)
    if not frame then
      return
    end

    frame.__itemTooltipLink = TrimText(itemLink)
    frame.__itemTooltipItemID = ExtractItemID(itemID or itemLink)
    frame.__itemTooltipName = TrimText(itemName)

    local hasTooltip = frame.__itemTooltipLink ~= "" or frame.__itemTooltipItemID ~= nil or frame.__itemTooltipName ~= ""
    if frame.itemTooltipButton and frame.itemTooltipButton.EnableMouse then
      frame.itemTooltipButton:EnableMouse(hasTooltip)
    end
    if frame.itemTextTooltipButton and frame.itemTextTooltipButton.EnableMouse then
      frame.itemTextTooltipButton:EnableMouse(hasTooltip)
    end
    if not hasTooltip then
      HideChoicePopupItemTooltip()
    end
  end

  local frame = CreateFrame("Frame", "LIDCOUNCIL_LootChoicePopup", UIParent, "BackdropTemplate")
  frame:SetSize(460, 320)
  frame:SetPoint("CENTER", 0, -140)
  frame:SetFrameStrata("TOOLTIP")
  frame:SetMovable(true)
  frame:EnableMouse(true)
  frame:SetToplevel(true)
  if frame.SetClampedToScreen then
    frame:SetClampedToScreen(true)
  end
  frame:RegisterForDrag("LeftButton")
  frame:SetScript("OnDragStart", frame.StartMoving)
  frame:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    SaveChoicePopupPosition(self)
  end)
  ApplyFrameSkin(frame)
  frame:HookScript("OnShow", function(self)
    RaisePopupFrameAboveEditor(self)
  end)
  frame:Hide()

  local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  title:SetPoint("TOPLEFT", 14, -14)
  title:SetText("LidCouncil Roll Choice")
  frame.title = title

  local queueText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  queueText:SetPoint("TOPRIGHT", -14, -18)
  queueText:SetText("")
  frame.queueText = queueText

  local icon = frame:CreateTexture(nil, "ARTWORK")
  icon:SetSize(34, 34)
  icon:SetPoint("TOPLEFT", 16, -44)
  frame.icon = icon

  local itemTooltipButton = CreateFrame("Button", nil, frame)
  itemTooltipButton:SetPoint("TOPLEFT", 16, -44)
  itemTooltipButton:SetSize(34, 34)
  itemTooltipButton.__choicePopup = frame
  itemTooltipButton:EnableMouse(false)
  itemTooltipButton:SetScript("OnEnter", ShowChoicePopupItemTooltip)
  itemTooltipButton:SetScript("OnLeave", HideChoicePopupItemTooltip)
  frame.itemTooltipButton = itemTooltipButton

  local itemText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
  itemText:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, 0)
  itemText:SetWidth(364)
  itemText:SetJustifyH("LEFT")
  frame.itemText = itemText

  local itemTextTooltipButton = CreateFrame("Button", nil, frame)
  itemTextTooltipButton:SetPoint("TOPLEFT", icon, "TOPRIGHT", 8, 2)
  itemTextTooltipButton:SetSize(368, 36)
  itemTextTooltipButton.__choicePopup = frame
  itemTextTooltipButton:EnableMouse(false)
  itemTextTooltipButton:SetScript("OnEnter", ShowChoicePopupItemTooltip)
  itemTextTooltipButton:SetScript("OnLeave", HideChoicePopupItemTooltip)
  frame.itemTextTooltipButton = itemTextTooltipButton

  local hint = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  hint:SetPoint("TOPLEFT", 16, -92)
  hint:SetWidth(420)
  hint:SetJustifyH("LEFT")
  hint:SetText("Choose your LidCouncil preference for this item. Blizzard group loot is still active during this preview phase.")
  frame.hint = hint

  local commentLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  commentLabel:SetPoint("TOPLEFT", 16, -122)
  commentLabel:SetText("Comment for the host (optional):")
  frame.commentLabel = commentLabel

  local commentEdit = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
  commentEdit:SetSize(420, 22)
  commentEdit:SetPoint("TOPLEFT", 16, -142)
  commentEdit:SetAutoFocus(false)
  commentEdit:SetTextInsets(6, 6, 0, 0)
  commentEdit:SetFontObject("GameFontHighlightSmall")
  if commentEdit.SetMaxLetters then
    commentEdit:SetMaxLetters(MAX_SELECTION_NOTE_LENGTH)
  end
  commentEdit:SetScript("OnEscapePressed", function(self)
    self:ClearFocus()
  end)
  commentEdit:SetScript("OnEnterPressed", function(self)
    self:ClearFocus()
  end)
  frame.commentEdit = commentEdit

  local needSectionFrame = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  ApplyFrameSkin(needSectionFrame)
  if needSectionFrame.SetFrameLevel then
    needSectionFrame:SetFrameLevel(math.max(1, (frame:GetFrameLevel() or 1) - 1))
  end
  if needSectionFrame.SetBackdropColor then
    needSectionFrame:SetBackdropColor(0.06, 0.08, 0.12, 0.98)
  end
  local needSectionTitle = needSectionFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  needSectionTitle:SetPoint("TOPLEFT", 12, -10)
  needSectionTitle:SetText("Need")
  needSectionFrame.title = needSectionTitle
  local needEmptyText = needSectionFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  needEmptyText:SetPoint("CENTER", 0, 0)
  needEmptyText:SetText("No options")
  needSectionFrame.emptyText = needEmptyText
  frame.needSectionFrame = needSectionFrame

  local greedPassSectionFrame = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  ApplyFrameSkin(greedPassSectionFrame)
  if greedPassSectionFrame.SetFrameLevel then
    greedPassSectionFrame:SetFrameLevel(math.max(1, (frame:GetFrameLevel() or 1) - 1))
  end
  if greedPassSectionFrame.SetBackdropColor then
    greedPassSectionFrame:SetBackdropColor(0.06, 0.08, 0.12, 0.98)
  end
  local greedPassSectionTitle = greedPassSectionFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  greedPassSectionTitle:SetPoint("TOPLEFT", 12, -10)
  greedPassSectionTitle:SetText("Greed / Pass")
  greedPassSectionFrame.title = greedPassSectionTitle
  local greedPassEmptyText = greedPassSectionFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  greedPassEmptyText:SetPoint("CENTER", 0, 0)
  greedPassEmptyText:SetText("No options")
  greedPassSectionFrame.emptyText = greedPassEmptyText
  frame.greedPassSectionFrame = greedPassSectionFrame

  local closeBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
  closeBtn:SetSize(70, 22)
  closeBtn:SetPoint("BOTTOMRIGHT", -16, 12)
  closeBtn:SetText("Close")
  closeBtn:SetScript("OnClick", function()
    frame:Hide()
  end)
  closeBtn:Hide()
  frame.closeBtn = closeBtn

  frame.optionButtons = {}
  frame.SetItemTooltipData = SetChoicePopupItemTooltipData
  frame:HookScript("OnHide", HideChoicePopupItemTooltip)

  UIState.choicePopup = frame
  return frame
end

local function SetChoicePopupComment(frame, value)
  if not frame or not frame.commentEdit or not frame.commentEdit.SetText then
    return
  end
  frame.commentEdit:SetText(NormalizeSelectionNote(value))
  if frame.commentEdit.ClearFocus then
    frame.commentEdit:ClearFocus()
  end
end

local function SubmitCurrentPopupChoice(option, noteText, activeSessionOverride, profileNameOverride, itemKeyOverride)
  local joined, activeSession, profileName
  if type(activeSessionOverride) == "table" and TrimText(profileNameOverride) ~= "" then
    joined = true
    activeSession = activeSessionOverride
    profileName = TrimText(profileNameOverride)
  else
    joined, activeSession, profileName = ResolveActivePopupSessionState()
  end
  if not joined or not activeSession then
    return false
  end
  if #RuntimeState.popupQueue == 0 and TrimText(itemKeyOverride) == "" then
    return false
  end

  local itemKey = TrimText(itemKeyOverride) ~= "" and TrimText(itemKeyOverride) or RuntimeState.popupQueue[1]
  local entry = RuntimeState.items[itemKey]
  local selfKey = ResolvePopupSelfMemberKey(activeSession)
  if not entry or selfKey == "" then
    RemoveQueuedPopupItem(itemKey)
    return false
  end

  local selectionOption = DeepCopy(option or {})
  selectionOption.note = NormalizeSelectionNote(noteText)
  local selectionKey = string.lower(TrimText(selectionOption.key or selectionOption.optionKey or selectionOption.label))
  if selectionKey ~= "" and selectionKey ~= "pending" then
    selectionOption.rollValue = math.random(1, 100)
  else
    selectionOption.rollValue = nil
  end
  local existingSelection = ResolvePopupEntrySelection(entry, selfKey)
  selectionOption.selectedSeq = (tonumber(type(existingSelection) == "table" and existingSelection.selectedSeq) or 0) + 1
  RecordChoice(entry, selfKey, selectionOption, "LOCAL")
  local storedSelection = ResolvePopupEntrySelection(entry, selfKey)
  local selectedAt = tonumber(type(storedSelection) == "table" and storedSelection.selectedAt) or (time and time() or 0)
  local selectedSeq = tonumber(type(storedSelection) == "table" and storedSelection.selectedSeq) or 1
  local commProfileName = GetSessionCommProfileName(activeSession, profileName)
  if IsSelfHost(activeSession) and type(SendSessionComm) == "function" then
    local distribution = GetBroadcastDistribution()
    if distribution then
      SendSessionComm(
        "RCHOICE",
        distribution,
        nil,
        activeSession.id,
        commProfileName,
        itemKey,
        selfKey,
        selectionOption.key,
        selectionOption.label,
        selectionOption.note or "",
        selectionOption.rollValue or "",
        selectedAt,
        selectedSeq
      )
    end
  elseif type(SendSessionComm) == "function" then
    SendSessionComm(
      "CHOICE",
      "WHISPER",
      activeSession.host,
      activeSession.id,
      commProfileName,
      itemKey,
      selfKey,
      selectionOption.key,
      selectionOption.label,
      selectionOption.note or "",
      selectionOption.rollValue or "",
      selectedAt,
      selectedSeq
    )
  end

  RemoveQueuedPopupItem(itemKey)
  RuntimeState.selectedItemKey = itemKey
  if UIState.choicePopup and UIState.choicePopup.commentEdit then
    SetChoicePopupComment(UIState.choicePopup, "")
  end
  RefreshBoardAndEditor({
    reason = "choice",
    itemKey = itemKey,
  })
  return true
end

local function PreviewRollChoicePopup(profileName)
  local frame = CreateChoicePopup()
  local _, resolvedProfileName = GetLootProfile(profileName)
  local options = ResolveRollOptions(nil, resolvedProfileName)
  if type(options) ~= "table" or #options == 0 then
    options = DEFAULT_OPTIONS
  end

  frame.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
  frame.title:SetText("LidCouncil Popup Preview")
  frame.itemText:SetText("|cffd1b35fSample Raid Item|r")
  if frame.SetItemTooltipData then
    frame:SetItemTooltipData(nil, nil, "Sample Raid Item")
  end
  frame.queueText:SetText("Preview: Member")
  frame.hint:SetText("Preview of the joined-player LidCouncil popup for profile \"" .. tostring(resolvedProfileName or "Default") .. "\". This does not send any selection.")
  if frame.commentLabel then
    frame.commentLabel:Show()
  end
  if frame.commentEdit then
    frame.commentEdit:Show()
  end
  SetChoicePopupComment(frame, "")
  if frame.closeBtn then
    frame.closeBtn:Show()
  end
  local orderedOptions = LayoutChoicePopupButtons(frame, options, true)
  if type(orderedOptions) ~= "table" or #orderedOptions == 0 then
    orderedOptions = LayoutChoicePopupButtons(frame, DEFAULT_OPTIONS, true)
  end

  for i = 1, #orderedOptions do
    local button = EnsureChoicePopupButton(frame, i)
    local option = orderedOptions[i]
    ApplyChoicePopupButtonOption(button, option)
    button:SetScript("OnClick", function() end)
    button:Show()
  end

  frame:Show()
end

local function OpenSimulationChoicePopup(preview, itemKey)
  if type(preview) ~= "table" or type(preview.items) ~= "table" then
    return
  end

  local entry = preview.items[itemKey]
  if type(entry) ~= "table" then
    return
  end

  local options = ResolveRollOptions(nil, TrimText(preview.sourceProfileName ~= "" and preview.sourceProfileName or preview.profileName))
  if type(options) ~= "table" or #options == 0 then
    options = DEFAULT_OPTIONS
  end
  local frame = CreateChoicePopup()
  preview.selectedItemKey = itemKey
  frame.title:SetText("LidCouncil Roll Simulation")
  frame.icon:SetTexture(entry.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
  frame.itemText:SetText(entry.link or entry.name or itemKey)
  if frame.SetItemTooltipData then
    frame:SetItemTooltipData(entry.link, entry.itemID, entry.name or itemKey)
  end
  local totalItems = tonumber(preview.totalItems) or #(preview.itemOrder or {})
  local pendingCount = #(preview.popupQueue or {})
  local currentIndex = math.max(1, (totalItems - pendingCount) + 1)
  local isPending = false
  for _, queuedKey in ipairs(preview.popupQueue or {}) do
    if queuedKey == itemKey then
      isPending = true
      break
    end
  end
  frame.queueText:SetText(isPending and string.format("Simulation %d/%d", currentIndex, totalItems) or "Simulation Review")
  frame.hint:SetText(isPending
    and "Choose a roll option as if you were a joined session member. The host-side Live Board will update immediately after the click."
    or "This item already has simulated roll results. You can inspect it in the Live Board or rerun the full simulation from the button.")
  if frame.commentLabel then
    frame.commentLabel:Show()
  end
  if frame.commentEdit then
    frame.commentEdit:Show()
  end
  local existingSelection = entry.selections and entry.selections[preview.selfKey or ""] or nil
  SetChoicePopupComment(frame, existingSelection and existingSelection.note or "")
  if frame.closeBtn then
    frame.closeBtn:Show()
  end
  local orderedOptions = LayoutChoicePopupButtons(frame, options, true)
  if type(orderedOptions) ~= "table" or #orderedOptions == 0 then
    frame.hint:SetText("No roll options were found for this simulation. LidCouncil is falling back to the default options.")
    orderedOptions = LayoutChoicePopupButtons(frame, DEFAULT_OPTIONS, true)
  end

  for i = 1, #orderedOptions do
    local button = EnsureChoicePopupButton(frame, i)
    local option = orderedOptions[i]
    ApplyChoicePopupButtonOption(button, option)
    button:SetScript("OnClick", function()
      local noteText = UIState.choicePopup and UIState.choicePopup.commentEdit and UIState.choicePopup.commentEdit:GetText() or ""
      local nextItemKey = ApplySimulationChoice(preview, itemKey, {
        key = TrimText(option.key or option.label),
        label = TrimText(option.label or option.key),
        color = DeepCopy(option.color or { 0.82, 0.82, 0.86 }),
        icon = ResolveOptionIconTexture(option.icon, option.key, option.label),
        pointBehavior = option.pointBehavior,
        popupSection = option.popupSection,
        note = noteText,
      }, options)
      PersistBoardState(UIState.boardFrame)
      frame:Hide()
      if UIState.boardFrame and UIState.boardFrame.RefreshBoard then
        UIState.boardFrame:RefreshBoard()
      end
      if nextItemKey and preview.items and preview.items[nextItemKey] then
        OpenSimulationChoicePopup(preview, nextItemKey)
      elseif type(Print) == "function" then
        Print("[Simulation] All simulated items have finished rolling. Use the Live Board to review and assign them.")
      end
    end)
    button:Show()
  end

  frame:Show()
end

local function ShowCurrentChoicePopup(activeSession, profileName)
  local frame = CreateChoicePopup()
  if type(activeSession) ~= "table" or TrimText(profileName) == "" then
    frame:Hide()
    return false
  end

  EnsureRuntimeSession(activeSession, profileName)
  local selfKey = ResolvePopupSelfMemberKey(activeSession)

  while #RuntimeState.popupQueue > 0 do
    local headKey = RuntimeState.popupQueue[1]
    local entry = RuntimeState.items[headKey]
    local selection = ResolvePopupEntrySelection(entry, selfKey)
    if not entry or (selection and tostring(selection.optionKey or "") ~= "pending") then
      table.remove(RuntimeState.popupQueue, 1)
    else
      break
    end
  end

  if #RuntimeState.popupQueue == 0 then
    frame:Hide()
    return false
  end

  local itemKey = RuntimeState.popupQueue[1]
  local entry = RuntimeState.items[itemKey]
  if not entry then
    frame:Hide()
    return false
  end

  local options = ResolveRollOptions(activeSession, profileName)
  local isRealSimulation = IsRealSimulationItemKey(itemKey)
  frame.icon:SetTexture(entry.icon)
  frame.title:SetText(isRealSimulation and "LidCouncil Real Simulation" or "LidCouncil Roll Choice")
  frame.itemText:SetText(entry.link or entry.name or itemKey)
  if frame.SetItemTooltipData then
    frame:SetItemTooltipData(entry.link, entry.itemID, entry.name or itemKey)
  end
  frame.queueText:SetText((isRealSimulation and "Pending Simulation Items: " or "Pending: ") .. tostring(#RuntimeState.popupQueue))
  frame.hint:SetText(isRealSimulation
    and "Choose your LidCouncil preference for this real-session simulation item. No Loot List points will be written."
    or "Choose your LidCouncil preference for this item. Blizzard group loot is still active during this preview phase.")
  if frame.commentLabel then
    frame.commentLabel:Show()
  end
  if frame.commentEdit then
    frame.commentEdit:Show()
  end
  local existingSelection = ResolvePopupEntrySelection(entry, selfKey)
  SetChoicePopupComment(frame, existingSelection and existingSelection.note or "")
  if frame.closeBtn then
    frame.closeBtn:Hide()
  end
  local orderedOptions = LayoutChoicePopupButtons(frame, options, false)
  local _, recommendedOption, recommendationStatus = ResolvePersonalRecommendationForPopup(profileName, entry, options)
  if recommendationStatus == "MISSING" and frame.hint then
    frame.hint:SetText((frame.hint:GetText() or "") .. " Your personal recommendation points to a removed roll priority.")
  end

  for i = 1, #orderedOptions do
    local button = EnsureChoicePopupButton(frame, i)
    local option = orderedOptions[i]
    ApplyChoicePopupButtonOption(button, option, DoesOptionMatchRecommendation(option, recommendedOption))
    button:SetScript("OnClick", function()
      local noteText = UIState.choicePopup and UIState.choicePopup.commentEdit and UIState.choicePopup.commentEdit:GetText() or ""
      local committed = SubmitCurrentPopupChoice({
        key = TrimText(option.key or option.label),
        label = TrimText(option.label or option.key),
        color = DeepCopy(option.color or { 0.82, 0.82, 0.86 }),
        icon = ResolveOptionIconTexture(option.icon, option.key, option.label),
        pointBehavior = option.pointBehavior,
        popupSection = option.popupSection,
      }, noteText, activeSession, profileName, itemKey)
      if committed then
        ShowCurrentChoicePopup(activeSession, profileName)
      else
        RefreshChoicePopup()
      end
    end)
    button:Show()
  end

  ApplyChoicePopupPosition(frame)
  RaisePopupFrameAboveEditor(frame)
  frame:Show()
  if frame.Raise then
    frame:Raise()
  end
  return true
end

local function RunLiveLootSimulation(profileName)
  local frame = createBoardFrame()
  local previewState, previewError = BuildSimulationBoardState(profileName)
  if type(previewState) ~= "table" then
    if type(Print) == "function" then
      Print(tostring(previewError or "Simulation profile unavailable."))
    end
    return
  end
  SetBoardPreviewState(frame, previewState)
  PersistBoardState(frame)
  if type(Print) == "function" and type(frame.__previewState) == "table" then
    Print(
      string.format(
        "[Simulation] Using the settings from profile \"%s\" for this run. Awarded simulated items will be added to the Simulation loot list.",
        tostring(frame.__previewState.sourceProfileName or profileName or "Default")
      )
    )
  end
  if not frame.__embeddedParent then
    applyPopupBoardLayout(frame)
  end
  frame:Show()
  frame:RefreshBoard()
  setBoardCollapsed(frame, false)
  OpenSimulationChoicePopup(frame.__previewState, frame.__previewState.selectedItemKey)
end

local function RunLiveLootRealSimulation(profileName)
  local activeSession, activeProfileName = GetActiveLootSession()
  if not activeSession or TrimText(activeProfileName) == "" then
    if type(Print) == "function" then
      Print("Start a session before running a real simulation.")
    end
    return
  end
  if string.upper(TrimText(activeSession.status or "ACTIVE")) ~= "ACTIVE" then
    if type(Print) == "function" then
      Print("Start the prepared session before running a real simulation.")
    end
    return
  end

  local resolvedProfileName = TrimText(profileName ~= "" and profileName or activeProfileName)
  if resolvedProfileName ~= "" and resolvedProfileName ~= TrimText(activeProfileName) then
    if type(Print) == "function" then
      Print("Real simulation only works for the active session profile \"" .. tostring(activeProfileName) .. "\".")
    end
    return
  end

  if not IsSelfHost(activeSession) then
    if type(Print) == "function" then
      Print("Only the active session host can run a real simulation.")
    end
    return
  end

  if not EnsureRuntimeSession(activeSession, activeProfileName) then
    if type(Print) == "function" then
      Print("Could not prepare the active session runtime for a real simulation.")
    end
    return
  end

  if CountNonSimulationRuntimeItems() > 0 then
    if type(Print) == "function" then
      Print("Finish or clear the current Live Board items before running a real simulation.")
    end
    return
  end

  ClearRealSimulationRuntimeItems()

  local itemDefs, sourceLabel = BuildRealSimulationItemDefs(activeProfileName, REAL_SIMULATION_ITEM_COUNT)
  if type(itemDefs) ~= "table" or #itemDefs == 0 then
    if type(Print) == "function" then
      Print("No real simulation items are available for the active profile.")
    end
    return
  end

  local selfKey = ResolvePopupSelfMemberKey(activeSession)
  local startedAt = (type(GetServerTime) == "function" and GetServerTime()) or (time and time() or 0)
  for index, itemDef in ipairs(itemDefs) do
    local itemKey = string.format("TESTSIM-%d-%d", startedAt, index)
    local displayName = TrimText(type(itemDef) == "table" and itemDef.name or "")
    if displayName == "" then
      displayName = "Simulation Item " .. tostring(index)
    end
    local entry = {
      key = itemKey,
      sessionId = tostring(activeSession.id or ""),
      profileName = tostring(activeProfileName or ""),
      name = displayName,
      link = type(itemDef) == "table" and itemDef.link or nil,
      icon = type(itemDef) == "table" and itemDef.icon or "Interface\\Icons\\INV_Misc_QuestionMark",
      quality = tonumber(type(itemDef) == "table" and itemDef.quality) or nil,
      itemID = ExtractItemID(type(itemDef) == "table" and (itemDef.itemID or itemDef.link) or nil),
      itemLevel = tonumber(type(itemDef) == "table" and itemDef.itemLevel) or nil,
      difficultyID = tonumber(type(itemDef) == "table" and itemDef.difficultyID) or nil,
      difficultyName = type(itemDef) == "table" and itemDef.difficultyName or nil,
      encounterName = TrimText(type(itemDef) == "table" and itemDef.encounterName or "") ~= ""
        and tostring(itemDef.encounterName)
        or "Raid Encounter",
      instanceName = TrimText(type(itemDef) == "table" and itemDef.instanceName or "") ~= ""
        and tostring(itemDef.instanceName)
        or "Raid Simulation",
      createdAt = startedAt + index,
      selections = {},
      votesByVoter = {},
      voteUpdatedAtByVoter = {},
      voteSequenceByVoter = {},
    }

    for memberKey, member in pairs(activeSession.members or {}) do
      if tostring(type(member) == "table" and member.status or "JOINED") == "JOINED" then
        entry.selections[memberKey] = BuildPendingSelection()
      end
    end
    if selfKey ~= "" and type(ResolvePopupEntrySelection(entry, selfKey)) ~= "table" then
      entry.selections[selfKey] = BuildPendingSelection()
    end

    RuntimeState.items[itemKey] = entry
    RuntimeState.itemOrder[#RuntimeState.itemOrder + 1] = itemKey
    RuntimeState.popupQueue[#RuntimeState.popupQueue + 1] = itemKey
  end

  RuntimeState.selectedItemKey = RuntimeState.itemOrder[1]
  RuntimeState.selectedMemberKey = nil
  RuntimeState.boardUpdatedAt = math.max(tonumber(RuntimeState.boardUpdatedAt) or 0, startedAt)
  PersistBoardState()

  local popupOpened = ShowCurrentChoicePopup(activeSession, activeProfileName)

  local frame = createBoardFrame()
  SetBoardPreviewState(frame, nil)
  if not frame.__embeddedParent then
    applyPopupBoardLayout(frame)
  end
  frame:Show()
  frame:RefreshBoard()
  setBoardCollapsed(frame, false)

  local sendBoardSync = ResolveOptionalFunction("LIDCOUNCIL_SendActiveLiveBoardSyncFlow")
  if type(sendBoardSync) == "function" then
    sendBoardSync()
  end
  RefreshBoardAndEditor({
    reason = "track",
    itemKey = RuntimeState.selectedItemKey,
    editorRefresh = { refreshPages = { LiveBoard = true } },
  })

  if not popupOpened and type(RefreshChoicePopup) == "function" then
    if not RefreshChoicePopup() then
      ShowCurrentChoicePopup(activeSession, activeProfileName)
    end
  elseif not popupOpened then
    ShowCurrentChoicePopup(activeSession, activeProfileName)
  end

  if type(Print) == "function" then
    local sourceText = TrimText(sourceLabel or "")
    if sourceText ~= "" then
      sourceText = " from " .. sourceText
    end
    Print(
      string.format(
        "[Real Simulation] Sent %d simulated raid item(s)%s to the active session. Roll choices update on the Live Board, but no Loot List points will be written.",
        #itemDefs,
        sourceText
      )
    )
  end
end

RefreshChoicePopup = function()
  local joined, activeSession, profileName = ResolveActivePopupSessionState()
  if not joined or not activeSession then
    local frame = CreateChoicePopup()
    frame:Hide()
    return false
  end
  if activeSession.sharedProfileReady == false then
    if not activeSession.__sharedProfilePendingNoticeShown and type(Print) == "function" then
      Print(
        "Waiting for the session host's bootstrap copy before LidCouncil roll choices can open."
        .. " Remaining shared history can keep syncing afterward."
      )
      activeSession.__sharedProfilePendingNoticeShown = true
    end
    local frame = CreateChoicePopup()
    frame:Hide()
    return false
  end
  activeSession.__sharedProfilePendingNoticeShown = nil
  return ShowCurrentChoicePopup(activeSession, profileName)
end

INTERNAL_API.LIDCOUNCIL_OpenSimulationChoicePopup_Impl = OpenSimulationChoicePopup
INTERNAL_API.LIDCOUNCIL_PreviewRollChoicePopup_Impl = PreviewRollChoicePopup
INTERNAL_API.LIDCOUNCIL_RunLiveLootSimulation_Impl = RunLiveLootSimulation
INTERNAL_API.LIDCOUNCIL_RunLiveLootRealSimulation_Impl = RunLiveLootRealSimulation
INTERNAL_API.LIDCOUNCIL_RefreshChoicePopup_Impl = RefreshChoicePopup
