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

  local TrimText = deps.TrimText
  local ResolveLootAwardOutcome = deps.ResolveLootAwardOutcome
  local ResolveLootPointValue = deps.ResolveLootPointValue
  local GetBoardPreviewState = deps.GetBoardPreviewState
  local EnsurePreviewBoardContext = deps.EnsurePreviewBoardContext
  local ResolveLootLedgerTotals = deps.ResolveLootLedgerTotals
  local EnsurePreviewSelectedMember = deps.EnsurePreviewSelectedMember
  local FormatSelectionLabel = deps.FormatSelectionLabel
  local BuildMemberDisplayText = deps.BuildMemberDisplayText
  local GetEntryVoteTargetForVoter = deps.GetEntryVoteTargetForVoter
  local GetSelfKey = deps.GetSelfKey
  local IsRealSimulationItemKey = deps.IsRealSimulationItemKey
  local UpdateBoardMemberRows = deps.UpdateBoardMemberRows
  local ResolveEntryVotedKey = deps.ResolveEntryVotedKey
  local CountPendingBoardSelections = deps.CountPendingBoardSelections
  local SetBoardAwardStatus = deps.SetBoardAwardStatus
  local UpdateAssignDropdown = deps.UpdateAssignDropdown
  local BuildAssignmentTargetText = deps.BuildAssignmentTargetText
  local GetLootProfileAccessInfo = deps.GetLootProfileAccessInfo
  local RuntimeState = deps.RuntimeState
  local ResolveRuntimeItemDisplayText = deps.ResolveRuntimeItemDisplayText

  local function GetSimulationPreviewAccess(preview)
    if not preview or not preview.isSimulation or type(GetLootProfileAccessInfo) ~= "function" then
      return nil
    end
    local profileName = TrimText(preview.profileName)
    if profileName == "" then
      profileName = "Simulation"
    end
    local rootProvider = rawget(_G, "LIDCOUNCIL_RootDB")
    local root = type(rootProvider) == "function" and rootProvider() or nil
    if type(root) == "table" and (type(root.profiles) ~= "table" or type(root.profiles[profileName]) ~= "table") then
      return nil
    end
    local access = GetLootProfileAccessInfo(profileName)
    if type(access) ~= "table" then
      return nil
    end
    return access
  end

  local function CanSelfUseSimulationPreviewBoard(preview)
    local access = GetSimulationPreviewAccess(preview)
    return type(access) == "table" and access.canOpenLiveBoard and true or false
  end

  local function CanSelfAssignSimulationPreview(preview)
    local access = GetSimulationPreviewAccess(preview)
    return type(access) == "table" and access.canAssignLoot and true or false
  end

  local function SetBoardDetailTooltipState(frame, entry, itemKey)
    local button = frame and frame.detailTooltipButton or nil
    if not button then
      return
    end
    local hasEntry = type(entry) == "table"
    button.__itemTooltipLink = hasEntry and entry.link or nil
    button.__itemTooltipItemID = hasEntry and (entry.itemID or entry.link) or nil
    button.__itemTooltipName = hasEntry and (entry.name or itemKey) or itemKey
    button.__itemTooltipItemLevel = hasEntry and tonumber(entry.itemLevel) or nil
    local hasTooltip = hasEntry
      and (TrimText(button.__itemTooltipLink) ~= "" or button.__itemTooltipItemID ~= nil or TrimText(button.__itemTooltipName) ~= "")
    if button.EnableMouse then
      button:EnableMouse(hasTooltip and true or false)
    end
  end

  local function ResolveBoardProjectedPoints(profileName, session, entry, memberKey)
    if type(entry) ~= "table" then
      return 0
    end
    local selection = type(entry.selections) == "table" and entry.selections[memberKey] or nil
    if type(ResolveLootAwardOutcome) == "function" then
      local points = ResolveLootAwardOutcome(
        profileName,
        entry.name or entry.link or entry.key or "?",
        entry.itemLevel,
        entry.itemID,
        entry.link,
        selection,
        type(session) == "table" and session.rollOptions or nil
      )
      return tonumber(points) or 0
    end
    if type(ResolveLootPointValue) == "function" then
      return tonumber((ResolveLootPointValue(profileName, entry.name or entry.link or entry.key, entry.itemLevel, entry.itemID, entry.link))) or 0
    end
    return tonumber(entry.projectedPoints) or 0
  end

  local function GetAwardedSelectionLabel(entry)
    if type(entry) ~= "table" then
      return ""
    end

    local awardedTo = TrimText(entry.awardedTo)
    local selections = type(entry.selections) == "table" and entry.selections or nil
    if awardedTo == "" or not selections then
      return ""
    end

    local selection = selections[awardedTo]
    if type(selection) ~= "table" then
      local targetNeedle = string.lower(awardedTo)
      for memberKey, candidate in pairs(selections) do
        if string.lower(TrimText(memberKey)) == targetNeedle and type(candidate) == "table" then
          selection = candidate
          break
        end
      end
    end

    return TrimText(type(selection) == "table" and (selection.label ~= "" and selection.label or selection.optionKey) or "")
  end

  local function IsManualVisibleBoardEntry(entry)
    return string.upper(TrimText(type(entry) == "table" and entry.distributionMode or "")) == "MANUAL_VISIBLE"
  end

  local function BuildAwardStatusDetail(profileName, entry)
    if IsManualVisibleBoardEntry(entry) then
      return "This item was not auto-passed or distributed by LidCouncil.\nIt stays visible for verification only and is not added to the loot list unless you add manual note points."
    end

    local awardedTo = TrimText(type(entry) == "table" and entry.awardedTo or "")
    local awardedPoints = tonumber(type(entry) == "table" and entry.awardedPoints) or 0
    local optionLabel = GetAwardedSelectionLabel(entry)
    local isRealSimulationItem = type(IsRealSimulationItemKey) == "function" and IsRealSimulationItemKey(type(entry) == "table" and entry.key or "") or false
    local parts = {}

    if isRealSimulationItem then
      parts[#parts + 1] = string.format("%s received this real simulation item.", awardedTo ~= "" and awardedTo or "?")
    else
      parts[#parts + 1] = string.format("%s received %d point(s).", awardedTo ~= "" and awardedTo or "?", awardedPoints)
    end

    if optionLabel ~= "" then
      parts[#parts + 1] = "Roll Option: " .. optionLabel
    end
    if isRealSimulationItem then
      parts[#parts + 1] = "No Loot List points were recorded."
    end

    return table.concat(parts, "\n")
  end

  local function HasLocalBoardVote(entry)
    if type(GetEntryVoteTargetForVoter) ~= "function" or type(GetSelfKey) ~= "function" then
      return false
    end
    local selfKey = TrimText(GetSelfKey())
    return selfKey ~= "" and TrimText(GetEntryVoteTargetForVoter(entry, selfKey)) ~= ""
  end

  local function FormatBoardQueueRecipient(entry)
    local recipient = TrimText(type(entry) == "table" and entry.awardedTo or "")
    if recipient == "" then
      return "", ""
    end
    local characterName = recipient:match("^([^%-]+)") or recipient
    if #characterName > 10 then
      characterName = characterName:sub(1, 9) .. "."
    end
    return "To " .. characterName, recipient
  end

  local function SetBoardItemButtonState(button, entry, itemKey, titleText, selected, hasLocalVote, recipientLabel, recipientName)
    if not button then
      return
    end
    button.__itemKey = itemKey
    button.__itemTooltipLink = type(entry) == "table" and entry.link or nil
    button.__itemTooltipItemID = type(entry) == "table" and (entry.itemID or entry.link) or nil
    button.__itemTooltipName = type(entry) == "table" and (entry.name or itemKey) or itemKey
    button.__itemTooltipItemLevel = type(entry) == "table" and tonumber(entry.itemLevel) or nil
    button.__itemSelected = selected and true or false
    button.__hasLocalVote = hasLocalVote and true or false
    button.__awardRecipient = recipientName or ""
    if button.itemIcon then
      local texture = type(entry) == "table" and entry.icon or nil
      button.itemIcon:SetTexture(texture or "Interface\\Icons\\INV_Misc_QuestionMark")
      button.itemIcon:Show()
    end
    if button.itemText then
      button.itemText:ClearAllPoints()
      button.itemText:SetPoint("LEFT", button, "LEFT", hasLocalVote and 44 or 28, 0)
      button.itemText:SetPoint("RIGHT", button, "RIGHT", recipientLabel ~= "" and -92 or -6, 0)
      button.itemText:SetText(titleText)
      if selected then
        button.itemText:SetTextColor(1.00, 0.90, 0.42, 1)
      else
        button.itemText:SetTextColor(0.92, 0.92, 0.96, 1)
      end
    end
    if button.localVoteMarker then
      if hasLocalVote then
        button.localVoteMarker:SetVertexColor(0.30, 0.66, 1.00, 1.00)
        button.localVoteMarker:Show()
      else
        button.localVoteMarker:Hide()
      end
    end
    if button.recipientText then
      if recipientLabel ~= "" then
        button.recipientText:SetText(recipientLabel)
        button.recipientText:Show()
      else
        button.recipientText:SetText("")
        button.recipientText:Hide()
      end
    end
    if button.__bg then
      if selected then
        button.__bg:SetVertexColor(0.22, 0.18, 0.08, 0.96)
      else
        button.__bg:SetVertexColor(0.09, 0.08, 0.10, 0.55)
      end
    end
    if button.__highlight then
      button.__highlight:SetShown(selected)
      if selected then
        button.__highlight:SetVertexColor(0.90, 0.74, 0.18, 0.20)
      end
    end
    button:Show()
  end

  local function BuildBoardItemButtonTitle(entry, itemKey)
    local title = type(ResolveRuntimeItemDisplayText) == "function"
      and ResolveRuntimeItemDisplayText(entry, itemKey)
      or tostring((type(entry) == "table" and entry.name) or itemKey or "?")
    if IsManualVisibleBoardEntry(entry) then
      title = "|TInterface\\FriendsFrame\\StatusIcon-DnD:14:14:0:0|t " .. title
    end
    if type(entry) == "table" and entry.awardedAt then
      title = "|TInterface\\Buttons\\UI-CheckBox-Check:14:14:0:0|t " .. title
    elseif type(entry) == "table" then
      local expectedCount = 0
      local pendingCount = 0
      for _, selection in pairs(type(entry.selections) == "table" and entry.selections or {}) do
        if type(selection) == "table" then
          expectedCount = expectedCount + 1
          local optionKey = string.lower(TrimText(selection.optionKey or selection.label or "pending"))
          if optionKey == "" or optionKey == "pending" then
            pendingCount = pendingCount + 1
          end
        end
      end
      local stateIcon = expectedCount > 0 and pendingCount == 0
        and "Interface\\RaidFrame\\ReadyCheck-Ready"
        or "Interface\\RaidFrame\\ReadyCheck-Waiting"
      title = "|T" .. stateIcon .. ":14:14:0:0|t " .. title
    end
    return title
  end

  local function IsBoardItemMarkedTraded(itemKey, entry)
    if type(entry) == "table" and string.upper(TrimText(entry.tradeState)) == "TRADED" and tonumber(entry.tradedAt) then
      local tradedAt = tonumber(entry.tradedAt) or 0
      local awardUpdatedAt = tonumber(entry.awardUpdatedAt or entry.awardedAt) or 0
      if awardUpdatedAt <= 0 or tradedAt >= awardUpdatedAt then
        return true
      end
    end
    local tradeHelper = type(RuntimeState) == "table" and RuntimeState.tradeHelper or nil
    local state = type(tradeHelper) == "table" and tradeHelper[TrimText(itemKey)] or nil
    if type(state) ~= "table" or string.upper(TrimText(state.state)) ~= "TRADED" then
      return false
    end
    local stateUpdatedAt = tonumber(state.updatedAt) or 0
    local awardUpdatedAt = tonumber(type(entry) == "table" and (entry.awardUpdatedAt or entry.awardedAt)) or 0
    return awardUpdatedAt <= 0 or stateUpdatedAt >= awardUpdatedAt
  end

  local function SelectNextUnawardedBoardItem(itemOrder, items, currentItemKey)
    local fallbackKey = TrimText(currentItemKey)
    for index = #(itemOrder or {}), 1, -1 do
      local itemKey = itemOrder[index]
      local entry = type(items) == "table" and items[itemKey] or nil
      if type(entry) == "table" and not entry.awardedAt and not IsManualVisibleBoardEntry(entry) then
        return itemKey
      end
      if fallbackKey == "" then
        fallbackKey = TrimText(itemKey)
      end
    end
    return fallbackKey
  end

  local function PositionBoardItemListWidget(widget, frame, offsetY, x)
    if not widget then
      return
    end
    widget:ClearAllPoints()
    widget:SetPoint("TOPLEFT", frame.itemsPanel or frame, "TOPLEFT", tonumber(x) or 10, -(tonumber(offsetY) or 34))
  end

  local function RenderBoardItemButtons(frame, itemOrder, items, selectedItemKey)
    if not frame then
      return 0, 0, 0
    end

    local resolvedItems = type(items) == "table" and items or {}
    local droppedKeys = {}
    local assignedKeys = {}
    local tradedKeys = {}
    for idx = #(itemOrder or {}), 1, -1 do
      local itemKey = itemOrder[idx]
      local entry = resolvedItems[itemKey]
      if type(entry) == "table" then
        if IsManualVisibleBoardEntry(entry) then
          tradedKeys[#tradedKeys + 1] = itemKey
        elseif entry.awardedAt and IsBoardItemMarkedTraded(itemKey, entry) then
          tradedKeys[#tradedKeys + 1] = itemKey
        elseif entry.awardedAt then
          assignedKeys[#assignedKeys + 1] = itemKey
        else
          droppedKeys[#droppedKeys + 1] = itemKey
        end
      end
    end

    if frame.leftHeader then
      frame.leftHeader:SetText(string.format("Dropped Items (%d)", #droppedKeys))
    end
    if frame.assignedHeader then
      frame.assignedHeader:SetText(string.format("Assigned Items (%d)", #assignedKeys))
    end
    if frame.tradedHeader then
      frame.tradedHeader:SetText(string.format("Traded Items (%d)", #tradedKeys))
    end

    local shown = 0
    local offsetY = 34
    local buttons = frame.itemButtons or {}
    local function renderKey(itemKey, showRecipient, showVoteMarker)
      shown = shown + 1
      local button = buttons[shown]
      if not button then
        shown = shown - 1
        return false
      end
      local entry = resolvedItems[itemKey]
      local recipientLabel, recipientName = "", ""
      if showRecipient then
        recipientLabel, recipientName = FormatBoardQueueRecipient(entry)
      end
      button.__itemListOffsetY = offsetY
      SetBoardItemButtonState(
        button,
        entry,
        itemKey,
        BuildBoardItemButtonTitle(entry, itemKey),
        selectedItemKey == itemKey,
        showVoteMarker and HasLocalBoardVote(entry),
        recipientLabel,
        recipientName
      )
      PositionBoardItemListWidget(button, frame, offsetY, 10)
      offsetY = offsetY + 24
      return true
    end

    for _, itemKey in ipairs(droppedKeys) do
      if not renderKey(itemKey, false, true) then
        break
      end
    end

    if #assignedKeys > 0 and shown < #buttons then
      offsetY = offsetY + 10
      frame.__assignedHeaderOffsetY = offsetY
      if frame.assignedHeader then
        PositionBoardItemListWidget(frame.assignedHeader, frame, offsetY, 12)
        frame.assignedHeader:Show()
      end
      offsetY = offsetY + 22
      for _, itemKey in ipairs(assignedKeys) do
        if not renderKey(itemKey, true, false) then
          break
        end
      end
    else
      frame.__assignedHeaderOffsetY = nil
      if frame.assignedHeader then
        frame.assignedHeader:Hide()
      end
    end

    if #tradedKeys > 0 and shown < #buttons then
      offsetY = offsetY + 10
      frame.__tradedHeaderOffsetY = offsetY
      if frame.tradedHeader then
        PositionBoardItemListWidget(frame.tradedHeader, frame, offsetY, 12)
        frame.tradedHeader:Show()
      end
      offsetY = offsetY + 22
      for _, itemKey in ipairs(tradedKeys) do
        if not renderKey(itemKey, false, false) then
          break
        end
      end
    else
      frame.__tradedHeaderOffsetY = nil
      if frame.tradedHeader then
        frame.tradedHeader:Hide()
      end
    end

    for i = shown + 1, #buttons do
      buttons[i]:Hide()
      buttons[i].__itemKey = nil
      buttons[i].__itemListOffsetY = nil
    end

    return shown, #droppedKeys, #assignedKeys, #tradedKeys
  end

  local function RenderPreviewBoard(frame)
    local preview = GetBoardPreviewState(frame)
    if frame then
      frame.__previewState = preview
      if type(deps.RefreshBoardHeaderSortState) == "function" then
        deps.RefreshBoardHeaderSortState(frame)
      end
    end
    if type(preview) ~= "table" then
      return false
    end
    EnsurePreviewBoardContext(preview)

    local simulationAccess = nil
    local canUseBoard = false
    local canAssign = false
    if preview.isSimulation then
      simulationAccess = GetSimulationPreviewAccess(preview)
      canUseBoard = CanSelfUseSimulationPreviewBoard(preview)
      canAssign = CanSelfAssignSimulationPreview(preview)
      frame.note:SetText(
        "Simulation of the session-host live board. Results are written into profile \"" ..
        tostring(preview.profileName or "Simulation") ..
        "\" and mirrored from source profile \"" ..
        tostring(preview.sourceProfileName or "Default") ..
        "\"." ..
        ((type(simulationAccess) == "table")
          and (" Your simulated role: " .. tostring(simulationAccess.role or "Raider") .. ".")
          or "")
      )
    else
      frame.note:SetText("Preview of the session-host live board for profile \"" .. tostring(preview.profileName or "Default") .. "\". This is visual only and does not start a real session.")
    end

    RenderBoardItemButtons(frame, preview.itemOrder, preview.items, preview.selectedItemKey)

    local selected = preview.items[preview.selectedItemKey]
    if not selected and #preview.itemOrder > 0 then
      preview.selectedItemKey = preview.itemOrder[1]
      selected = preview.items[preview.selectedItemKey]
    end
    if not selected then
      frame.detailIcon:SetTexture(nil)
      SetBoardDetailTooltipState(frame, nil, "")
      frame.detailTitle:SetText("No preview items available.")
      frame.detailMeta:SetText("")
      frame.assignTarget:SetText("")
      SetBoardAwardStatus(frame)
      UpdateBoardMemberRows(frame, {}, nil, nil, nil, nil)
      UpdateAssignDropdown(frame, {}, nil, preview.isSimulation and "Await rolls" or "Preview only")
      frame.prevAssignBtn:Hide()
      frame.prevAssignBtn:Disable()
      frame.nextAssignBtn:Hide()
      frame.nextAssignBtn:Disable()
      frame.assignBtn:Disable()
      frame.assignBtn:SetText(preview.isSimulation and "Simulation Only" or "Preview Only")
      frame.voteBtn:Disable()
      frame.unassignBtn:Disable()
      frame.unassignBtn:Hide()
      frame.__showUnassignBtn = false
      if frame.changeRollBtn then
        frame.changeRollBtn:Disable()
        frame.changeRollBtn:Hide()
        frame.__showChangeRollBtn = false
      end
      return true
    end

    frame.detailIcon:SetTexture(selected.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
    SetBoardDetailTooltipState(frame, selected, preview.selectedItemKey)
    frame.detailTitle:SetText(type(ResolveRuntimeItemDisplayText) == "function"
      and ResolveRuntimeItemDisplayText(selected, selected.key)
      or (selected.name or selected.key))
    frame.detailMeta:SetText(
      string.format(
        "%s Item: %s%s",
        preview.isSimulation and "Simulation" or "Preview",
        tostring(selected.key or "?"),
        selected.itemLevel and (" | Item Level: " .. tostring(selected.itemLevel)) or ""
      )
    )

    local selectedMemberLabel = nil
    local selectedMemberName = nil
    local memberRows = {}
    local selectedMember = nil
    local totalsByCharacter = ResolveLootLedgerTotals(preview.sourceProfileName ~= "" and preview.sourceProfileName or preview.profileName)

    if preview.isSimulation then
      memberRows, selectedMember = EnsurePreviewSelectedMember(preview, selected)
      for _, row in ipairs(memberRows) do
        local selection = row.selection
        local label = FormatSelectionLabel(selection)
        if selectedMember and selectedMember.key == row.key then
          selectedMemberName = tostring(row.name or row.key or "?")
          selectedMemberLabel = label
        end
      end
    else
      memberRows = {}
      for _, member in ipairs(preview.members or {}) do
        local row = {
          key = member.key,
          name = tostring(member.name or member.key or "?"),
          isHost = member.isHost and true or false,
          className = TrimText(member.className),
          classFile = TrimText(member.classFile),
          specName = TrimText(member.specName),
          specIcon = TrimText(member.specIcon),
          note = TrimText(member.note),
          selection = selected.selections and selected.selections[member.key] or nil,
        }
        memberRows[#memberRows + 1] = row
        local selection = row.selection
        local label = FormatSelectionLabel(selection)
        if preview.selectedMemberKey == member.key then
          selectedMember = row
          selectedMemberName = tostring(row.name or row.key or "?")
          selectedMemberLabel = label
        end
      end
    end
    local pendingRollCount, resolvedRollCount = CountPendingBoardSelections(memberRows)

    if IsManualVisibleBoardEntry(selected) then
      frame.assignTarget:SetText(BuildAssignmentTargetText(nil, nil, "Non-council visibility only"))
    elseif selectedMemberName then
      local assignRow = selectedMember or { key = preview.selectedMemberKey, name = selectedMemberName }
      frame.assignTarget:SetText(BuildAssignmentTargetText(assignRow, selectedMemberLabel))
    elseif preview.isSimulation and resolvedRollCount <= 0 then
      frame.assignTarget:SetText(BuildAssignmentTargetText(nil, nil, "Awaiting simulated rolls"))
    else
      frame.assignTarget:SetText(BuildAssignmentTargetText(nil, nil, "Preview"))
    end

    if IsManualVisibleBoardEntry(selected) then
      SetBoardAwardStatus(
        frame,
        "success",
        "Non-Council Item",
        BuildAwardStatusDetail(tostring(preview.profileName or "Simulation"), selected)
      )
    elseif selected.awardedAt and selected.awardedTo then
      SetBoardAwardStatus(
        frame,
        "complete",
        "Award Complete",
        BuildAwardStatusDetail(tostring(preview.profileName or "Simulation"), selected)
      )
    else
      local projectedPoints = ResolveBoardProjectedPoints(
        tostring(preview.profileName or "Simulation"),
        nil,
        selected,
        selectedMember and selectedMember.key or nil
      )
      selected.projectedPoints = projectedPoints
      if pendingRollCount > 0 then
        local waitingText = canAssign
          and string.format(
            "%d pending | Projected award: %d point(s).",
            pendingRollCount,
            projectedPoints
          )
          or string.format(
            "%d pending | Projected award: %d point(s).",
            pendingRollCount,
            projectedPoints
          )
        SetBoardAwardStatus(
          frame,
          "waiting",
          "Responses pending",
          waitingText
        )
      else
        SetBoardAwardStatus(
          frame,
          "ready",
          "All responses received",
          string.format("Projected award: %d point(s).", projectedPoints)
        )
      end
    end

    UpdateBoardMemberRows(frame, memberRows, preview.selectedMemberKey, totalsByCharacter, ResolveEntryVotedKey(selected), selected.awardedTo, selected)
    UpdateAssignDropdown(frame, preview.isSimulation and memberRows or {}, selectedMember, preview.isSimulation and ((pendingRollCount > 0 and resolvedRollCount > 0) and "Await rolls" or (resolvedRollCount > 0 and "Select member" or "Await rolls")) or "Preview only")
    if preview.isSimulation then
      if canAssign and resolvedRollCount > 0 and not selected.awardedAt and #memberRows > 1 then
        frame.prevAssignBtn:Hide()
        frame.prevAssignBtn:Disable()
        frame.nextAssignBtn:Hide()
        frame.nextAssignBtn:Disable()
      else
        frame.prevAssignBtn:Hide()
        frame.prevAssignBtn:Disable()
        frame.nextAssignBtn:Hide()
        frame.nextAssignBtn:Disable()
      end
      if not canUseBoard then
        frame.assignBtn:Disable()
        frame.assignBtn:SetText("History Only")
      elseif canAssign and resolvedRollCount > 0 and not selected.awardedAt and selectedMember then
        frame.assignBtn:Enable()
        frame.assignBtn:SetText("Assign Item")
      elseif IsManualVisibleBoardEntry(selected) then
        frame.assignBtn:Disable()
        frame.assignBtn:SetText("Non-Council Item")
      elseif selected.awardedAt then
        frame.assignBtn:Disable()
        frame.assignBtn:SetText("Item Awarded")
      elseif canUseBoard and not canAssign then
        frame.assignBtn:Disable()
        frame.assignBtn:SetText("Host Only")
      else
        frame.assignBtn:Disable()
        frame.assignBtn:SetText("Await Rolls")
      end
    else
      frame.prevAssignBtn:Hide()
      frame.prevAssignBtn:Disable()
      frame.nextAssignBtn:Hide()
      frame.nextAssignBtn:Disable()
      frame.assignBtn:Disable()
      frame.assignBtn:SetText("Preview Only")
    end
    if frame.voteBtn then
      frame.voteBtn:Hide()
    end
    local canUnassign = false
    if IsManualVisibleBoardEntry(selected) then
      frame.unassignBtn:Disable()
    elseif preview.isSimulation and not canUseBoard then
      frame.unassignBtn:Disable()
    elseif selected.awardedAt and (not preview.isSimulation or canAssign) then
      canUnassign = true
      frame.unassignBtn:Enable()
    else
      frame.unassignBtn:Disable()
    end
    frame.__showUnassignBtn = canUnassign
    frame.unassignBtn:SetShown(canUnassign)
    if frame.changeRollBtn then
      frame.changeRollBtn:Disable()
      frame.changeRollBtn:Hide()
      frame.__showChangeRollBtn = false
    end
    return true
  end

  return {
    ResolveBoardProjectedPoints = ResolveBoardProjectedPoints,
    BuildAwardStatusDetail = BuildAwardStatusDetail,
    SetBoardItemButtonState = SetBoardItemButtonState,
    BuildBoardItemButtonTitle = BuildBoardItemButtonTitle,
    SelectNextUnawardedBoardItem = SelectNextUnawardedBoardItem,
    RenderBoardItemButtons = RenderBoardItemButtons,
    RenderPreviewBoard = RenderPreviewBoard,
  }
end
