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

  local ApplyFrameSkin = deps.ApplyFrameSkin
  local TrimText = deps.TrimText
  local FormatDateTime = deps.FormatDateTime
  local ClampLayoutValue = deps.ClampLayoutValue
  local SetControlWidth = deps.SetControlWidth
  local GetFontStringWidth = deps.GetFontStringWidth
  local FlowLayoutControls = deps.FlowLayoutControls

  -- Blizzard talent sheets are shared by two specs in several cases. These
  -- coordinates keep the active half without shipping duplicate addon media.
  local TALENT_BACKGROUND_TEXCOORD = {
    FULL = { 0.0, 0.77, 0.0, 0.75 },
    TOP = { 0.0, 0.77, 0.0, 0.38 },
    BOTTOM = { 0.0, 0.77, 0.38, 0.75 },
  }
  local TALENT_BACKGROUND_BY_SPEC = {
    ["DEATHKNIGHT:BLOOD"] = { 4631290, "TOP" },
    ["DEATHKNIGHT:FROST"] = { 4631290, "BOTTOM" },
    ["DEATHKNIGHT:UNHOLY"] = { 4631293, "FULL" },
    ["DEMONHUNTER:HAVOC"] = { 4631296, "TOP" },
    ["DEMONHUNTER:VENGEANCE"] = { 4631296, "BOTTOM" },
    ["DEMONHUNTER:DEVOURER"] = { 7323575, "FULL" },
    ["DRUID:BALANCE"] = { 4631299, "TOP" },
    ["DRUID:FERAL"] = { 4631299, "BOTTOM" },
    ["DRUID:GUARDIAN"] = { 4631304, "TOP" },
    ["DRUID:RESTORATION"] = { 4631304, "BOTTOM" },
    ["EVOKER:DEVASTATION"] = { 4631307, "TOP" },
    ["EVOKER:PRESERVATION"] = { 4631307, "BOTTOM" },
    ["EVOKER:AUGMENTATION"] = { 5179179, "FULL" },
    ["HUNTER:BEAST MASTERY"] = { 4631310, "TOP" },
    ["HUNTER:MARKSMANSHIP"] = { 4631310, "BOTTOM" },
    ["HUNTER:SURVIVAL"] = { 4631313, "FULL" },
    ["MAGE:ARCANE"] = { 4631318, "TOP" },
    ["MAGE:FIRE"] = { 4631318, "BOTTOM" },
    ["MAGE:FROST"] = { 4631321, "FULL" },
    ["MONK:BREWMASTER"] = { 4631324, "TOP" },
    ["MONK:MISTWEAVER"] = { 4631324, "BOTTOM" },
    ["MONK:WINDWALKER"] = { 4631329, "FULL" },
    ["PALADIN:HOLY"] = { 4631337, "TOP" },
    ["PALADIN:PROTECTION"] = { 4631337, "BOTTOM" },
    ["PALADIN:RETRIBUTION"] = { 4631340, "FULL" },
    ["PRIEST:DISCIPLINE"] = { 4631348, "TOP" },
    ["PRIEST:HOLY"] = { 4631348, "BOTTOM" },
    ["PRIEST:SHADOW"] = { 4631371, "FULL" },
    ["ROGUE:ASSASSINATION"] = { 4631374, "TOP" },
    ["ROGUE:OUTLAW"] = { 4631374, "BOTTOM" },
    ["ROGUE:SUBTLETY"] = { 4631377, "FULL" },
    ["SHAMAN:ELEMENTAL"] = { 4631381, "TOP" },
    ["SHAMAN:ENHANCEMENT"] = { 4631381, "BOTTOM" },
    ["SHAMAN:RESTORATION"] = { 4631383, "FULL" },
    ["WARLOCK:AFFLICTION"] = { 4631386, "TOP" },
    ["WARLOCK:DEMONOLOGY"] = { 4631386, "BOTTOM" },
    ["WARLOCK:DESTRUCTION"] = { 4631389, "FULL" },
    ["WARRIOR:ARMS"] = { 4631392, "TOP" },
    ["WARRIOR:FURY"] = { 4631392, "BOTTOM" },
    ["WARRIOR:PROTECTION"] = { 4631395, "FULL" },
  }

  local function GetLootLedgerTalentBackground(row)
    if type(row) ~= "table" or row.type ~= "character" then
      return nil
    end
    local classKey = string.upper(TrimText(row.classFile))
    if classKey == "" then
      classKey = string.upper(TrimText(row.className)):gsub("[%s%-]", "")
    end
    local specKey = string.upper(TrimText(row.specName))
    local definition = TALENT_BACKGROUND_BY_SPEC[classKey .. ":" .. specKey]
    if not definition then
      return nil
    end
    local texCoord = TALENT_BACKGROUND_TEXCOORD[definition[2]] or TALENT_BACKGROUND_TEXCOORD.FULL
    return {
      fileID = definition[1],
      left = texCoord[1],
      right = texCoord[2],
      top = texCoord[3],
      bottom = texCoord[4],
    }
  end

  local function EnsureLootLedgerHistoryRows(rows, parent, count)
    rows = rows or {}
    while #rows < count do
      local row = CreateFrame("Button", nil, parent)
      row:SetSize(552, 24)
      row:EnableMouse(true)
      row:RegisterForClicks("LeftButtonUp")

      local bg = row:CreateTexture(nil, "BACKGROUND")
      bg:SetAllPoints(true)
      bg:SetTexture("Interface\\Buttons\\WHITE8x8")
      bg:SetVertexColor(0.08, 0.05, 0.06, 0.72)
      row.__bg = bg

      local icon = row:CreateTexture(nil, "ARTWORK")
      icon:SetSize(18, 18)
      icon:SetPoint("LEFT", 4, 0)
      icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      row.__icon = icon

      local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      nameText:SetPoint("LEFT", icon, "RIGHT", 8, 0)
      nameText:SetWidth(286)
      nameText:SetJustifyH("LEFT")
      row.__nameText = nameText

      local receivedText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      receivedText:SetPoint("LEFT", 300, 0)
      receivedText:SetWidth(160)
      receivedText:SetJustifyH("LEFT")
      row.__receivedText = receivedText

      local pointsText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      pointsText:SetPoint("LEFT", 468, 0)
      pointsText:SetWidth(60)
      pointsText:SetJustifyH("LEFT")
      row.__pointsText = pointsText

      rows[#rows + 1] = row
    end
    return rows
  end

  local function EnsureLootLedgerMemberRows(rows, parent, count)
    rows = rows or {}
    while #rows < count do
      local row = CreateFrame("Frame", nil, parent, "BackdropTemplate")
      row:SetSize(552, 44)
      ApplyFrameSkin(row)

      local bg = row:CreateTexture(nil, "BACKGROUND")
      bg:SetAllPoints(true)
      bg:SetTexture("Interface\\Buttons\\WHITE8x8")
      bg:SetVertexColor(0.08, 0.05, 0.06, 0.42)
      row.__bg = bg

      local accent = row:CreateTexture(nil, "BORDER")
      accent:SetTexture("Interface\\Buttons\\WHITE8x8")
      accent:SetPoint("TOPLEFT", 1, -1)
      accent:SetPoint("BOTTOMLEFT", 1, 1)
      accent:SetWidth(2)
      row.__accent = accent

      local icon = row:CreateTexture(nil, "ARTWORK")
      icon:SetSize(18, 18)
      icon:SetPoint("LEFT", 10, 0)
      icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      row.__icon = icon

      local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
      nameText:SetPoint("TOPLEFT", icon, "TOPRIGHT", 8, -5)
      nameText:SetJustifyH("LEFT")
      if nameText.SetMaxLines then
        nameText:SetMaxLines(1)
      end
      row.__nameText = nameText

      local roleText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      roleText:SetPoint("TOPRIGHT", -8, -6)
      roleText:SetJustifyH("RIGHT")
      if roleText.SetMaxLines then
        roleText:SetMaxLines(1)
      end
      row.__roleText = roleText

      local specText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      specText:SetPoint("TOPLEFT", nameText, "BOTTOMLEFT", 0, -3)
      specText:SetJustifyH("LEFT")
      if specText.SetMaxLines then
        specText:SetMaxLines(1)
      end
      row.__specText = specText

      local statsText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
      statsText:SetPoint("TOPLEFT", specText, "BOTTOMLEFT", 0, -4)
      statsText:SetPoint("RIGHT", -10, 0)
      statsText:SetJustifyH("LEFT")
      if statsText.SetMaxLines then
        statsText:SetMaxLines(1)
      end
      row.__statsText = statsText

      rows[#rows + 1] = row
    end
    return rows
  end

  local function ResolveLootLedgerEntryIcon(item)
    if type(item) ~= "table" then
      return "Interface\\Icons\\INV_Misc_QuestionMark"
    end
    if item.itemIcon then
      return item.itemIcon
    end
    local infoSource = item.itemLink or item.itemID
    if infoSource and type(GetItemInfo) == "function" then
      local _, _, _, _, _, _, _, _, _, itemIcon = GetItemInfo(infoSource)
      if itemIcon then
        return itemIcon
      end
    end
    return "Interface\\Icons\\INV_Misc_QuestionMark"
  end

  local function NormalizeLootLedgerRollColor(color)
    if type(color) ~= "table" then
      return nil
    end
    local r = tonumber(color[1])
    local g = tonumber(color[2])
    local b = tonumber(color[3])
    if not r or not g or not b then
      return nil
    end
    return { r, g, b }
  end

  local function ResolveLootLedgerRollOption(profile, item)
    local itemTable = type(item) == "table" and item or {}
    local optionKey = string.lower(TrimText(itemTable.rollOptionKey))
    local optionLabel = string.lower(TrimText(itemTable.rollOptionLabel))
    local resolvedLabel = TrimText(itemTable.rollOptionLabel)
    if resolvedLabel == "" then
      resolvedLabel = TrimText(itemTable.rollOptionKey)
    end
    local resolvedColor = NormalizeLootLedgerRollColor(itemTable.rollOptionColor)
    local options = type(profile) == "table" and profile.settings and profile.settings.rollOptions or nil
    for _, option in ipairs(type(options) == "table" and options or {}) do
      if type(option) == "table" then
        local candidateKey = string.lower(TrimText(option.key))
        local candidateLabel = string.lower(TrimText(option.label))
        if (optionKey ~= "" and optionKey == candidateKey) or (optionLabel ~= "" and optionLabel == candidateLabel) then
          local label = TrimText(option.label)
          if label ~= "" then
            resolvedLabel = label
          end
          resolvedColor = NormalizeLootLedgerRollColor(option.color) or resolvedColor
          break
        end
      end
    end
    return resolvedLabel, resolvedColor
  end

  local function BuildLootLedgerHistoryEntries(profile, selectedRow)
    local entries = {}
    if not selectedRow or selectedRow.type ~= "character" then
      return entries
    end

    local memberKey = selectedRow.character
    local ledger = profile.lootLedger and profile.lootLedger[memberKey] or nil
    for _, item in ipairs(ledger and ledger.items or {}) do
      local rollOptionLabel, rollOptionColor = ResolveLootLedgerRollOption(profile, item)
      entries[#entries + 1] = {
        owner = memberKey,
        entryId = item.entryId,
        itemName = item.itemName,
        itemLevel = item.itemLevel,
        points = item.points,
        note = item.note,
        awardedAt = item.awardedAt,
        itemLink = item.itemLink,
        itemID = item.itemID,
        itemIcon = item.itemIcon,
        itemQuality = item.itemQuality,
        pointBehavior = item.pointBehavior,
        rollOptionKey = item.rollOptionKey,
        rollOptionLabel = rollOptionLabel,
        rollOptionColor = rollOptionColor,
        rollValue = item.rollValue,
        grantedBy = item.grantedBy,
        isManual = item.isManual == true,
      }
    end

    table.sort(entries, function(a, b)
      return (tonumber(a.awardedAt) or 0) > (tonumber(b.awardedAt) or 0)
    end)
    return entries
  end

  local function NormalizeLootLedgerPointBehavior(value)
    local text = string.upper(TrimText(value))
    if text == "BIS" or text == "OFFSPEC" or text == "TRANSMOG" or text == "DISENCHANT" or text == "NONE" or text == "ITEMLEVEL" then
      return text
    end
    return ""
  end

  local function FormatLootLedgerPointBehaviorLabel(value)
    local resolved = NormalizeLootLedgerPointBehavior(value)
    if resolved == "BIS" then
      return "BiS"
    end
    if resolved == "OFFSPEC" then
      return "Off-Spec"
    end
    if resolved == "TRANSMOG" then
      return "Transmog"
    end
    if resolved == "DISENCHANT" then
      return "Disenchant"
    end
    if resolved == "NONE" then
      return "No Points"
    end
    return ""
  end

  local function BuildLootLedgerAwardInsight(profile, selectedRow)
    local counters = {
      bis = 0,
      offSpec = 0,
      transmog = 0,
      disenchant = 0,
    }
    local bisItems = {}
    if type(profile) ~= "table" or type(selectedRow) ~= "table" or selectedRow.type ~= "character" then
      return counters, bisItems
    end

    local members = type(selectedRow.summaryMembers) == "table" and selectedRow.summaryMembers or { selectedRow.character }
    for _, memberKey in ipairs(members) do
      local ledger = type(profile.lootLedger) == "table" and profile.lootLedger[memberKey] or nil
      for _, item in ipairs(type(ledger) == "table" and ledger.items or {}) do
        local behavior = NormalizeLootLedgerPointBehavior(type(item) == "table" and item.pointBehavior or "")
        if behavior == "BIS" then
          counters.bis = counters.bis + 1
          local itemLabel = TrimText(type(item) == "table" and (item.itemLink or item.itemName) or "")
          if itemLabel ~= "" then
            bisItems[#bisItems + 1] = itemLabel
          end
        elseif behavior == "OFFSPEC" then
          counters.offSpec = counters.offSpec + 1
        elseif behavior == "TRANSMOG" then
          counters.transmog = counters.transmog + 1
        elseif behavior == "DISENCHANT" then
          counters.disenchant = counters.disenchant + 1
        end
      end
    end

    return counters, bisItems
  end

  local function BuildLootLedgerNeedDiceInsight(profile, selectedRow)
    local rolls = {}
    if type(profile) ~= "table" or type(selectedRow) ~= "table" or selectedRow.type ~= "character" then
      return {
        count = 0,
        highest = 0,
        average = 0,
        recent = {},
      }
    end

    local members = type(selectedRow.summaryMembers) == "table" and selectedRow.summaryMembers or { selectedRow.character }
    for _, memberKey in ipairs(members) do
      local ledger = type(profile.lootLedger) == "table" and profile.lootLedger[memberKey] or nil
      for _, item in ipairs(type(ledger) == "table" and ledger.items or {}) do
        local optionKey = string.lower(TrimText(type(item) == "table" and (item.rollOptionKey or item.rollOptionLabel) or ""))
        local rollValue = tonumber(type(item) == "table" and item.rollValue or nil)
        if optionKey == "need" and rollValue then
          rolls[#rolls + 1] = {
            rollValue = rollValue,
            awardedAt = tonumber(type(item) == "table" and item.awardedAt or 0) or 0,
          }
        end
      end
    end

    table.sort(rolls, function(a, b)
      return (tonumber(a.awardedAt) or 0) > (tonumber(b.awardedAt) or 0)
    end)

    local highest = 0
    local total = 0
    local recent = {}
    for index, entry in ipairs(rolls) do
      local value = tonumber(entry.rollValue) or 0
      total = total + value
      if value > highest then
        highest = value
      end
      if index <= 8 then
        recent[#recent + 1] = value
      end
    end

    return {
      count = #rolls,
      highest = highest,
      average = #rolls > 0 and (total / #rolls) or 0,
      recent = recent,
    }
  end

  local function BuildNeedDiceSummaryText(diceInsight)
    if type(diceInsight) ~= "table" or tonumber(diceInsight.count) == 0 then
      return "Need Dice: None yet"
    end

    local recentParts = {}
    for _, value in ipairs(diceInsight.recent or {}) do
      recentParts[#recentParts + 1] = tostring(value)
    end
    return string.format(
      "Need Dice: %d roll(s), avg %.1f, high %d, recent [%s]",
      tonumber(diceInsight.count) or 0,
      tonumber(diceInsight.average) or 0,
      tonumber(diceInsight.highest) or 0,
      #recentParts > 0 and table.concat(recentParts, ", ") or "-"
    )
  end

  local function BuildBisItemSummaryText(items)
    if type(items) ~= "table" or #items == 0 then
      return "None"
    end
    if #items <= 4 then
      return table.concat(items, ", ")
    end
    local preview = {}
    for index = 1, 4 do
      preview[#preview + 1] = tostring(items[index])
    end
    return table.concat(preview, ", ") .. string.format(" +%d more", #items - 4)
  end

  local function ApplyLootLedgerTextureIcon(texture, iconData)
    if not texture then
      return
    end

    local resolvedTexture = type(iconData) == "table" and iconData.texture or nil
    if not resolvedTexture or resolvedTexture == "" then
      texture:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
      texture:SetTexCoord(0, 1, 0, 1)
      return
    end

    texture:SetTexture(resolvedTexture)
    if type(iconData.left) == "number" and type(iconData.right) == "number" and type(iconData.top) == "number" and type(iconData.bottom) == "number" then
      texture:SetTexCoord(iconData.left, iconData.right, iconData.top, iconData.bottom)
    else
      texture:SetTexCoord(0, 1, 0, 1)
    end
  end

  local function UpdateLootLedgerDetailLayout(self)
    if not self or not self.lootLedgerRightContent then
      return
    end

    local contentWidth = math.max(540, math.floor((self.lootListRightBg and self.lootListRightBg:GetWidth() or 620) - 40))
    local rightWidth = math.max(320, contentWidth - 18)
    local heroWidth = rightWidth - 8
    local heroBackground = self.lootLedgerHeroTalentBackground
    local heroFade = self.lootLedgerHeroTalentFade
    local showTalentBackground = heroBackground and self.__lootLedgerHasTalentBackground == true and heroWidth >= 500
    local heroBackgroundWidth = showTalentBackground and heroWidth or 0
    local heroTextRightInset = 14
    if self.lootLedgerMembersText then
      self.lootLedgerMembersText:SetWidth(rightWidth)
    end
    if self.lootLedgerHistoryText then
      self.lootLedgerHistoryText:SetWidth(rightWidth)
    end
    local summaryHeight = math.max(34, math.ceil((self.lootLedgerSummaryText and self.lootLedgerSummaryText:GetStringHeight() or 0)))
    local membersInfoHeight = math.max(0, math.ceil((self.lootLedgerMembersText and self.lootLedgerMembersText:IsShown() and self.lootLedgerMembersText:GetStringHeight() or 0)))
    local membersRowsHeight = math.max(0, tonumber(self.lootLedgerMembersRowsHeight) or 0)
    local historyInfoHeight = math.max(24, math.ceil((self.lootLedgerHistoryText and self.lootLedgerHistoryText:GetStringHeight() or 0)))
    local historyRowsHeight = math.max(1, tonumber(self.lootLedgerHistoryRowsHeight) or 1)
    local historyPagerHeight = self.__lootLedgerHistoryPagerVisible == true and 28 or 0
    local heroHeight = math.max(126, 92 + summaryHeight)
    local roleLabelWidth = GetFontStringWidth(self.lootLedgerRoleLabel, 32)
    local mainLabelWidth = GetFontStringWidth(self.lootLedgerMainLabel, 36)
    local roleDropdownWidth = 120
    local mainDropdownWidth = math.max(170, rightWidth - mainLabelWidth - 8)
    local fieldGap = 10
    local pointsEditWidth = 82
    local descriptionWidth = math.max(140, rightWidth - pointsEditWidth - fieldGap)
    local showMainAssignment = self.__lootLedgerShowMainAssignment == true
    local showNoteEditor = self.__lootLedgerShowNoteEditor == true
    local hasSelectedCharacter = self.__lootLedgerHasSelectedCharacter == true
    local hasCharacterNote = self.__lootLedgerHasCharacterNote == true
    local linkedAltsEmpty = self.__lootLedgerLinkedAltsEmpty == true
    local notePanelHeight = 0
    local roleRowTop
    local roleControlsHeight
    local mainRowTop
    local mainControlsHeight
    local membersTop
    local membersBlockHeight

    if self.lootLedgerHeroFrame then
      self.lootLedgerHeroFrame:SetWidth(heroWidth)
    end
    if self.lootLedgerSelectedLabel then
      self.lootLedgerSelectedLabel:ClearAllPoints()
      self.lootLedgerSelectedLabel:SetPoint("TOPLEFT", self.lootLedgerHeroIcon, "TOPRIGHT", 12, -2)
      self.lootLedgerSelectedLabel:SetPoint("TOPRIGHT", self.lootLedgerHeroFrame, "TOPRIGHT", -heroTextRightInset, -14)
    end
    if self.lootLedgerTotalLabel then
      self.lootLedgerTotalLabel:ClearAllPoints()
      self.lootLedgerTotalLabel:SetPoint("TOPLEFT", self.lootLedgerSelectedLabel, "BOTTOMLEFT", 0, -6)
      self.lootLedgerTotalLabel:SetPoint("TOPRIGHT", self.lootLedgerSelectedLabel, "BOTTOMRIGHT", 0, -6)
    end
    if self.lootLedgerSummaryText then
      self.lootLedgerSummaryText:ClearAllPoints()
      self.lootLedgerSummaryText:SetPoint("TOPLEFT", self.lootLedgerTotalLabel, "BOTTOMLEFT", 0, -8)
      self.lootLedgerSummaryText:SetPoint("TOPRIGHT", self.lootLedgerTotalLabel, "BOTTOMRIGHT", 0, -8)
    end
    if showTalentBackground then
      heroBackground:ClearAllPoints()
      heroBackground:SetPoint("TOPRIGHT", self.lootLedgerHeroFrame, "TOPRIGHT", -1, -1)
      heroBackground:SetPoint("BOTTOMRIGHT", self.lootLedgerHeroFrame, "BOTTOMRIGHT", -1, 1)
      heroBackground:SetWidth(heroBackgroundWidth)
      heroBackground:SetAlpha(1.00)
      heroBackground:Show()
      if heroFade then
        heroFade:ClearAllPoints()
        heroFade:SetPoint("TOPLEFT", heroBackground, "TOPLEFT", 0, 0)
        heroFade:SetPoint("BOTTOMRIGHT", heroBackground, "BOTTOMRIGHT", 0, 0)
        heroFade:Show()
      end
    else
      if heroBackground then
        heroBackground:Hide()
      end
      if heroFade then
        heroFade:Hide()
      end
    end

    summaryHeight = math.max(34, math.ceil((self.lootLedgerSummaryText and self.lootLedgerSummaryText:GetStringHeight() or 0)))
    heroHeight = math.max(126, 92 + summaryHeight)
    if self.lootLedgerHeroFrame then
      self.lootLedgerHeroFrame:SetHeight(heroHeight)
    end

    local function LayoutDivider(divider, top)
      if not divider then
        return
      end
      divider:ClearAllPoints()
      divider:SetPoint("TOPLEFT", 18, top)
      divider:SetSize(rightWidth, 1)
      divider:Show()
    end

    local function SetShown(control, shown)
      if control and control.SetShown then
        control:SetShown(shown and true or false)
      end
    end

    local roleBadgeWidth = 0
    if self.lootLedgerHeroRoleBadge and self.lootLedgerHeroRoleBadge:IsShown() then
      roleBadgeWidth = math.max(52, GetFontStringWidth(self.lootLedgerHeroRoleText, 32) + 20)
      self.lootLedgerHeroRoleBadge:SetSize(roleBadgeWidth, 20)
    end
    if self.lootLedgerSelectedLabel then
      local heroIconAnchor = self.lootLedgerHeroIconFrame or self.lootLedgerHeroIcon
      local heroIconWidth = heroIconAnchor and heroIconAnchor:GetWidth() or 52
      local heroTextLeft = 14 + heroIconWidth + 16
      local maxNameWidth = math.max(160, heroWidth - heroTextLeft - heroTextRightInset)
      self.lootLedgerSelectedLabel:ClearAllPoints()
      self.lootLedgerSelectedLabel:SetPoint("TOPLEFT", heroIconAnchor, "TOPRIGHT", 16, -2)
      self.lootLedgerSelectedLabel:SetWidth(maxNameWidth)
    end
    if self.lootLedgerHeroRoleBadge and roleBadgeWidth > 0 then
      self.lootLedgerHeroRoleBadge:ClearAllPoints()
      self.lootLedgerHeroRoleBadge:SetPoint("TOPLEFT", self.lootLedgerSelectedLabel, "BOTTOMLEFT", 0, -5)
    end
    if self.lootLedgerTotalLabel then
      self.lootLedgerTotalLabel:ClearAllPoints()
      if self.lootLedgerHeroRoleBadge and roleBadgeWidth > 0 then
        self.lootLedgerTotalLabel:SetPoint("TOPLEFT", self.lootLedgerHeroRoleBadge, "TOPRIGHT", 8, 0)
      else
        self.lootLedgerTotalLabel:SetPoint("TOPLEFT", self.lootLedgerSelectedLabel, "BOTTOMLEFT", 0, -6)
      end
      self.lootLedgerTotalLabel:SetPoint("TOPRIGHT", self.lootLedgerHeroFrame, "TOPRIGHT", -heroTextRightInset, -49)
    end
    if self.lootLedgerSummaryText then
      self.lootLedgerSummaryText:ClearAllPoints()
      self.lootLedgerSummaryText:SetPoint("TOPLEFT", self.lootLedgerTotalLabel, "BOTTOMLEFT", 0, -10)
      self.lootLedgerSummaryText:SetPoint("TOPRIGHT", self.lootLedgerHeroFrame, "TOPRIGHT", -heroTextRightInset, -78)
    end

    local notePanelTop = -46 - heroHeight - 16
    if self.lootLedgerCharacterNoteFrame then
      self.lootLedgerCharacterNoteFrame:SetShown(hasSelectedCharacter)
      if hasSelectedCharacter then
        if self.lootLedgerCharacterNoteDisplay then
          self.lootLedgerCharacterNoteDisplay:SetWidth(math.max(160, rightWidth - 124))
        end
        local noteTextHeight = math.max(16, math.ceil((self.lootLedgerCharacterNoteDisplay and self.lootLedgerCharacterNoteDisplay:GetStringHeight()) or 16))
        if showNoteEditor then
          notePanelHeight = 86
        elseif hasCharacterNote then
          notePanelHeight = math.max(56, noteTextHeight + 38)
        else
          notePanelHeight = 32
        end
        self.lootLedgerCharacterNoteFrame:ClearAllPoints()
        self.lootLedgerCharacterNoteFrame:SetPoint("TOPLEFT", 18, notePanelTop)
        self.lootLedgerCharacterNoteFrame:SetSize(rightWidth, notePanelHeight)

        SetShown(self.lootLedgerCharacterNoteLabel, true)
        if self.lootLedgerCharacterNoteLabel then
          self.lootLedgerCharacterNoteLabel:ClearAllPoints()
          self.lootLedgerCharacterNoteLabel:SetPoint("TOPLEFT", self.lootLedgerCharacterNoteFrame, "TOPLEFT", 10, -8)
        end
        SetShown(self.lootLedgerCharacterNoteDisplay, not showNoteEditor)
        if self.lootLedgerCharacterNoteDisplay then
          self.lootLedgerCharacterNoteDisplay:ClearAllPoints()
          if hasCharacterNote then
            self.lootLedgerCharacterNoteDisplay:SetPoint("TOPLEFT", self.lootLedgerCharacterNoteFrame, "TOPLEFT", 10, -28)
          else
            self.lootLedgerCharacterNoteDisplay:SetPoint("LEFT", self.lootLedgerCharacterNoteLabel, "RIGHT", 14, 0)
            self.lootLedgerCharacterNoteDisplay:SetPoint("RIGHT", self.lootLedgerCharacterNoteToggleBtn, "LEFT", -12, 0)
          end
        end
        SetShown(self.lootLedgerCharacterNoteToggleBtn, not showNoteEditor)
        if self.lootLedgerCharacterNoteToggleBtn then
          self.lootLedgerCharacterNoteToggleBtn:ClearAllPoints()
          self.lootLedgerCharacterNoteToggleBtn:SetPoint("TOPRIGHT", self.lootLedgerCharacterNoteFrame, "TOPRIGHT", -10, -8)
        end
        SetShown(self.lootLedgerCharacterNoteEdit, showNoteEditor)
        if self.lootLedgerCharacterNoteEdit then
          SetControlWidth(self.lootLedgerCharacterNoteEdit, math.max(180, rightWidth - 20))
          self.lootLedgerCharacterNoteEdit:ClearAllPoints()
          self.lootLedgerCharacterNoteEdit:SetPoint("TOPLEFT", self.lootLedgerCharacterNoteFrame, "TOPLEFT", 10, -30)
        end
        SetShown(self.lootLedgerCharacterNoteSaveBtn, showNoteEditor)
        if self.lootLedgerCharacterNoteSaveBtn then
          self.lootLedgerCharacterNoteSaveBtn:ClearAllPoints()
          self.lootLedgerCharacterNoteSaveBtn:SetPoint("BOTTOMLEFT", self.lootLedgerCharacterNoteFrame, "BOTTOMLEFT", 10, 8)
        end
        SetShown(self.lootLedgerCharacterNoteCancelBtn, showNoteEditor)
        if self.lootLedgerCharacterNoteCancelBtn then
          self.lootLedgerCharacterNoteCancelBtn:ClearAllPoints()
          self.lootLedgerCharacterNoteCancelBtn:SetPoint("LEFT", self.lootLedgerCharacterNoteSaveBtn, "RIGHT", 8, 0)
        end
      else
        SetShown(self.lootLedgerCharacterNoteLabel, false)
        SetShown(self.lootLedgerCharacterNoteDisplay, false)
        SetShown(self.lootLedgerCharacterNoteToggleBtn, false)
        SetShown(self.lootLedgerCharacterNoteEdit, false)
        SetShown(self.lootLedgerCharacterNoteSaveBtn, false)
        SetShown(self.lootLedgerCharacterNoteCancelBtn, false)
      end
    end

    local primaryCardWidth = math.floor((heroWidth - 24) / 4)
    local secondaryCardWidth = math.floor((heroWidth - 24) / 4)
    local activityHeaderTop = notePanelTop - notePanelHeight - (notePanelHeight > 0 and 16 or 0)
    local primaryCardTop = activityHeaderTop - 22
    local awardsHeaderTop = primaryCardTop - 68
    local secondaryCardTop = awardsHeaderTop - 22
    if self.lootLedgerActivityHeader then
      self.lootLedgerActivityHeader:ClearAllPoints()
      self.lootLedgerActivityHeader:SetPoint("TOPLEFT", 18, activityHeaderTop)
    end
    if self.lootLedgerAwardsHeader then
      self.lootLedgerAwardsHeader:ClearAllPoints()
      self.lootLedgerAwardsHeader:SetPoint("TOPLEFT", 18, awardsHeaderTop)
    end
    local cardIndex = 0
    for _, key in ipairs({ "sessions", "items", "points", "total" }) do
      local card = self.lootLedgerStatCards and self.lootLedgerStatCards[key] or nil
      if card and card.frame then
        cardIndex = cardIndex + 1
        card.frame:ClearAllPoints()
        card.frame:SetPoint("TOPLEFT", 18 + ((cardIndex - 1) * (primaryCardWidth + 8)), primaryCardTop)
        card.frame:SetSize(primaryCardWidth, 54)
      end
    end
    cardIndex = 0
    for _, key in ipairs({ "bis", "offspec", "transmog", "disenchant" }) do
      local card = self.lootLedgerStatCards and self.lootLedgerStatCards[key] or nil
      if card and card.frame then
        cardIndex = cardIndex + 1
        card.frame:ClearAllPoints()
        card.frame:SetPoint("TOPLEFT", 18 + ((cardIndex - 1) * (secondaryCardWidth + 8)), secondaryCardTop)
        card.frame:SetSize(secondaryCardWidth, 54)
      end
    end

    local membersHeaderTop = secondaryCardTop - 74
    LayoutDivider(self.lootLedgerMembersDivider, membersHeaderTop + 12)
    if self.lootLedgerMembersHeader then
      self.lootLedgerMembersHeader:ClearAllPoints()
      self.lootLedgerMembersHeader:SetPoint("TOPLEFT", 18, membersHeaderTop)
    end
    membersTop = membersHeaderTop - 28
    if self.lootLedgerMembersRowsFrame then
      self.lootLedgerMembersRowsFrame:ClearAllPoints()
      self.lootLedgerMembersRowsFrame:SetPoint("TOPLEFT", 18, membersTop)
      self.lootLedgerMembersRowsFrame:SetSize(rightWidth, math.max(1, membersRowsHeight))
      self.lootLedgerMembersRowsFrame:SetShown(membersRowsHeight > 0)
    end
    if self.lootLedgerMembersText then
      self.lootLedgerMembersText:ClearAllPoints()
      if membersRowsHeight > 0 and self.lootLedgerMembersRowsFrame then
        self.lootLedgerMembersText:SetPoint("TOPLEFT", self.lootLedgerMembersRowsFrame, "BOTTOMLEFT", 0, -8)
      else
        self.lootLedgerMembersText:SetPoint("TOPLEFT", 18, membersTop)
      end
      self.lootLedgerMembersText:SetWidth(linkedAltsEmpty and math.max(140, rightWidth - 218) or rightWidth)
    end

    membersBlockHeight = membersRowsHeight
    if membersInfoHeight > 0 then
      if membersBlockHeight > 0 then
        membersBlockHeight = membersBlockHeight + 8
      end
      membersBlockHeight = membersBlockHeight + membersInfoHeight
    end
    if membersBlockHeight <= 0 then
      membersBlockHeight = 20
    end

    local linksButtonTop = linkedAltsEmpty and membersTop + 5 or membersTop - membersBlockHeight - 10
    SetShown(self.lootLedgerManageLinksBtn, hasSelectedCharacter)
    if self.lootLedgerManageLinksBtn and hasSelectedCharacter then
      if not linkedAltsEmpty then
        self.lootLedgerManageLinksBtn:SetSize(236, 32)
      end
      self.lootLedgerManageLinksBtn:ClearAllPoints()
      if linkedAltsEmpty then
        self.lootLedgerManageLinksBtn:SetSize(236, 32)
        self.lootLedgerManageLinksBtn:SetPoint("TOPLEFT", self.lootLedgerMembersHeader, "BOTTOMLEFT", 0, -10)
      else
        self.lootLedgerManageLinksBtn:SetPoint("TOPLEFT", 18, linksButtonTop)
      end
      self.lootLedgerManageLinksBtn:Show()
    end

    local showLinkManagement = self.__lootLedgerShowLinkManagement == true
    local linkSettingsTop = linkedAltsEmpty and membersTop - membersBlockHeight - 14 or linksButtonTop - 30
    local linkSettingsHeight = 0
    SetShown(self.lootLedgerRoleLabel, showLinkManagement)
    SetShown(self.lootLedgerRoleDropdown, showLinkManagement)
    SetShown(self.lootLedgerSummaryLinkBtn, showLinkManagement)
    SetShown(self.lootLedgerMainLabel, showLinkManagement and showMainAssignment)
    SetShown(self.lootLedgerSummaryDropdown, showLinkManagement and showMainAssignment)
    SetShown(self.lootLedgerSummaryClearBtn, showLinkManagement and showMainAssignment)
    if showLinkManagement then
      roleRowTop = linkSettingsTop
      if self.lootLedgerRoleLabel then
        self.lootLedgerRoleLabel:ClearAllPoints()
        self.lootLedgerRoleLabel:SetPoint("TOPLEFT", 18, roleRowTop)
      end
      if self.lootLedgerRoleDropdown then
        SetControlWidth(self.lootLedgerRoleDropdown, roleDropdownWidth)
      end
      roleControlsHeight = FlowLayoutControls({
        self.lootLedgerRoleDropdown,
        self.lootLedgerSummaryLinkBtn,
        self.lootLedgerSummaryClearBtn,
      }, 18 + roleLabelWidth + 8, roleRowTop, math.max(180, rightWidth - roleLabelWidth - 8), 10, 8)

      if showMainAssignment then
        mainRowTop = roleRowTop - roleControlsHeight - 10
        if self.lootLedgerMainLabel then
          self.lootLedgerMainLabel:ClearAllPoints()
          self.lootLedgerMainLabel:SetPoint("TOPLEFT", 18, mainRowTop)
        end
        if self.lootLedgerSummaryDropdown then
          SetControlWidth(self.lootLedgerSummaryDropdown, mainDropdownWidth)
        end
        mainControlsHeight = FlowLayoutControls({
          self.lootLedgerSummaryDropdown,
        }, 18 + mainLabelWidth + 8, mainRowTop, math.max(180, rightWidth - mainLabelWidth - 8), 10, 8)
        linkSettingsHeight = roleControlsHeight + mainControlsHeight + 20
      else
        linkSettingsHeight = roleControlsHeight + 20
      end
    end

    local historyHeaderTop = linkSettingsTop - linkSettingsHeight - 26
    local actionsHeaderTop = historyHeaderTop - 54 - historyRowsHeight - historyPagerHeight - historyInfoHeight - 26
    local hasSelectedCharacter = self.lootLedgerManualToggleBtn and self.lootLedgerManualToggleBtn:IsShown()
    LayoutDivider(self.lootLedgerManualDivider, actionsHeaderTop + 12)
    if self.lootLedgerHeader then
      self.lootLedgerHeader:ClearAllPoints()
      self.lootLedgerHeader:SetPoint("TOPLEFT", 18, actionsHeaderTop)
      self.lootLedgerHeader:SetShown(hasSelectedCharacter)
    end
    if self.lootLedgerManualToggleBtn and self.lootLedgerManualToggleBtn:IsShown() then
      self.lootLedgerManualToggleBtn:ClearAllPoints()
      self.lootLedgerManualToggleBtn:SetPoint("TOPLEFT", 18, actionsHeaderTop - 28)
    end

    local showManualAdjustment = self.__lootLedgerShowManualAdjustment == true
    local manualFieldsTop = actionsHeaderTop - 58
    local actionBottom = actionsHeaderTop - 58
    descriptionWidth = math.max(140, rightWidth - pointsEditWidth - fieldGap)
    local pointsX = 18 + descriptionWidth + fieldGap
    SetShown(self.lootLedgerCharacterEditLabel, false)
    SetShown(self.lootLedgerCharacterEdit, false)
    SetShown(self.lootLedgerItemEditLabel, showManualAdjustment)
    SetShown(self.lootLedgerPointsEditLabel, showManualAdjustment)
    SetShown(self.lootLedgerItemEdit, showManualAdjustment)
    SetShown(self.lootLedgerPointsEdit, showManualAdjustment)
    SetShown(self.lootLedgerAddEntryBtn, showManualAdjustment)
    SetShown(self.lootLedgerDangerLabel, hasSelectedCharacter)
    SetShown(self.lootLedgerDeleteCharacterBtn, hasSelectedCharacter)
    if showManualAdjustment then
      if self.lootLedgerItemEditLabel then
        self.lootLedgerItemEditLabel:ClearAllPoints()
        self.lootLedgerItemEditLabel:SetPoint("TOPLEFT", 18, manualFieldsTop)
        self.lootLedgerItemEditLabel:SetWidth(descriptionWidth)
      end
      if self.lootLedgerPointsEditLabel then
        self.lootLedgerPointsEditLabel:ClearAllPoints()
        self.lootLedgerPointsEditLabel:SetPoint("TOPLEFT", pointsX, manualFieldsTop)
        self.lootLedgerPointsEditLabel:SetWidth(pointsEditWidth)
      end
      local manualInputTop = manualFieldsTop - 18
      if self.lootLedgerItemEdit then
        SetControlWidth(self.lootLedgerItemEdit, descriptionWidth)
        self.lootLedgerItemEdit:ClearAllPoints()
        self.lootLedgerItemEdit:SetPoint("TOPLEFT", 18, manualInputTop)
      end
      if self.lootLedgerPointsEdit then
        SetControlWidth(self.lootLedgerPointsEdit, pointsEditWidth)
        self.lootLedgerPointsEdit:ClearAllPoints()
        self.lootLedgerPointsEdit:SetPoint("TOPLEFT", pointsX, manualInputTop)
      end
      if self.lootLedgerAddEntryBtn then
        self.lootLedgerAddEntryBtn:ClearAllPoints()
        self.lootLedgerAddEntryBtn:SetPoint("TOPLEFT", 18, manualInputTop - 32)
      end
      if self.lootLedgerDeleteEntryBtn and self.lootLedgerDeleteEntryBtn:IsShown() then
        self.lootLedgerDeleteEntryBtn:ClearAllPoints()
        self.lootLedgerDeleteEntryBtn:SetPoint("LEFT", self.lootLedgerAddEntryBtn, "RIGHT", 10, 0)
      end
      actionBottom = manualInputTop - 64
    else
      SetShown(self.lootLedgerDeleteEntryBtn, false)
    end
    local dangerTop = actionBottom - 14
    LayoutDivider(self.lootLedgerDangerDivider, dangerTop)
    if self.lootLedgerDangerLabel then
      self.lootLedgerDangerLabel:ClearAllPoints()
      self.lootLedgerDangerLabel:SetPoint("TOPLEFT", 18, dangerTop - 18)
    end
    if self.lootLedgerDeleteCharacterBtn then
      self.lootLedgerDeleteCharacterBtn:ClearAllPoints()
      self.lootLedgerDeleteCharacterBtn:SetPoint("TOPLEFT", 18, dangerTop - 40)
    end
    if self.lootLedgerFeedback then
      self.lootLedgerFeedback:ClearAllPoints()
      self.lootLedgerFeedback:SetPoint("TOPLEFT", 18, dangerTop - 72)
      self.lootLedgerFeedback:SetWidth(rightWidth)
    end

    LayoutDivider(self.lootLedgerHistoryDivider, historyHeaderTop + 12)
    if self.lootLedgerHistoryHeader then
      self.lootLedgerHistoryHeader:ClearAllPoints()
      self.lootLedgerHistoryHeader:SetPoint("TOPLEFT", 18, historyHeaderTop)
    end
    if self.lootLedgerHistoryColumns then
      self.lootLedgerHistoryColumns:ClearAllPoints()
      self.lootLedgerHistoryColumns:SetPoint("TOPLEFT", 18, historyHeaderTop - 24)
      self.lootLedgerHistoryColumns:SetWidth(rightWidth)
    end
    if self.lootLedgerHistoryItemHeader then
      self.lootLedgerHistoryItemHeader:SetWidth(math.max(180, rightWidth - 266))
    end
    if self.lootLedgerHistoryReceivedHeader then
      self.lootLedgerHistoryReceivedHeader:ClearAllPoints()
      self.lootLedgerHistoryReceivedHeader:SetPoint("TOPLEFT", math.max(190, rightWidth - 250), 0)
    end
    if self.lootLedgerHistoryPointsHeader then
      self.lootLedgerHistoryPointsHeader:ClearAllPoints()
      self.lootLedgerHistoryPointsHeader:SetPoint("TOPLEFT", math.max(360, rightWidth - 82), 0)
    end
    if self.lootLedgerHistoryRowsFrame then
      self.lootLedgerHistoryRowsFrame:ClearAllPoints()
      self.lootLedgerHistoryRowsFrame:SetPoint("TOPLEFT", 18, historyHeaderTop - 46)
      self.lootLedgerHistoryRowsFrame:SetSize(rightWidth, historyRowsHeight)
    end
    SetShown(self.lootLedgerHistoryPager, self.__lootLedgerHistoryPagerVisible == true)
    if self.lootLedgerHistoryPager and self.__lootLedgerHistoryPagerVisible == true and self.lootLedgerHistoryRowsFrame then
      self.lootLedgerHistoryPager:ClearAllPoints()
      self.lootLedgerHistoryPager:SetPoint("TOPLEFT", self.lootLedgerHistoryRowsFrame, "BOTTOMLEFT", 0, -6)
      self.lootLedgerHistoryPager:SetWidth(rightWidth)
    end
    if self.lootLedgerHistoryText and self.lootLedgerHistoryRowsFrame then
      self.lootLedgerHistoryText:ClearAllPoints()
      if self.__lootLedgerHistoryPagerVisible == true and self.lootLedgerHistoryPager then
        self.lootLedgerHistoryText:SetPoint("TOPLEFT", self.lootLedgerHistoryPager, "BOTTOMLEFT", 0, -6)
      else
        self.lootLedgerHistoryText:SetPoint("TOPLEFT", self.lootLedgerHistoryRowsFrame, "BOTTOMLEFT", 0, -8)
      end
      self.lootLedgerHistoryText:SetWidth(rightWidth)
    end

    local contentHeight = math.abs(dangerTop - 96) + 42
    self.lootLedgerRightContent:SetSize(contentWidth + 14, math.max(1, contentHeight))
    if self.lootLedgerRightScroll and self.lootLedgerRightScroll.UpdateScrollChildRect then
      self.lootLedgerRightScroll:UpdateScrollChildRect()
    end
  end

  return {
    EnsureLootLedgerHistoryRows = EnsureLootLedgerHistoryRows,
    EnsureLootLedgerMemberRows = EnsureLootLedgerMemberRows,
    ResolveLootLedgerEntryIcon = ResolveLootLedgerEntryIcon,
    BuildLootLedgerHistoryEntries = BuildLootLedgerHistoryEntries,
    FormatLootLedgerPointBehaviorLabel = FormatLootLedgerPointBehaviorLabel,
    BuildLootLedgerAwardInsight = BuildLootLedgerAwardInsight,
    BuildLootLedgerNeedDiceInsight = BuildLootLedgerNeedDiceInsight,
    BuildNeedDiceSummaryText = BuildNeedDiceSummaryText,
    BuildBisItemSummaryText = BuildBisItemSummaryText,
    ApplyLootLedgerTextureIcon = ApplyLootLedgerTextureIcon,
    GetLootLedgerTalentBackground = GetLootLedgerTalentBackground,
    UpdateLootLedgerDetailLayout = UpdateLootLedgerDetailLayout,
  }
end
