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

  local ApplyFrameSkin = deps.ApplyFrameSkin
  local WrapColor = deps.WrapColor
  local TrimText = deps.TrimText
  local UIState = deps.UIState
  local GetLiveLootBoardSummary = deps.GetLiveLootBoardSummary
  local GetApplyBoardMemberRowLayout = deps.GetApplyBoardMemberRowLayout
  local DEFAULT_POPUP_BOARD_WIDTH = tonumber(deps.DEFAULT_POPUP_BOARD_WIDTH) or 760
  local DEFAULT_POPUP_BOARD_HEIGHT = tonumber(deps.DEFAULT_POPUP_BOARD_HEIGHT) or 560
  local MIN_POPUP_BOARD_WIDTH = tonumber(deps.MIN_POPUP_BOARD_WIDTH) or 720
  local MIN_POPUP_BOARD_HEIGHT = tonumber(deps.MIN_POPUP_BOARD_HEIGHT) or 500
  -- The embedded board lives inside the editor scroll viewport, so it can use
  -- a smaller safe layout than the independently resizable Pop Out Board.
  local MIN_EMBEDDED_BOARD_WIDTH = 560
  local MIN_EMBEDDED_BOARD_HEIGHT = 460
  local OPTIONAL_MEMBER_COLUMNS = {
    { key = "total", header = "memberHeaderTotal", width = 52, beforeDice = true },
    { key = "bis", header = "memberHeaderBis", width = 32, beforeDice = true },
    { key = "offspec", header = "memberHeaderOffSpec", width = 28 },
    { key = "transmog", header = "memberHeaderTransmog", width = 28 },
    { key = "disenchant", header = "memberHeaderDisenchant", width = 28 },
    { key = "items", header = "memberHeaderItems", width = 38 },
  }

  local ApplyBoardButtonStyle
  local LayoutBoardFrame

  local function ClampBoardLayoutValue(value, minimum, maximum)
    local numericValue = tonumber(value) or 0
    local minValue = tonumber(minimum) or numericValue
    local maxValue = tonumber(maximum) or numericValue
    if minValue > maxValue then
      minValue, maxValue = maxValue, minValue
    end
    if numericValue < minValue then
      return minValue
    end
    if numericValue > maxValue then
      return maxValue
    end
    return numericValue
  end

  local function RaisePopupFrameAboveEditor(frame, strata)
    if not frame then
      return
    end
    if frame.SetFrameStrata then
      frame:SetFrameStrata(strata or "FULLSCREEN_DIALOG")
    end
    if frame.SetToplevel then
      frame:SetToplevel(true)
    end
    local editor = rawget(_G, "LIDCOUNCIL_Editor")
    if editor and editor.GetFrameLevel and frame.SetFrameLevel then
      frame:SetFrameLevel((editor:GetFrameLevel() or 0) + 120)
    end
    if frame.Raise then
      frame:Raise()
    end
  end

  local function GetPopupBoardSizeLimits()
    local parentWidth = tonumber(UIParent and UIParent.GetWidth and UIParent:GetWidth()) or DEFAULT_POPUP_BOARD_WIDTH
    local parentHeight = tonumber(UIParent and UIParent.GetHeight and UIParent:GetHeight()) or DEFAULT_POPUP_BOARD_HEIGHT
    local maxWidth = math.max(420, math.floor(parentWidth - 40))
    local maxHeight = math.max(320, math.floor(parentHeight - 40))
    local minWidth = math.min(MIN_POPUP_BOARD_WIDTH, maxWidth)
    local minHeight = math.min(MIN_POPUP_BOARD_HEIGHT, maxHeight)
    return minWidth, minHeight, maxWidth, maxHeight
  end

  local function NormalizePopupBoardDimension(value, minimum, fallback, maximum)
    local numericValue = tonumber(value)
    if not numericValue or numericValue <= 0 then
      numericValue = tonumber(fallback) or tonumber(minimum) or 0
    end
    numericValue = math.floor(numericValue + 0.5)
    local minValue = tonumber(minimum) or 0
    local maxValue = tonumber(maximum)
    numericValue = math.max(minValue, numericValue)
    if maxValue and maxValue >= minValue then
      numericValue = math.min(numericValue, maxValue)
    end
    return numericValue
  end

  local function ClampPopupBoardSize(width, height)
    local minWidth, minHeight, maxWidth, maxHeight = GetPopupBoardSizeLimits()
    local clampedWidth = NormalizePopupBoardDimension(width, minWidth, DEFAULT_POPUP_BOARD_WIDTH, maxWidth)
    local clampedHeight = NormalizePopupBoardDimension(height, minHeight, DEFAULT_POPUP_BOARD_HEIGHT, maxHeight)
    return clampedWidth, clampedHeight
  end

  local function ClampPopupBoardPosition(left, top, width, height)
    local parentWidth = tonumber(UIParent and UIParent.GetWidth and UIParent:GetWidth()) or width or DEFAULT_POPUP_BOARD_WIDTH
    local parentHeight = tonumber(UIParent and UIParent.GetHeight and UIParent:GetHeight()) or height or DEFAULT_POPUP_BOARD_HEIGHT
    local horizontalInset = 12
    local verticalInset = 12
    local resolvedWidth = tonumber(width) or DEFAULT_POPUP_BOARD_WIDTH
    local resolvedHeight = tonumber(height) or DEFAULT_POPUP_BOARD_HEIGHT
    local minLeft = horizontalInset
    local maxLeft = math.max(minLeft, parentWidth - resolvedWidth - horizontalInset)
    local minTop = resolvedHeight + verticalInset
    local maxTop = math.max(minTop, parentHeight - verticalInset)
    local resolvedLeft = tonumber(left)
    local resolvedTop = tonumber(top)
    if not resolvedLeft then
      resolvedLeft = math.floor(((parentWidth - resolvedWidth) * 0.5) + 0.5)
    end
    if not resolvedTop then
      resolvedTop = math.floor((((parentHeight + resolvedHeight) * 0.5) + 40) + 0.5)
    end
    resolvedLeft = math.max(minLeft, math.min(maxLeft, resolvedLeft))
    resolvedTop = math.max(minTop, math.min(maxTop, resolvedTop))
    return resolvedLeft, resolvedTop
  end

  local function ApplyPopupBoardResizeLimits(frame)
    if not frame or frame.__embeddedParent then
      return
    end
    local minWidth, minHeight, maxWidth, maxHeight = GetPopupBoardSizeLimits()
    if frame.SetMinResize then
      frame:SetMinResize(minWidth, minHeight)
    end
    if frame.SetMaxResize then
      frame:SetMaxResize(maxWidth, maxHeight)
    end
  end

  local function PreparePopupBoardForUserPlacement(frame)
    if not frame or frame.__embeddedParent then
      return
    end
    local left = frame.GetLeft and frame:GetLeft() or nil
    local top = frame.GetTop and frame:GetTop() or nil
    if not left or not top then
      return
    end
    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
  end

  local function CapturePopupBoardGeometry(frame)
    if not frame or frame.__embeddedParent then
      return nil
    end
    local geometry = frame.__popupGeometry or {}
    local minWidth, minHeight, maxWidth, maxHeight = GetPopupBoardSizeLimits()
    local left = frame.GetLeft and frame:GetLeft() or nil
    local top = frame.GetTop and frame:GetTop() or nil
    local width = frame.GetWidth and frame:GetWidth() or nil
    local height = frame.GetHeight and frame:GetHeight() or nil

    geometry.width = NormalizePopupBoardDimension(width, minWidth, geometry.width or DEFAULT_POPUP_BOARD_WIDTH, maxWidth)
    geometry.height = NormalizePopupBoardDimension(height, minHeight, geometry.height or DEFAULT_POPUP_BOARD_HEIGHT, maxHeight)
    geometry.left, geometry.top = ClampPopupBoardPosition(left or geometry.left, top or geometry.top, geometry.width, geometry.height)
    frame.__popupGeometry = geometry
    return geometry
  end

  local function ApplyPopupBoardGeometry(frame, geometry)
    if not frame or frame.__embeddedParent then
      return
    end
    ApplyPopupBoardResizeLimits(frame)
    local minWidth, minHeight, maxWidth, maxHeight = GetPopupBoardSizeLimits()
    local width = NormalizePopupBoardDimension(geometry and geometry.width, minWidth, DEFAULT_POPUP_BOARD_WIDTH, maxWidth)
    local height = NormalizePopupBoardDimension(geometry and geometry.height, minHeight, DEFAULT_POPUP_BOARD_HEIGHT, maxHeight)
    frame:ClearAllPoints()
    if tonumber(geometry and geometry.left) and tonumber(geometry and geometry.top) then
      local left, top = ClampPopupBoardPosition(geometry.left, geometry.top, width, height)
      frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
    else
      frame:SetPoint("CENTER", 0, 40)
    end
    frame:SetSize(width, height)
  end

  local function EnforcePopupBoardMinimumSize(frame)
    if not frame or frame.__embeddedParent or (UIState and UIState.collapsed) then
      return false
    end
    ApplyPopupBoardResizeLimits(frame)
    local currentWidth = tonumber(frame.GetWidth and frame:GetWidth()) or 0
    local currentHeight = tonumber(frame.GetHeight and frame:GetHeight()) or 0
    local clampedWidth, clampedHeight = ClampPopupBoardSize(currentWidth, currentHeight)
    if math.abs(currentWidth - clampedWidth) > 0.5 or math.abs(currentHeight - clampedHeight) > 0.5 then
      frame:SetSize(clampedWidth, clampedHeight)
      return true
    end
    return false
  end

  local function StopPopupBoardResize(frame, resizeHandle)
    if resizeHandle then
      resizeHandle:SetScript("OnUpdate", nil)
    end
    if not frame or not frame.__resizing then
      return
    end

    if frame.__resizeStartX and frame.__resizeStartY then
      local cursorX, cursorY = GetCursorPosition()
      local scale = (UIParent and UIParent.GetScale and UIParent:GetScale()) or 1
      local deltaX = math.abs(((cursorX or 0) / scale) - frame.__resizeStartX)
      local deltaY = math.abs(((cursorY or 0) / scale) - frame.__resizeStartY)
      if deltaX < 2 and deltaY < 2 then
        frame:SetSize(frame.__resizeStartW or frame:GetWidth(), frame.__resizeStartH or frame:GetHeight())
      end
    end

    frame.__resizing = false
    frame.__resizeStartW = nil
    frame.__resizeStartH = nil
    frame.__resizeStartX = nil
    frame.__resizeStartY = nil

    if not frame.__embeddedParent and not (UIState and UIState.collapsed) then
      CapturePopupBoardGeometry(frame)
    end
    if frame.__embeddedParent or not (UIState and UIState.collapsed) then
      LayoutBoardFrame(frame)
    end
  end

  local function StartPopupBoardResize(frame, resizeHandle)
    if not frame or not resizeHandle or frame.__embeddedParent or (UIState and UIState.collapsed) then
      return
    end

    ApplyPopupBoardResizeLimits(frame)
    PreparePopupBoardForUserPlacement(frame)
    frame.__resizing = true
    frame.__resizeStartW = frame:GetWidth()
    frame.__resizeStartH = frame:GetHeight()

    local cursorX, cursorY = GetCursorPosition()
    local scale = (UIParent and UIParent.GetScale and UIParent:GetScale()) or 1
    frame.__resizeStartX = (cursorX or 0) / scale
    frame.__resizeStartY = (cursorY or 0) / scale

    resizeHandle:SetScript("OnUpdate", function()
      if not frame.__resizing then
        return
      end

      local currentX, currentY = GetCursorPosition()
      local uiScale = (UIParent and UIParent.GetScale and UIParent:GetScale()) or 1
      currentX = (currentX or 0) / uiScale
      currentY = (currentY or 0) / uiScale

      local deltaX = currentX - (frame.__resizeStartX or currentX)
      local deltaY = (frame.__resizeStartY or currentY) - currentY
      local nextW = (frame.__resizeStartW or frame:GetWidth()) + deltaX
      local nextH = (frame.__resizeStartH or frame:GetHeight()) + deltaY
      local clampedWidth, clampedHeight = ClampPopupBoardSize(nextW, nextH)

      frame:SetSize(clampedWidth, clampedHeight)
      LayoutBoardFrame(frame)
    end)
  end

  local function BindPopupBoardDragScripts(frame)
    if not frame then
      return
    end
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", function(self)
      PreparePopupBoardForUserPlacement(self)
      self:StartMoving()
    end)
    frame:SetScript("OnDragStop", function(self)
      self:StopMovingOrSizing()
      CapturePopupBoardGeometry(self)
    end)
  end

  local function ApplyBoardSectionSkin(frame)
    if not frame then
      return
    end
    ApplyFrameSkin(frame)
    if frame.SetBackdropColor then
      frame:SetBackdropColor(0.05, 0.05, 0.07, 0.96)
    end
  end

  local function UpdateBoardButtonStyleState(button)
    if not button then
      return
    end

    local enabled = not button.IsEnabled or button:IsEnabled()
    local hovered = button.__lidBoardHovered == true
    local isPrimary = button.__lidBoardPrimary == true
    local isSelected = button.__lidBoardSelected == true
    local isUtility = button.__lidBoardUtility == true
    local isWindowControl = button.__lidBoardWindowControl == true
    local borderColor = enabled
        and (isPrimary and { 0.24, 0.82, 0.40, 0.96 }
          or (isSelected and { 0.30, 0.66, 1.00, 0.98 }
            or (isUtility and { 0.30, 0.50, 0.66, 0.78 }
              or (isWindowControl and { 0.40, 0.43, 0.48, 0.72 } or { 0.72, 0.58, 0.20, 0.92 }))))
        or { 0.24, 0.24, 0.28, 0.72 }
    local fillColor
    local textColor

    if not enabled then
      fillColor = { 0.06, 0.06, 0.08, 0.90 }
      textColor = { 0.48, 0.48, 0.54, 1.00 }
    elseif hovered then
      fillColor = isPrimary and { 0.10, 0.28, 0.15, 0.98 }
        or (isSelected and { 0.10, 0.28, 0.44, 0.98 }
          or (isUtility and { 0.10, 0.20, 0.30, 0.96 }
            or (isWindowControl and { 0.14, 0.15, 0.18, 0.96 } or { 0.15, 0.18, 0.24, 0.98 })))
      textColor = isPrimary and { 0.70, 1.00, 0.76, 1.00 }
        or (isSelected and { 0.70, 0.90, 1.00, 1.00 }
          or (isUtility and { 0.72, 0.88, 1.00, 1.00 }
            or (isWindowControl and { 0.92, 0.92, 0.96, 1.00 } or { 1.00, 0.90, 0.40, 1.00 })))
    else
      fillColor = isPrimary and { 0.07, 0.20, 0.11, 0.96 }
        or (isSelected and { 0.06, 0.18, 0.32, 0.96 }
          or (isUtility and { 0.07, 0.12, 0.18, 0.92 }
            or (isWindowControl and { 0.08, 0.09, 0.11, 0.88 } or { 0.10, 0.12, 0.16, 0.96 })))
      textColor = isPrimary and { 0.46, 0.96, 0.56, 1.00 }
        or (isSelected and { 0.50, 0.82, 1.00, 1.00 }
          or (isUtility and { 0.60, 0.76, 0.90, 1.00 }
            or (isWindowControl and { 0.72, 0.74, 0.80, 1.00 } or { 0.94, 0.84, 0.34, 1.00 })))
    end

    if button.__lidBoardBorder then
      button.__lidBoardBorder:SetVertexColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4])
    end
    if button.__lidBoardFill then
      button.__lidBoardFill:SetVertexColor(fillColor[1], fillColor[2], fillColor[3], fillColor[4])
    end

    local fontString = button.label or (button.GetFontString and button:GetFontString()) or nil
    if fontString and fontString.SetTextColor then
      fontString:SetTextColor(textColor[1], textColor[2], textColor[3], textColor[4])
    end
  end

  ApplyBoardButtonStyle = function(button)
    if not button then
      return button
    end
    if button.__lidBoardStyled then
      UpdateBoardButtonStyleState(button)
      return button
    end

    button.__lidBoardStyled = true
    if button.SetNormalTexture then button:SetNormalTexture("") end
    if button.SetPushedTexture then button:SetPushedTexture("") end
    if button.SetHighlightTexture then button:SetHighlightTexture("") end
    if button.SetDisabledTexture then button:SetDisabledTexture("") end

    if button.GetRegions then
      local regions = { button:GetRegions() }
      for _, region in ipairs(regions) do
        if region and region.GetObjectType and region:GetObjectType() == "Texture" then
          region:SetTexture("")
          region:Hide()
        end
      end
    end

    local border = button:CreateTexture(nil, "BACKGROUND")
    border:SetAllPoints(true)
    border:SetTexture("Interface\\Buttons\\WHITE8x8")
    button.__lidBoardBorder = border

    local fill = button:CreateTexture(nil, "BORDER")
    fill:SetPoint("TOPLEFT", 1, -1)
    fill:SetPoint("BOTTOMRIGHT", -1, 1)
    fill:SetTexture("Interface\\Buttons\\WHITE8x8")
    button.__lidBoardFill = fill

    if button.HookScript then
      button:HookScript("OnEnter", function(self)
        self.__lidBoardHovered = true
        UpdateBoardButtonStyleState(self)
      end)
      button:HookScript("OnLeave", function(self)
        self.__lidBoardHovered = false
        UpdateBoardButtonStyleState(self)
      end)
      button:HookScript("OnShow", function(self)
        UpdateBoardButtonStyleState(self)
      end)
      button:HookScript("OnEnable", function(self)
        UpdateBoardButtonStyleState(self)
      end)
      button:HookScript("OnDisable", function(self)
        UpdateBoardButtonStyleState(self)
      end)
    end

    UpdateBoardButtonStyleState(button)
    return button
  end

  local function TruncateCollapsedBoardText(text, maxLength)
    local resolved = TrimText(text)
    local limit = math.max(8, tonumber(maxLength) or 32)
    if resolved == "" or #resolved <= limit then
      return resolved
    end
    return resolved:sub(1, limit - 3) .. "..."
  end

  local function ApplyPopupBoardLayout(frame)
    if not frame then
      return
    end
    frame:SetParent(UIParent)
    RaisePopupFrameAboveEditor(frame, "FULLSCREEN_DIALOG")
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame.__embeddedParent = nil
    BindPopupBoardDragScripts(frame)
    ApplyPopupBoardGeometry(frame, frame.__popupGeometry)
    if frame.closeBtn then
      frame.closeBtn:Show()
    end
    if frame.collapseBtn then
      frame.collapseBtn:Show()
    end
    if frame.boardSyncBtn then
      frame.boardSyncBtn:SetShown(not (UIState and UIState.collapsed))
    end
    if frame.tradeHelperBtn then
      frame.tradeHelperBtn:SetShown(not (UIState and UIState.collapsed))
    end
    if frame.resizeHandle then
      frame.resizeHandle:Show()
    end
  end

  local function ApplyEmbeddedBoardLayout(frame, parent)
    if not frame or not parent then
      return
    end
    CapturePopupBoardGeometry(frame)
    if UIState and UIState.embeddedHistoryFrame then
      UIState.embeddedHistoryFrame:Hide()
    end
    frame:SetParent(parent)
    frame:SetFrameStrata("DIALOG")
    if parent.GetFrameLevel and frame.SetFrameLevel then
      frame:SetFrameLevel(parent:GetFrameLevel() + 20)
    end
    frame:SetClampedToScreen(false)
    frame:SetMovable(false)
    frame:SetToplevel(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", nil)
    frame:SetScript("OnDragStop", nil)
    -- Mark this before anchoring so its first size event uses the embedded
    -- layout rules instead of momentarily enforcing popup dimensions.
    frame.__embeddedParent = parent
    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
    frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
    if frame.Raise then
      frame:Raise()
    end
    if frame.closeBtn then
      frame.closeBtn:Hide()
    end
    if frame.collapseBtn then
      frame.collapseBtn:Hide()
    end
    if frame.boardSyncBtn then
      frame.boardSyncBtn:Show()
    end
    if frame.tradeHelperBtn then
      frame.tradeHelperBtn:Show()
    end
    if frame.resizeHandle then
      frame.resizeHandle:Hide()
    end
  end

  LayoutBoardFrame = function(frame)
    if not frame then
      return
    end

    local isEmbedded = frame.__embeddedParent ~= nil
    local minWidth, minHeight
    if isEmbedded then
      minWidth = MIN_EMBEDDED_BOARD_WIDTH
      minHeight = MIN_EMBEDDED_BOARD_HEIGHT
    else
      minWidth, minHeight = GetPopupBoardSizeLimits()
    end
    local frameWidth = math.max(minWidth, math.floor((frame.GetWidth and frame:GetWidth()) or DEFAULT_POPUP_BOARD_WIDTH))
    local frameHeight = math.max(minHeight, math.floor((frame.GetHeight and frame:GetHeight()) or DEFAULT_POPUP_BOARD_HEIGHT))
    local contentWidth = frameWidth - 32
    local leftWidth = ClampBoardLayoutValue(math.floor((contentWidth - 28) * 0.31), 220, 280)
    local rightX = 16 + leftWidth + 20
    local rightWidth = math.max(280, frameWidth - rightX - 24)
    local memberAreaWidth = math.max(220, rightWidth - 40)
    local assignWidth = ClampBoardLayoutValue(math.floor(rightWidth * 0.56), 220, 320)
    local topInset = 68
    local bottomInset = 40
    local panelGap = 10
    local selectionPanelHeight = 98
    local assignmentPanelHeight = 120
    local memberPanelTop = topInset + selectionPanelHeight + panelGap
    local memberPanelBottomInset = bottomInset + assignmentPanelHeight + panelGap
    local buttonRowX = rightX

    if frame.itemsPanel then
      frame.itemsPanel:ClearAllPoints()
      frame.itemsPanel:SetPoint("TOPLEFT", 16, -topInset)
      frame.itemsPanel:SetPoint("BOTTOMLEFT", 16, bottomInset)
      frame.itemsPanel:SetWidth(leftWidth)
    end
    if frame.selectionPanel then
      frame.selectionPanel:ClearAllPoints()
      frame.selectionPanel:SetPoint("TOPLEFT", rightX, -topInset)
      frame.selectionPanel:SetWidth(rightWidth)
      frame.selectionPanel:SetHeight(selectionPanelHeight)
    end
    if frame.assignmentPanel then
      frame.assignmentPanel:ClearAllPoints()
      frame.assignmentPanel:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", rightX, bottomInset)
      frame.assignmentPanel:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, bottomInset)
      frame.assignmentPanel:SetHeight(assignmentPanelHeight)
    end
    if frame.membersPanel then
      frame.membersPanel:ClearAllPoints()
      frame.membersPanel:SetPoint("TOPLEFT", rightX, -memberPanelTop)
      frame.membersPanel:SetPoint("BOTTOMRIGHT", -16, memberPanelBottomInset)
    end

    if frame.note then
      frame.note:SetWidth(frameWidth - 36)
    end
    if frame.leftHeader then
      frame.leftHeader:ClearAllPoints()
      frame.leftHeader:SetPoint("TOPLEFT", frame.itemsPanel or frame, "TOPLEFT", 12, -12)
    end
    if frame.assignedHeader then
      local assignedHeaderOffset = tonumber(frame.__assignedHeaderOffsetY)
      frame.assignedHeader:ClearAllPoints()
      frame.assignedHeader:SetPoint("TOPLEFT", frame.itemsPanel or frame, "TOPLEFT", 12, -(assignedHeaderOffset or 58))
      if assignedHeaderOffset then
        frame.assignedHeader:Show()
      else
        frame.assignedHeader:Hide()
      end
    end
    if frame.tradedHeader then
      local tradedHeaderOffset = tonumber(frame.__tradedHeaderOffsetY)
      frame.tradedHeader:ClearAllPoints()
      frame.tradedHeader:SetPoint("TOPLEFT", frame.itemsPanel or frame, "TOPLEFT", 12, -(tradedHeaderOffset or 58))
      if tradedHeaderOffset then
        frame.tradedHeader:Show()
      else
        frame.tradedHeader:Hide()
      end
    end
    if frame.rightHeader then
      frame.rightHeader:ClearAllPoints()
      frame.rightHeader:SetPoint("TOPLEFT", frame.selectionPanel or frame, "TOPLEFT", 12, -12)
    end
    if frame.memberSectionTitle then
      frame.memberSectionTitle:ClearAllPoints()
      frame.memberSectionTitle:SetPoint("TOPLEFT", frame.membersPanel or frame, "TOPLEFT", 12, -12)
    end
    if frame.memberColumnToggleLabel then
      frame.memberColumnToggleLabel:ClearAllPoints()
      frame.memberColumnToggleLabel:SetPoint("TOPLEFT", frame.membersPanel or frame, "TOPLEFT", 12, -36)
    end
    if frame.memberColumnToggleFrame then
      frame.memberColumnToggleFrame:ClearAllPoints()
      frame.memberColumnToggleFrame:SetPoint("TOPLEFT", frame.membersPanel or frame, "TOPLEFT", 68, -32)
      frame.memberColumnToggleFrame:SetSize(math.max(120, rightWidth - 92), 22)
      local toggleX = 0
      local toggleWidths = {
        items = 56,
        offspec = 38,
        transmog = 38,
        disenchant = 38,
        total = 66,
        bis = 42,
        dice = 50,
      }
      for _, check in ipairs(frame.memberColumnToggles or {}) do
        local key = TrimText(check.__columnKey)
        local width = tonumber(toggleWidths[key]) or 44
        check:ClearAllPoints()
        check:SetPoint("LEFT", toggleX, 0)
        check:SetSize(20, 20)
        if check.text then
          check.text:SetWidth(math.max(20, width - 18))
        end
        toggleX = toggleX + width
      end
    end
    for index, button in ipairs(frame.itemButtons or {}) do
      button:ClearAllPoints()
      button:SetSize(math.max(120, leftWidth - 20), 22)
      button:SetPoint("TOPLEFT", frame.itemsPanel or frame, "TOPLEFT", 10, -(tonumber(button.__itemListOffsetY) or (34 + ((index - 1) * 24))))
    end
    if frame.detailIcon then
      frame.detailIcon:ClearAllPoints()
      frame.detailIcon:SetPoint("TOPLEFT", frame.selectionPanel or frame, "TOPLEFT", 12, -38)
    end
    if frame.detailTooltipButton and frame.detailIcon then
      frame.detailTooltipButton:ClearAllPoints()
      frame.detailTooltipButton:SetPoint("TOPLEFT", frame.detailIcon, "TOPLEFT", 0, 0)
      frame.detailTooltipButton:SetSize(32, 32)
    end
    if frame.detailTitle then
      frame.detailTitle:ClearAllPoints()
      frame.detailTitle:SetPoint("TOPLEFT", frame.detailIcon, "TOPRIGHT", 10, -2)
      frame.detailTitle:SetWidth(math.max(160, rightWidth - 66))
    end
    if frame.detailMeta then
      frame.detailMeta:ClearAllPoints()
      frame.detailMeta:SetPoint("TOPLEFT", frame.selectionPanel or frame, "TOPLEFT", 12, -74)
      frame.detailMeta:SetWidth(rightWidth - 24)
    end
    if frame.assignHeader then
      frame.assignHeader:ClearAllPoints()
      frame.assignHeader:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", 12, -12)
    end
    if frame.assignTarget then
      frame.assignTarget:ClearAllPoints()
      frame.assignTarget:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", 12, -34)
      frame.assignTarget:SetWidth(rightWidth - 24)
    end
    if frame.assignDropdown then
      frame.assignDropdown:ClearAllPoints()
      frame.assignDropdown:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", 12, -58)
      if frame.assignDropdown.SetControlWidth then
        frame.assignDropdown:SetControlWidth(assignWidth)
      elseif frame.assignDropdown.SetWidth then
        frame.assignDropdown:SetWidth(assignWidth)
      end
      buttonRowX = rightX + assignWidth + 24
    end
    if frame.prevAssignBtn then
      frame.prevAssignBtn:Hide()
    end
    if frame.nextAssignBtn then
      frame.nextAssignBtn:Hide()
    end
    if frame.assignBtn then
      local buttonOffsetX = buttonRowX
      if frame.assignmentPanel then
        buttonOffsetX = buttonRowX - rightX
        if not frame.assignDropdown then
          buttonOffsetX = 12
        end
      end
      frame.assignBtn:ClearAllPoints()
      frame.assignBtn:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", buttonOffsetX, -58)
      frame.assignBtn:SetWidth(ClampBoardLayoutValue(rightWidth - buttonOffsetX - 12, 120, 180))
    end
    if frame.voteBtn then
      frame.voteBtn:Hide()
    end
    if frame.unassignBtn then
      frame.unassignBtn:ClearAllPoints()
      frame.unassignBtn:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", 12, -86)
    end
    if frame.changeRollBtn then
      frame.changeRollBtn:ClearAllPoints()
      if frame.unassignBtn then
        frame.changeRollBtn:SetPoint("LEFT", frame.unassignBtn, "RIGHT", 10, 0)
      else
        frame.changeRollBtn:SetPoint("TOPLEFT", frame.assignmentPanel or frame, "TOPLEFT", 108, -86)
      end
    end
    if frame.assignmentReadiness then
      frame.assignmentReadiness:ClearAllPoints()
      frame.assignmentReadiness:SetPoint("TOPRIGHT", frame.assignmentPanel or frame, "TOPRIGHT", -12, -14)
      frame.assignmentReadiness:SetWidth(math.max(150, rightWidth - 180))
    end
    if frame.memberHeader then
      frame.memberHeader:ClearAllPoints()
      frame.memberHeader:SetPoint("TOPLEFT", frame.membersPanel or frame, "TOPLEFT", 12, -60)
      frame.memberHeader:SetSize(rightWidth - 24, 18)
    end
    if frame.memberListScroll then
      frame.memberListScroll:ClearAllPoints()
      frame.memberListScroll:SetPoint("TOPLEFT", frame.membersPanel or frame, "TOPLEFT", 12, -80)
      frame.memberListScroll:SetPoint("BOTTOMRIGHT", frame.membersPanel or frame, "BOTTOMRIGHT", -12, 12)
    end
    if frame.memberListContent then
      frame.memberListContent:SetWidth(memberAreaWidth)
    end
    if frame.boardSyncBtn and frame.__embeddedParent and frame.tradeHelperBtn then
      frame.tradeHelperBtn:ClearAllPoints()
      frame.tradeHelperBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -16, -12)
      frame.tradeHelperBtn:SetWidth(104)
      frame.boardSyncBtn:ClearAllPoints()
      frame.boardSyncBtn:SetPoint("RIGHT", frame.tradeHelperBtn, "LEFT", -8, 0)
      frame.boardSyncBtn:SetWidth(88)
    elseif frame.boardSyncBtn and frame.tradeHelperBtn and frame.closeBtn then
      frame.tradeHelperBtn:ClearAllPoints()
      frame.tradeHelperBtn:SetPoint("RIGHT", frame.closeBtn, "LEFT", -18, 0)
      frame.tradeHelperBtn:SetWidth(96)
      frame.boardSyncBtn:ClearAllPoints()
      frame.boardSyncBtn:SetPoint("RIGHT", frame.tradeHelperBtn, "LEFT", -8, 0)
      frame.boardSyncBtn:SetWidth(82)
    elseif frame.boardSyncBtn and frame.collapseBtn then
      frame.boardSyncBtn:ClearAllPoints()
      frame.boardSyncBtn:SetPoint("LEFT", frame.collapseBtn, "RIGHT", 8, 0)
      frame.boardSyncBtn:SetWidth(88)
      if frame.tradeHelperBtn then
        frame.tradeHelperBtn:ClearAllPoints()
        frame.tradeHelperBtn:SetPoint("LEFT", frame.boardSyncBtn, "RIGHT", 8, 0)
        frame.tradeHelperBtn:SetWidth(104)
      end
    elseif frame.tradeHelperBtn and frame.boardSyncBtn and not frame.__embeddedParent then
      frame.tradeHelperBtn:ClearAllPoints()
      frame.tradeHelperBtn:SetPoint("LEFT", frame.boardSyncBtn, "RIGHT", 8, 0)
      frame.tradeHelperBtn:SetWidth(104)
    end
    if frame.hostToolsLabel then
      local showHostToolsLabel = not frame.__embeddedParent
        and frameWidth >= 760
        and frame.boardSyncBtn
        and frame.tradeHelperBtn
        and frame.closeBtn
      frame.hostToolsLabel:SetShown(showHostToolsLabel)
      if showHostToolsLabel then
        frame.hostToolsLabel:ClearAllPoints()
        frame.hostToolsLabel:SetPoint("RIGHT", frame.boardSyncBtn, "LEFT", -8, 0)
        frame.hostToolsLabel:SetWidth(64)
      end
    end

    local visibleColumns = type(frame.__memberColumnVisibility) == "table" and frame.__memberColumnVisibility or {}
    local optionalColumnLayouts = {}
    local optionalWidth = 0
    local optionalCount = 0
    for _, column in ipairs(OPTIONAL_MEMBER_COLUMNS) do
      if visibleColumns[column.key] == true then
        optionalCount = optionalCount + 1
      end
    end
    local compactColumns = memberAreaWidth < 560 or optionalCount >= 4
    local compactWidths = {
      items = 30,
      offspec = 22,
      transmog = 22,
      disenchant = 22,
      total = 36,
      bis = 24,
    }
    for _, column in ipairs(OPTIONAL_MEMBER_COLUMNS) do
      if visibleColumns[column.key] == true then
        optionalColumnLayouts[column.key] = {
          visible = true,
          width = compactColumns and (tonumber(compactWidths[column.key]) or 24) or (tonumber(column.width) or 32),
        }
        optionalWidth = optionalWidth + optionalColumnLayouts[column.key].width
      end
    end

    local noteWidth = compactColumns and 16 or 18
    local showDiceColumn = visibleColumns.dice == true
    local diceWidth = showDiceColumn and (compactColumns and 30 or 38) or 0
    local voteBtnWidth = compactColumns and 44 or 50
    local voteCountWidth = compactColumns and 28 or 34
    local rollWidth = compactColumns and ClampBoardLayoutValue(math.floor(memberAreaWidth * 0.16), 56, 90) or ClampBoardLayoutValue(math.floor(memberAreaWidth * 0.20), 76, 112)
    local padding = compactColumns and 6 or 8
    local columnGap = compactColumns and 3 or (memberAreaWidth < 500 and 4 or 6)
    local visibleGapCount = 3 + optionalCount + (showDiceColumn and 1 or 0)
    local nameWidth = math.max(compactColumns and 18 or 44, memberAreaWidth - (padding * 2) - noteWidth - optionalWidth - diceWidth - rollWidth - voteBtnWidth - voteCountWidth - (columnGap * visibleGapCount))
    local nameX = padding
    local rollX = nameX + nameWidth + columnGap
    local nextOptionalX = rollX + rollWidth + columnGap
    for _, column in ipairs(OPTIONAL_MEMBER_COLUMNS) do
      local columnLayout = optionalColumnLayouts[column.key]
      if columnLayout and column.beforeDice then
        columnLayout.x = nextOptionalX
        nextOptionalX = nextOptionalX + columnLayout.width + columnGap
      end
    end
    local diceX = nextOptionalX
    if showDiceColumn then
      nextOptionalX = diceX + diceWidth + columnGap
    end
    for _, column in ipairs(OPTIONAL_MEMBER_COLUMNS) do
      local columnLayout = optionalColumnLayouts[column.key]
      if columnLayout and not column.beforeDice then
        columnLayout.x = nextOptionalX
        nextOptionalX = nextOptionalX + columnLayout.width + columnGap
      end
    end
    local voteBtnX = nextOptionalX
    local voteCountX = voteBtnX + voteBtnWidth + columnGap
    local noteX = voteCountX + voteCountWidth + (compactColumns and 2 or 4)

    frame.__memberLayout = {
      nameX = nameX,
      nameWidth = nameWidth,
      rollX = rollX,
      rollWidth = rollWidth,
      diceX = diceX,
      diceWidth = diceWidth,
      diceVisible = showDiceColumn,
      optionalColumns = optionalColumnLayouts,
      voteBtnX = voteBtnX,
      voteBtnWidth = voteBtnWidth,
      voteCountX = voteCountX,
      voteCountWidth = voteCountWidth,
      noteX = noteX,
    }

    if frame.memberHeaderName then
      frame.memberHeaderName:ClearAllPoints()
      frame.memberHeaderName:SetPoint("LEFT", nameX, 0)
      frame.memberHeaderName:SetWidth(nameWidth)
    end
    if frame.memberHeaderRoll then
      frame.memberHeaderRoll:ClearAllPoints()
      frame.memberHeaderRoll:SetPoint("LEFT", rollX, 0)
      frame.memberHeaderRoll:SetWidth(rollWidth)
    end
    if frame.memberHeaderDice then
      frame.memberHeaderDice:SetShown(showDiceColumn)
      if showDiceColumn then
        frame.memberHeaderDice:ClearAllPoints()
        frame.memberHeaderDice:SetPoint("LEFT", diceX, 0)
        frame.memberHeaderDice:SetWidth(diceWidth)
      end
    end
    for _, column in ipairs(OPTIONAL_MEMBER_COLUMNS) do
      local header = frame[column.header]
      local columnLayout = optionalColumnLayouts[column.key]
      if header then
        header:SetShown(type(columnLayout) == "table" and columnLayout.visible == true)
        if type(columnLayout) == "table" and columnLayout.visible == true then
          header:ClearAllPoints()
          header:SetPoint("LEFT", tonumber(columnLayout.x) or 0, 0)
          header:SetWidth(tonumber(columnLayout.width) or tonumber(column.width) or 32)
        end
      end
    end
    if frame.memberHeaderState then
      frame.memberHeaderState:Hide()
    end
    if frame.memberHeaderVote then
      frame.memberHeaderVote:ClearAllPoints()
      frame.memberHeaderVote:SetPoint("LEFT", voteBtnX, 0)
      frame.memberHeaderVote:SetWidth(voteBtnWidth)
    end
    if frame.memberHeaderVotes then
      frame.memberHeaderVotes:ClearAllPoints()
      frame.memberHeaderVotes:SetPoint("LEFT", voteCountX, 0)
      frame.memberHeaderVotes:SetWidth(voteCountWidth)
    end

    local applyMemberRowLayout = type(GetApplyBoardMemberRowLayout) == "function" and GetApplyBoardMemberRowLayout() or nil
    if type(applyMemberRowLayout) == "function" then
      for _, row in ipairs(frame.memberRows or {}) do
        applyMemberRowLayout(frame, row)
      end
    end
  end

  local function UpdateCollapsedBoardSummary(frame)
    if not frame or not frame.note then
      return
    end
    local summary = GetLiveLootBoardSummary and GetLiveLootBoardSummary() or nil
    if type(summary) ~= "table" then
      frame.note:SetText("LidCouncil Live Board")
      return
    end
    local parts = {
      tostring(tonumber(summary.itemCount) or 0) .. " item(s)",
      tostring(tonumber(summary.pendingChoices) or 0) .. " pending",
    }
    local unassignedItems = tonumber(summary.unassignedItems) or 0
    if unassignedItems > 0 then
      local label
      if unassignedItems == 1 then
        label = "1 unassigned item"
      else
        label = tostring(unassignedItems) .. " unassigned items"
      end
      parts[#parts + 1] = WrapColor(label, { 1, 0.24, 0.24 })
    end
    if TrimText(summary.previewLabel) ~= "" then
      parts[#parts + 1] = TruncateCollapsedBoardText(summary.previewLabel, 26)
    end
    if TrimText(summary.latestItem) ~= "" then
      parts[#parts + 1] = TruncateCollapsedBoardText(summary.latestItem, 34)
    end
    frame.note:SetText(table.concat(parts, " | "))
  end

  local function SetBoardCollapsed(frame, collapsed)
    if not frame then
      return
    end

    if frame.__embeddedParent then
      collapsed = false
    end
    if UIState then
      UIState.collapsed = collapsed and true or false
    end
    local isCollapsed = UIState and UIState.collapsed

    local widgets = {
      frame.itemsPanel,
      frame.selectionPanel,
      frame.assignmentPanel,
      frame.membersPanel,
      frame.leftHeader,
      frame.assignedHeader,
      frame.tradedHeader,
      frame.rightHeader,
      frame.memberSectionTitle,
      frame.memberColumnToggleLabel,
      frame.detailIcon,
      frame.detailTitle,
      frame.detailMeta,
      frame.assignHeader,
      frame.assignTarget,
      frame.assignDropdown,
      frame.prevAssignBtn,
      frame.nextAssignBtn,
      frame.assignBtn,
      frame.boardSyncBtn,
      frame.tradeHelperBtn,
      frame.hostToolsLabel,
      frame.unassignBtn,
      frame.changeRollBtn,
      frame.memberColumnToggleFrame,
      frame.memberHeader,
      frame.memberListScroll,
    }
    for _, widget in ipairs(widgets) do
      if widget then
        if isCollapsed then
          widget:Hide()
        else
          widget:Show()
        end
      end
    end
    if frame.assignedHeader and not isCollapsed then
      if tonumber(frame.__assignedHeaderOffsetY) then
        frame.assignedHeader:Show()
      else
        frame.assignedHeader:Hide()
      end
    end
    if frame.tradedHeader and not isCollapsed then
      if tonumber(frame.__tradedHeaderOffsetY) then
        frame.tradedHeader:Show()
      else
        frame.tradedHeader:Hide()
      end
    end
    if frame.unassignBtn and not isCollapsed then
      frame.unassignBtn:SetShown(frame.__showUnassignBtn == true)
    end
    if frame.changeRollBtn and not isCollapsed then
      frame.changeRollBtn:SetShown(frame.__showChangeRollBtn == true)
    end
    for _, button in ipairs(frame.itemButtons or {}) do
      if not isCollapsed and TrimText(button.__itemKey) ~= "" then
        button:Show()
      else
        button:Hide()
      end
    end

    if frame.collapseBtn then
      frame.collapseBtn:SetText(isCollapsed and "Expand" or "Collapse")
      ApplyBoardButtonStyle(frame.collapseBtn)
    end
    if frame.boardSyncBtn then
      frame.boardSyncBtn:SetShown(not isCollapsed)
    end
    if frame.tradeHelperBtn then
      frame.tradeHelperBtn:SetShown(not isCollapsed)
    end
    if frame.resizeHandle then
      frame.resizeHandle:SetShown((not isCollapsed) and (not frame.__embeddedParent))
    end

    if isCollapsed then
      if not frame.__embeddedParent then
        CapturePopupBoardGeometry(frame)
      end
      frame:SetHeight(84)
      frame:SetWidth(380)
      if frame.note then
        frame.note:SetWidth(344)
        if frame.note.SetMaxLines then
          frame.note:SetMaxLines(2)
        end
      end
      UpdateCollapsedBoardSummary(frame)
    else
      if frame.__embeddedParent then
        ApplyEmbeddedBoardLayout(frame, frame.__embeddedParent)
      else
        ApplyPopupBoardGeometry(frame, frame.__popupGeometry)
      end
      if frame.note and frame.note.SetMaxLines then
        frame.note:SetMaxLines(3)
      end
      LayoutBoardFrame(frame)
    end
  end

  return {
    RaisePopupFrameAboveEditor = RaisePopupFrameAboveEditor,
    ApplyPopupBoardResizeLimits = ApplyPopupBoardResizeLimits,
    CapturePopupBoardGeometry = CapturePopupBoardGeometry,
    ApplyPopupBoardGeometry = ApplyPopupBoardGeometry,
    EnforcePopupBoardMinimumSize = EnforcePopupBoardMinimumSize,
    StartPopupBoardResize = StartPopupBoardResize,
    StopPopupBoardResize = StopPopupBoardResize,
    BindPopupBoardDragScripts = BindPopupBoardDragScripts,
    ApplyBoardSectionSkin = ApplyBoardSectionSkin,
    ApplyBoardButtonStyle = ApplyBoardButtonStyle,
    ApplyPopupBoardLayout = ApplyPopupBoardLayout,
    ApplyEmbeddedBoardLayout = ApplyEmbeddedBoardLayout,
    SetBoardCollapsed = SetBoardCollapsed,
    LayoutBoardFrame = LayoutBoardFrame,
  }
end
