local API = rawget(_G, "LIDCOUNCIL_API")
local INTERNAL_API = type(API) == "table" and type(API.__internal) == "table" and API.__internal or nil
if type(INTERNAL_API) ~= "table" then
  error("LidCouncil missing internal API registry.")
end

local shared = INTERNAL_API.LIDCOUNCIL_LootRuntimeUiShared_Impl
if type(shared) ~= "table" then
  error("LidCouncil missing loot runtime UI shared helpers.")
end

local ApplyFrameSkin = shared.ApplyFrameSkin
local GetLiveBoardHistoryPage = shared.GetLiveBoardHistoryPage
local GetLiveBoardHistorySnapshot = shared.GetLiveBoardHistorySnapshot
local TrimText = shared.TrimText
local FormatSelectionLabel = shared.FormatSelectionLabel
local ResolveBoardProfileName = shared.ResolveBoardProfileName
local UIState = shared.UIState

local HISTORY_LIST_LEFT = 16
local HISTORY_LIST_WIDTH = 220
local HISTORY_DETAIL_LEFT = 268
local HISTORY_DETAIL_RIGHT = -32
local HISTORY_DETAIL_WIDTH = 540
local LIVE_BOARD_HISTORY_PAGE_SIZE = 10
local HISTORY_LIST_ENTRY_HEIGHT = 52
local HISTORY_LIST_ENTRY_GAP = 6

local function RaisePopupFrameAboveEditor(frame)
  if not frame then
    return
  end
  if frame.SetFrameStrata then
    frame:SetFrameStrata("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 StripTemplateButtonTextures(button)
  if not button or button.__lid_historyTemplateStripped then
    return
  end

  button.__lid_historyTemplateStripped = true
  local normalTexture = button.GetNormalTexture and button:GetNormalTexture() or nil
  local pushedTexture = button.GetPushedTexture and button:GetPushedTexture() or nil
  local highlightTexture = button.GetHighlightTexture and button:GetHighlightTexture() or nil
  local disabledTexture = button.GetDisabledTexture and button:GetDisabledTexture() or nil

  if normalTexture and button.SetNormalTexture then button:SetNormalTexture("") end
  if pushedTexture and button.SetPushedTexture then button:SetPushedTexture("") end
  if highlightTexture and button.SetHighlightTexture then button:SetHighlightTexture("") end
  if disabledTexture and button.SetDisabledTexture then button:SetDisabledTexture("") end

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

local function EnsureHistoryListButtonSkin(button)
  if not button or button.__lid_historySkinned then
    return
  end

  button.__lid_historySkinned = true
  StripTemplateButtonTextures(button)

  local bg = button:CreateTexture(nil, "BACKGROUND")
  bg:SetAllPoints(true)
  bg:SetTexture("Interface\\Buttons\\WHITE8x8")
  bg:SetVertexColor(0.09, 0.07, 0.09, 0.90)
  button.__baseBg = bg

  local hover = button:CreateTexture(nil, "HIGHLIGHT")
  hover:SetAllPoints(true)
  hover:SetTexture("Interface\\Buttons\\WHITE8x8")
  hover:SetVertexColor(1.00, 0.88, 0.32, 0.10)
  button.__hoverBg = hover
end

local function CreateHistoryListButton(parent, width, height)
  local button = CreateFrame("Button", nil, parent)
  button:SetSize(width or 192, height or HISTORY_LIST_ENTRY_HEIGHT)
  button:RegisterForClicks("LeftButtonUp")

  local text = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  text:SetPoint("TOPLEFT", 10, -6)
  text:SetPoint("TOPRIGHT", -78, -6)
  text:SetJustifyH("LEFT")
  text:SetText("")
  button.__text = text

  local statusBg = button:CreateTexture(nil, "ARTWORK")
  statusBg:SetTexture("Interface\\Buttons\\WHITE8x8")
  statusBg:SetPoint("TOPRIGHT", -5, -5)
  statusBg:SetSize(70, 16)
  statusBg:SetVertexColor(0.12, 0.15, 0.20, 0.92)
  button.__statusBg = statusBg

  local statusText = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  statusText:SetPoint("TOPRIGHT", -8, -6)
  statusText:SetJustifyH("RIGHT")
  statusText:SetText("")
  button.__statusText = statusText

  local detailText = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  detailText:SetPoint("BOTTOMLEFT", 10, 7)
  detailText:SetPoint("BOTTOMRIGHT", -8, 7)
  detailText:SetJustifyH("LEFT")
  detailText:SetTextColor(0.66, 0.70, 0.78, 1.00)
  if detailText.SetMaxLines then detailText:SetMaxLines(1) end
  button.__detailText = detailText

  button.SetText = function(self, value)
    if self.__text then
      self.__text:SetText(tostring(value or ""))
    end
    if self.__statusText then
      self.__statusText:SetText("")
    end
    if self.__detailText then
      self.__detailText:SetText("")
    end
  end

  button.GetFontString = function(self)
    return self.__text
  end

  EnsureHistoryListButtonSkin(button)
  return button
end

local function FormatHistoryListTimestamp(value)
  local timestamp = tonumber(value)
  if not timestamp or timestamp <= 0 or type(date) ~= "function" then
    return "Unknown date"
  end
  return date("%d %b, %H:%M", timestamp)
end

local function SetHistoryListButtonEntry(button, entry, boardStatus, awardedItems, totalItems)
  if not button then
    return
  end
  local status = TrimText(boardStatus)
  local statusColor = { 0.72, 0.75, 0.82 }
  if status == "Completed" then
    statusColor = { 0.34, 0.92, 0.52 }
  elseif status == "In Progress" then
    statusColor = { 1.00, 0.82, 0.28 }
  elseif status == "Pending" then
    statusColor = { 1.00, 0.64, 0.28 }
  end
  if button.__text then
    button.__text:SetText(FormatHistoryListTimestamp(entry and (entry.updatedAt or entry.createdAt)))
  end
  if button.__statusText then
    button.__statusText:SetText(status)
    button.__statusText:SetTextColor(statusColor[1], statusColor[2], statusColor[3], 1.00)
  end
  if button.__statusBg then
    button.__statusBg:SetVertexColor(statusColor[1] * 0.18, statusColor[2] * 0.18, statusColor[3] * 0.18, 0.96)
  end
  if button.__detailText then
    local mode = entry and entry.isSimulation and "Simulation" or "Live Board"
    button.__detailText:SetText(string.format("%s | %d of %d assigned", mode, tonumber(awardedItems) or 0, tonumber(totalItems) or 0))
  end
end

local function SetHistoryListButtonSelected(button, selected)
  if not button then
    return
  end
  EnsureHistoryListButtonSkin(button)
  if not button.__selectionBg then
    local bg = button:CreateTexture(nil, "BORDER", nil, 1)
    bg:SetTexture("Interface\\Buttons\\WHITE8x8")
    bg:SetAllPoints(true)
    bg:SetVertexColor(0, 0, 0, 0)
    button.__selectionBg = bg
  end
  if not button.__selectionAccent then
    local accent = button:CreateTexture(nil, "ARTWORK", nil, 1)
    accent:SetTexture("Interface\\Buttons\\WHITE8x8")
    accent:SetPoint("TOPLEFT", 1, -1)
    accent:SetPoint("BOTTOMLEFT", 1, 1)
    accent:SetWidth(4)
    accent:SetVertexColor(0, 0, 0, 0)
    button.__selectionAccent = accent
  end
  if selected then
    if button.__baseBg then
      button.__baseBg:SetVertexColor(0.14, 0.10, 0.08, 0.94)
    end
    button.__selectionBg:SetVertexColor(0.90, 0.74, 0.18, 0.26)
    button.__selectionAccent:SetVertexColor(1.00, 0.88, 0.32, 0.96)
  else
    if button.__baseBg then
      button.__baseBg:SetVertexColor(0.09, 0.07, 0.09, 0.90)
    end
    button.__selectionBg:SetVertexColor(0, 0, 0, 0)
    button.__selectionAccent:SetVertexColor(0, 0, 0, 0)
  end
  if button.SetButtonState then
    button:SetButtonState(selected and "PUSHED" or "NORMAL", false)
  end
  local fontString = button.GetFontString and button:GetFontString() or nil
  if fontString and fontString.SetTextColor then
    if selected then
      fontString:SetTextColor(1.00, 0.88, 0.32, 1.00)
    else
      fontString:SetTextColor(0.92, 0.92, 0.96, 1.00)
    end
  end
end

local function FormatBoardTimestamp(value)
  local timestamp = tonumber(value)
  if not timestamp or timestamp <= 0 or type(date) ~= "function" then
    return "Unknown time"
  end
  return date("%d %b %Y, %H:%M", timestamp)
end

local function BuildHistoryBoardDetailText(board)
  if type(board) ~= "table" then
    return "No board history selected."
  end

  local lines = {}
  lines[#lines + 1] = string.format("%s | %s", TrimText(board.label ~= "" and board.label or "Live Board"), FormatBoardTimestamp(board.updatedAt or board.createdAt))
  if TrimText(board.profileName) ~= "" then
    lines[#lines + 1] = "Profile: " .. TrimText(board.profileName)
  end
  if TrimText(board.sourceProfileName) ~= "" then
    lines[#lines + 1] = "Source Profile: " .. TrimText(board.sourceProfileName)
  end
  lines[#lines + 1] = board.isSimulation and "Mode: Simulation" or "Mode: Live"
  lines[#lines + 1] = ""

  for _, itemKey in ipairs(board.itemOrder or {}) do
    local item = type(board.items) == "table" and board.items[itemKey] or nil
    if type(item) == "table" then
      local title = TrimText(item.link ~= "" and item.link or (item.name ~= "" and item.name or itemKey))
      if tonumber(item.itemLevel) then
        title = title .. " | iLvl " .. tostring(item.itemLevel)
      end
      lines[#lines + 1] = title
      if TrimText(item.awardedTo) ~= "" then
        local points = tonumber(item.awardedPoints) or 0
        lines[#lines + 1] = string.format("  Assigned to %s | %d %s", TrimText(item.awardedTo), points, points == 1 and "point" or "points")
      else
        lines[#lines + 1] = "  Pending assignment"
      end
      if TrimText(item.hostVoteFor) ~= "" then
        lines[#lines + 1] = "  Vote: " .. TrimText(item.hostVoteFor)
      end

      local members = {}
      for memberKey in pairs(item.selections or {}) do
        members[#members + 1] = memberKey
      end
      table.sort(members, function(a, b)
        return string.lower(tostring(a)) < string.lower(tostring(b))
      end)
      for _, memberKey in ipairs(members) do
        local selection = item.selections[memberKey]
        local label = FormatSelectionLabel(selection)
        local suffix = TrimText(selection and selection.note or "")
        if suffix ~= "" then
          suffix = " | " .. suffix
        end
        lines[#lines + 1] = string.format("  - %s: %s%s", tostring(memberKey), tostring(label), suffix)
      end
      lines[#lines + 1] = ""
    end
  end

  if #lines == 0 then
    return "No history available."
  end
  return table.concat(lines, "\n")
end

local function GetHistoryBoardStatus(board)
  if type(board) ~= "table" then
    return "Empty", 0, 0
  end

  local cachedTotalItems = tonumber(board.itemCount)
  local cachedAwardedItems = tonumber(board.awardedItemCount)
  if cachedTotalItems and cachedAwardedItems then
    if cachedTotalItems == 0 then
      return "Empty", 0, 0
    end
    if cachedAwardedItems <= 0 then
      return "Pending", cachedAwardedItems, cachedTotalItems
    end
    if cachedAwardedItems >= cachedTotalItems then
      return "Completed", cachedAwardedItems, cachedTotalItems
    end
    return "In Progress", cachedAwardedItems, cachedTotalItems
  end

  local totalItems = 0
  local awardedItems = 0
  for _, itemKey in ipairs(board.itemOrder or {}) do
    local item = type(board.items) == "table" and board.items[itemKey] or nil
    if type(item) == "table" then
      totalItems = totalItems + 1
      if tonumber(item.awardedAt) or TrimText(item.awardedTo) ~= "" then
        awardedItems = awardedItems + 1
      end
    end
  end

  if totalItems <= 0 then
    return "Empty", awardedItems, totalItems
  end
  if awardedItems <= 0 then
    return "Pending", awardedItems, totalItems
  end
  if awardedItems >= totalItems then
    return "Completed", awardedItems, totalItems
  end
  return "In Progress", awardedItems, totalItems
end

local function FormatHistoryRollLabel(selection)
  local label = TrimText(selection and (selection.label or selection.optionKey) or "Pending")
  if label == "" then
    label = "Pending"
  end
  return label
end

local function CountHistoryVotesForMember(item, memberKey)
  local targetKey = string.lower(TrimText(memberKey))
  if type(item) ~= "table" or targetKey == "" then
    return 0
  end
  local count = 0
  for _, votedTarget in pairs(item.votesByVoter or {}) do
    if string.lower(TrimText(votedTarget)) == targetKey then
      count = count + 1
    end
  end
  return count
end

local function BuildHistoryMemberRows(item)
  local rows = {}
  if type(item) ~= "table" then
    return rows
  end

  for memberKey, selection in pairs(item.selections or {}) do
    local selectionKey = string.lower(TrimText(selection and (selection.optionKey or selection.label) or "pending"))
    local rollValue = tonumber(selection and selection.rollValue)
    rows[#rows + 1] = {
      memberKey = tostring(memberKey or ""),
      rollLabel = FormatHistoryRollLabel(selection),
      rollKey = selectionKey,
      dice = rollValue,
      diceText = rollValue and tostring(math.floor(rollValue + 0.5)) or "-",
      votes = CountHistoryVotesForMember(item, memberKey),
      note = TrimText(selection and selection.note or ""),
      isPending = selectionKey == "" or selectionKey == "pending",
    }
  end

  table.sort(rows, function(left, right)
    if left.isPending ~= right.isPending then
      return not left.isPending
    end
    local leftRoll = string.lower(tostring(left.rollLabel or ""))
    local rightRoll = string.lower(tostring(right.rollLabel or ""))
    if leftRoll ~= rightRoll then
      return leftRoll < rightRoll
    end
    local leftDice = tonumber(left.dice) or -1
    local rightDice = tonumber(right.dice) or -1
    if leftDice ~= rightDice then
      return leftDice > rightDice
    end
    return string.lower(tostring(left.memberKey or "")) < string.lower(tostring(right.memberKey or ""))
  end)
  return rows
end

local function FormatHistoryRollSummary(resolvedRolls, totalRolls)
  local resolved = tonumber(resolvedRolls) or 0
  local total = tonumber(totalRolls) or 0
  if total <= 0 then
    return "No rolls"
  end
  if resolved >= total then
    return tostring(resolved) .. (resolved == 1 and " roll" or " rolls")
  end
  return string.format("%d of %d rolls", resolved, total)
end

local function BuildHistoryItemRowsData(board)
  local rows = {}
  if type(board) ~= "table" then
    return rows
  end

  for _, itemKey in ipairs(board.itemOrder or {}) do
    local item = type(board.items) == "table" and board.items[itemKey] or nil
    if type(item) == "table" then
      local title = TrimText(item.link ~= "" and item.link or (item.name ~= "" and item.name or itemKey))
      if title == "" then
        title = tostring(itemKey or "Unknown Item")
      end
      local assignedTo = TrimText(item.awardedTo)
      local tradeState = string.upper(TrimText(item.tradeState))
      local tradeComplete = tradeState == "TRADED" and tonumber(item.tradedAt)
      local assignmentState = assignedTo == "" and "Pending assignment" or (tradeComplete and "Traded" or "Assigned")
      local recipientText = ""
      if assignedTo ~= "" then
        local points = tonumber(item.awardedPoints) or 0
        recipientText = string.format("%s | %d %s", assignedTo, points, points == 1 and "point" or "points")
      end
      local memberRows = BuildHistoryMemberRows(item)
      local resolvedRolls = 0
      for _, memberRow in ipairs(memberRows) do
        if not memberRow.isPending then
          resolvedRolls = resolvedRolls + 1
        end
      end
      rows[#rows + 1] = {
        key = TrimText(item.key ~= "" and item.key or itemKey),
        title = title,
        icon = item.icon,
        itemLevel = tonumber(item.itemLevel),
        quality = tonumber(item.quality),
        recipientText = recipientText,
        assignmentState = assignmentState,
        assignmentPending = assignedTo == "",
        tradeComplete = tradeComplete and true or false,
        rollSummary = FormatHistoryRollSummary(resolvedRolls, #memberRows),
        memberRows = memberRows,
      }
    end
  end
  return rows
end

local function GetCachedHistoryItemRows(frame, board)
  if type(frame) ~= "table" or type(board) ~= "table" then
    return {}
  end

  local cacheKey = table.concat({
    TrimText(board.boardId),
    tostring(tonumber(board.updatedAt or board.createdAt) or 0),
    tostring(#(board.itemOrder or {})),
  }, "\31")
  if frame.__historyItemRowsCacheKey ~= cacheKey or type(frame.__historyItemRowsCache) ~= "table" then
    frame.__historyItemRowsCacheKey = cacheKey
    frame.__historyItemRowsCache = BuildHistoryItemRowsData(board)
  end
  return frame.__historyItemRowsCache
end

local function GetHistoryDetailWidth(frame)
  local width = frame and frame.detailScroll and frame.detailScroll.GetWidth and frame.detailScroll:GetWidth() or HISTORY_DETAIL_WIDTH
  width = math.floor((tonumber(width) or HISTORY_DETAIL_WIDTH) - 34)
  if width < 320 then
    width = 320
  end
  return width
end

local function ScrollHistoryFrame(scrollFrame, delta)
  if not scrollFrame or not scrollFrame.GetVerticalScrollRange or not scrollFrame.SetVerticalScroll then
    return
  end
  local range = math.max(0, tonumber(scrollFrame:GetVerticalScrollRange()) or 0)
  if range <= 0 then
    scrollFrame:SetVerticalScroll(0)
    return
  end
  local current = tonumber(scrollFrame:GetVerticalScroll()) or 0
  local nextOffset = current - ((tonumber(delta) or 0) * 42)
  if nextOffset < 0 then
    nextOffset = 0
  elseif nextOffset > range then
    nextOffset = range
  end
  scrollFrame:SetVerticalScroll(nextOffset)
end

local function BindHistoryMouseWheel(frame, scrollFrame)
  if not frame or not scrollFrame or frame.__lid_historyWheelBound then
    return
  end
  frame.__lid_historyWheelBound = true
  if frame.EnableMouse then
    frame:EnableMouse(true)
  end
  if frame.EnableMouseWheel then
    frame:EnableMouseWheel(true)
  end
  if frame.SetScript then
    frame:SetScript("OnMouseWheel", function(_, delta)
      ScrollHistoryFrame(scrollFrame, delta)
    end)
  end
end

local function EnsureHistoryItemMemberRows(row, count)
  if not row or not row.detailsFrame then
    return
  end
  row.memberRows = row.memberRows or {}
  while #row.memberRows < count do
    local memberRow = CreateFrame("Frame", nil, row.detailsFrame)
    memberRow:SetHeight(22)

    local memberText = memberRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    memberText:SetJustifyH("LEFT")
    if memberText.SetMaxLines then memberText:SetMaxLines(1) end
    memberRow.memberText = memberText

    local rollText = memberRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    rollText:SetJustifyH("LEFT")
    if rollText.SetMaxLines then rollText:SetMaxLines(1) end
    memberRow.rollText = rollText

    local diceText = memberRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    diceText:SetJustifyH("LEFT")
    if diceText.SetMaxLines then diceText:SetMaxLines(1) end
    memberRow.diceText = diceText

    local votesText = memberRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    votesText:SetJustifyH("LEFT")
    if votesText.SetMaxLines then votesText:SetMaxLines(1) end
    memberRow.votesText = votesText

    local noteText = memberRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    noteText:SetJustifyH("LEFT")
    if noteText.SetMaxLines then noteText:SetMaxLines(1) end
    memberRow.noteText = noteText

    if row.__owner and row.__owner.detailScroll then
      BindHistoryMouseWheel(memberRow, row.__owner.detailScroll)
    end
    row.memberRows[#row.memberRows + 1] = memberRow
  end
end

local function EnsureHistoryItemRows(frame, count)
  if not frame or not frame.historyItemsFrame then
    return
  end
  frame.historyItemRows = frame.historyItemRows or {}
  while #frame.historyItemRows < count do
    local row = CreateFrame("Frame", nil, frame.historyItemsFrame, "BackdropTemplate")
    row:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })
    row:SetBackdropColor(0.025, 0.030, 0.043, 0.94)
    row:SetHeight(60)
    row:EnableMouse(true)

    local separator = row:CreateTexture(nil, "ARTWORK")
    separator:SetTexture("Interface\\Buttons\\WHITE8x8")
    separator:SetPoint("BOTTOMLEFT", 6, 0)
    separator:SetPoint("BOTTOMRIGHT", -6, 0)
    separator:SetHeight(1)
    separator:SetVertexColor(0.24, 0.30, 0.40, 0.66)
    row.separator = separator

    local arrowText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    arrowText:SetJustifyH("CENTER")
    arrowText:SetTextColor(1.00, 0.82, 0.28, 1.00)
    row.arrowText = arrowText

    local icon = row:CreateTexture(nil, "ARTWORK")
    icon:SetSize(26, 26)
    row.icon = icon

    local titleText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    titleText:SetJustifyH("LEFT")
    if titleText.SetMaxLines then titleText:SetMaxLines(1) end
    row.titleText = titleText

    local metaText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    metaText:SetJustifyH("LEFT")
    if metaText.SetMaxLines then metaText:SetMaxLines(1) end
    row.metaText = metaText

    local recipientText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    recipientText:SetJustifyH("RIGHT")
    if recipientText.SetMaxLines then recipientText:SetMaxLines(1) end
    row.recipientText = recipientText

    local assignmentStateText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    assignmentStateText:SetJustifyH("RIGHT")
    if assignmentStateText.SetMaxLines then assignmentStateText:SetMaxLines(1) end
    row.assignmentStateText = assignmentStateText

    local detailsFrame = CreateFrame("Frame", nil, row, "BackdropTemplate")
    ApplyFrameSkin(detailsFrame)
    if detailsFrame.SetBackdropColor then
      detailsFrame:SetBackdropColor(0.05, 0.05, 0.07, 0.98)
    end
    detailsFrame:Hide()
    row.detailsFrame = detailsFrame

    local detailsTitle = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    detailsTitle:SetText("Candidate Responses")
    detailsTitle:SetJustifyH("LEFT")
    row.detailsTitle = detailsTitle

    local headerMember = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerMember:SetText("Raider")
    headerMember:SetJustifyH("LEFT")
    row.headerMember = headerMember

    local headerRoll = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerRoll:SetText("Roll")
    headerRoll:SetJustifyH("LEFT")
    row.headerRoll = headerRoll

    local headerDice = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerDice:SetText("Dice")
    headerDice:SetJustifyH("LEFT")
    row.headerDice = headerDice

    local headerVotes = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerVotes:SetText("Votes")
    headerVotes:SetJustifyH("LEFT")
    row.headerVotes = headerVotes

    local headerNote = detailsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    headerNote:SetText("Note")
    headerNote:SetJustifyH("LEFT")
    row.headerNote = headerNote

    row.memberRows = {}
    row:SetScript("OnEnter", function(self)
      self:SetBackdropColor(0.055, 0.065, 0.088, 0.98)
      if self.separator then
        self.separator:SetVertexColor(1.00, 0.82, 0.28, 0.84)
      end
    end)
    row:SetScript("OnLeave", function(self)
      self:SetBackdropColor(0.025, 0.030, 0.043, 0.94)
      if self.separator then
        self.separator:SetVertexColor(0.24, 0.30, 0.40, 0.66)
      end
    end)
    row:SetScript("OnMouseUp", function(self, mouseButton)
      if mouseButton ~= "LeftButton" or TrimText(self.__itemKey) == "" or not self.__owner then
        return
      end
      local owner = self.__owner
      local boardId = TrimText(self.__boardId)
      owner.historyExpandedItems = owner.historyExpandedItems or {}
      owner.historyExpandedItems[boardId] = owner.historyExpandedItems[boardId] or {}
      owner.historyExpandedItems[boardId][self.__itemKey] = not owner.historyExpandedItems[boardId][self.__itemKey]
      if owner.RefreshHistory then
        owner:RefreshHistory(owner.__lastProfileName)
      end
    end)

    BindHistoryMouseWheel(row, frame.detailScroll)
    BindHistoryMouseWheel(detailsFrame, frame.detailScroll)
    frame.historyItemRows[#frame.historyItemRows + 1] = row
  end
end

local function LayoutHistoryItemMemberRow(memberRow, width, index)
  local memberWidth = math.max(120, math.floor(width * 0.34))
  local rollWidth = math.max(90, math.floor(width * 0.20))
  local diceWidth = 44
  local votesWidth = 48
  local noteX = 8 + memberWidth + rollWidth + diceWidth + votesWidth
  local noteWidth = math.max(80, width - noteX - 8)
  memberRow:ClearAllPoints()
  memberRow:SetPoint("TOPLEFT", 0, -44 - ((index - 1) * 22))
  memberRow:SetSize(width, 22)
  memberRow.memberText:ClearAllPoints()
  memberRow.memberText:SetPoint("LEFT", 8, 0)
  memberRow.memberText:SetWidth(memberWidth - 8)
  memberRow.rollText:ClearAllPoints()
  memberRow.rollText:SetPoint("LEFT", 8 + memberWidth, 0)
  memberRow.rollText:SetWidth(rollWidth)
  memberRow.diceText:ClearAllPoints()
  memberRow.diceText:SetPoint("LEFT", 8 + memberWidth + rollWidth, 0)
  memberRow.diceText:SetWidth(diceWidth)
  memberRow.votesText:ClearAllPoints()
  memberRow.votesText:SetPoint("LEFT", 8 + memberWidth + rollWidth + diceWidth, 0)
  memberRow.votesText:SetWidth(votesWidth)
  memberRow.noteText:ClearAllPoints()
  memberRow.noteText:SetPoint("LEFT", noteX, 0)
  memberRow.noteText:SetWidth(noteWidth)
end

local function LayoutHistoryItemRow(row, width, expanded, memberCount)
  row.arrowText:ClearAllPoints()
  row.arrowText:SetPoint("LEFT", 10, 0)
  row.arrowText:SetSize(16, 20)
  row.icon:ClearAllPoints()
  row.icon:SetPoint("TOPLEFT", 32, -10)
  row.titleText:ClearAllPoints()
  row.titleText:SetPoint("TOPLEFT", 66, -8)
  row.titleText:SetWidth(math.max(120, width - 276))
  row.metaText:ClearAllPoints()
  row.metaText:SetPoint("TOPLEFT", 66, -30)
  row.metaText:SetWidth(math.max(120, width - 276))
  row.recipientText:ClearAllPoints()
  row.recipientText:SetPoint("TOPRIGHT", -12, -9)
  row.recipientText:SetWidth(238)
  row.assignmentStateText:ClearAllPoints()
  row.assignmentStateText:SetPoint("TOPRIGHT", -12, -31)
  row.assignmentStateText:SetWidth(238)

  local detailsHeight = expanded and (48 + (memberCount * 22)) or 0
  if expanded then
    local detailsWidth = math.max(260, width - 16)
    row.detailsFrame:ClearAllPoints()
    row.detailsFrame:SetPoint("TOPLEFT", 8, -56)
    row.detailsFrame:SetSize(detailsWidth, detailsHeight)
    row.detailsFrame:Show()
    row.detailsTitle:ClearAllPoints()
    row.detailsTitle:SetPoint("TOPLEFT", 8, -6)
    row.detailsTitle:SetWidth(detailsWidth - 16)

    local memberWidth = math.max(120, math.floor(detailsWidth * 0.34))
    local rollWidth = math.max(90, math.floor(detailsWidth * 0.20))
    local diceWidth = 44
    local votesWidth = 48
    local headerY = -26
    row.headerMember:ClearAllPoints()
    row.headerMember:SetPoint("TOPLEFT", 8, headerY)
    row.headerMember:SetWidth(memberWidth - 8)
    row.headerRoll:ClearAllPoints()
    row.headerRoll:SetPoint("TOPLEFT", 8 + memberWidth, headerY)
    row.headerRoll:SetWidth(rollWidth)
    row.headerDice:ClearAllPoints()
    row.headerDice:SetPoint("TOPLEFT", 8 + memberWidth + rollWidth, headerY)
    row.headerDice:SetWidth(diceWidth)
    row.headerVotes:ClearAllPoints()
    row.headerVotes:SetPoint("TOPLEFT", 8 + memberWidth + rollWidth + diceWidth, headerY)
    row.headerVotes:SetWidth(votesWidth)
    row.headerNote:ClearAllPoints()
    row.headerNote:SetPoint("TOPLEFT", 8 + memberWidth + rollWidth + diceWidth + votesWidth, headerY)
    row.headerNote:SetWidth(math.max(80, detailsWidth - (8 + memberWidth + rollWidth + diceWidth + votesWidth) - 8))
  else
    row.detailsFrame:Hide()
  end

  row:SetSize(width, expanded and (64 + detailsHeight) or 60)
  row.layoutHeight = expanded and (64 + detailsHeight) or 60
end

local function SetupHistoryDetailArea(frame)
  local summary = frame.detailContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  summary:SetPoint("TOPLEFT", 8, 0)
  summary:SetJustifyH("LEFT")
  summary:SetJustifyV("TOP")
  summary:SetText("")
  frame.detailSummary = summary

  local itemRowsFrame = CreateFrame("Frame", nil, frame.detailContent)
  itemRowsFrame:SetPoint("TOPLEFT", 8, -70)
  itemRowsFrame:SetSize(HISTORY_DETAIL_WIDTH - 16, 1)
  frame.historyItemsFrame = itemRowsFrame
  frame.historyItemRows = {}
  frame.historyExpandedItems = {}

  BindHistoryMouseWheel(frame.detailContent, frame.detailScroll)
  BindHistoryMouseWheel(itemRowsFrame, frame.detailScroll)
end

local function RefreshHistoryBoardDetails(frame, board, boardStatus, awardedItems, totalItems)
  if not frame or not frame.detailContent then
    return
  end

  local width = GetHistoryDetailWidth(frame)
  frame.detailContent:SetWidth(width)
  if frame.detailSummary then
    frame.detailSummary:SetWidth(width - 8)
  end

  if type(board) ~= "table" then
    if frame.detailSummary then
      frame.detailSummary:SetText("No board history selected.")
    end
    for _, row in ipairs(frame.historyItemRows or {}) do
      row:Hide()
    end
    frame.detailContent:SetHeight(64)
    return
  end

  local summaryLines = {
    string.format("%s | %d of %d assigned", board.isSimulation and "Simulation Board" or "Live Board", tonumber(awardedItems) or 0, tonumber(totalItems) or 0),
    FormatBoardTimestamp(board.updatedAt or board.createdAt),
  }
  if TrimText(board.profileName) ~= "" then
    summaryLines[#summaryLines + 1] = "Profile: " .. TrimText(board.profileName)
  end
  frame.__historySessionId = TrimText(board.sessionId)
  if board.isSimulation then
    summaryLines[#summaryLines + 1] = "|cffffb84dSimulation history uses preview members and does not reflect real raid history.|r"
  end
  if frame.detailSummary then
    frame.detailSummary:SetText(table.concat(summaryLines, "\n"))
  end

  local summaryHeight = math.max(58, math.ceil((frame.detailSummary and frame.detailSummary.GetStringHeight and frame.detailSummary:GetStringHeight()) or 58))
  local rowsTop = -(summaryHeight + 14)
  if frame.historyItemsFrame then
    frame.historyItemsFrame:ClearAllPoints()
    frame.historyItemsFrame:SetPoint("TOPLEFT", 8, rowsTop)
    frame.historyItemsFrame:SetWidth(width - 16)
  end

  local itemRows = GetCachedHistoryItemRows(frame, board)
  EnsureHistoryItemRows(frame, #itemRows)

  local boardId = TrimText(board.boardId)
  local expandedForBoard = type(frame.historyExpandedItems) == "table" and type(frame.historyExpandedItems[boardId]) == "table" and frame.historyExpandedItems[boardId] or {}
  local y = 0
  local rowWidth = math.max(260, width - 16)
  for index, itemData in ipairs(itemRows) do
    local row = frame.historyItemRows[index]
    local expanded = expandedForBoard[itemData.key] and true or false
    row.__owner = frame
    row.__profileName = frame.__lastProfileName
    row.__boardId = boardId
    row.__itemKey = itemData.key
    row.arrowText:SetText(expanded and "v" or ">")
    row.icon:SetTexture(itemData.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
    row.titleText:SetText(itemData.title)
    row.metaText:SetText(string.format("%s%s", itemData.itemLevel and ("iLvl " .. tostring(itemData.itemLevel) .. " | ") or "", itemData.rollSummary))
    row.recipientText:SetText(itemData.recipientText)
    row.recipientText:SetTextColor(0.88, 0.91, 0.98, 1.00)
    row.assignmentStateText:SetText(itemData.assignmentState)
    if itemData.assignmentPending then
      row.assignmentStateText:SetTextColor(1.00, 0.72, 0.32, 1.00)
    elseif itemData.tradeComplete then
      row.assignmentStateText:SetTextColor(0.42, 0.94, 0.58, 1.00)
    else
      row.assignmentStateText:SetTextColor(0.56, 0.74, 0.98, 1.00)
    end

    EnsureHistoryItemMemberRows(row, #itemData.memberRows)
    LayoutHistoryItemRow(row, rowWidth, expanded, #itemData.memberRows)
    row:ClearAllPoints()
    row:SetPoint("TOPLEFT", 0, -y)

    if expanded then
      for memberIndex, memberRow in ipairs(row.memberRows or {}) do
        local memberData = itemData.memberRows[memberIndex]
        if memberData then
          memberRow.memberText:SetText(memberData.memberKey)
          memberRow.rollText:SetText(memberData.rollLabel)
          memberRow.diceText:SetText(memberData.diceText)
          memberRow.votesText:SetText(tostring(tonumber(memberData.votes) or 0))
          memberRow.noteText:SetText(memberData.note ~= "" and memberData.note or "-")
          if memberData.isPending then
            memberRow.rollText:SetTextColor(0.70, 0.70, 0.76, 1.00)
            memberRow.diceText:SetTextColor(0.70, 0.70, 0.76, 1.00)
          else
            memberRow.rollText:SetTextColor(0.92, 0.92, 0.96, 1.00)
            memberRow.diceText:SetTextColor(1.00, 0.88, 0.32, 1.00)
          end
          memberRow:Show()
          LayoutHistoryItemMemberRow(memberRow, math.max(260, rowWidth - 16), memberIndex)
        else
          memberRow:Hide()
        end
      end
    else
      for _, memberRow in ipairs(row.memberRows or {}) do
        memberRow:Hide()
      end
    end

    row:Show()
    y = y + (row.layoutHeight or 54) + 6
  end
  for index = #itemRows + 1, #(frame.historyItemRows or {}) do
    frame.historyItemRows[index]:Hide()
  end
  if frame.historyItemsFrame then
    frame.historyItemsFrame:SetHeight(math.max(1, y))
  end

  local emptyExtra = #itemRows == 0 and 28 or 0
  if #itemRows == 0 and frame.detailSummary then
    frame.detailSummary:SetText(frame.detailSummary:GetText() .. "\n\nNo dropped item history exists for this board snapshot.")
  end
  frame.detailContent:SetHeight(math.max(1, summaryHeight + 24 + y + emptyExtra))
  if frame.detailScroll and frame.detailScroll.UpdateScrollChildRect then
    frame.detailScroll:UpdateScrollChildRect()
  end
  if frame.detailScroll and frame.detailScroll.GetVerticalScrollRange and frame.detailScroll.GetVerticalScroll and frame.detailScroll.SetVerticalScroll then
    local range = math.max(0, tonumber(frame.detailScroll:GetVerticalScrollRange()) or 0)
    local current = tonumber(frame.detailScroll:GetVerticalScroll()) or 0
    if current > range then
      frame.detailScroll:SetVerticalScroll(range)
    end
  end
end

local function RefreshPagedBoardHistory(frame, profileName)
  local pageData = type(GetLiveBoardHistoryPage) == "function"
    and GetLiveBoardHistoryPage(profileName, frame.historyPage, LIVE_BOARD_HISTORY_PAGE_SIZE)
    or nil
  if type(pageData) ~= "table" then
    pageData = {
      entries = {},
      page = 1,
      totalPages = 1,
      signature = "",
    }
  end
  local entries = type(pageData.entries) == "table" and pageData.entries or {}
  frame.entries = entries

  local signature = TrimText(pageData.signature)
  if frame.__historyPagingSignature ~= signature then
    frame.__historyPagingSignature = signature
    frame.historyPage = 1
    frame.selectedBoardId = nil
    if type(GetLiveBoardHistoryPage) == "function" then
      pageData = GetLiveBoardHistoryPage(profileName, frame.historyPage, LIVE_BOARD_HISTORY_PAGE_SIZE)
      entries = type(pageData) == "table" and type(pageData.entries) == "table" and pageData.entries or {}
      frame.entries = entries
    end
  end

  local totalPages = math.max(1, tonumber(pageData.totalPages) or 1)
  local page = math.max(1, tonumber(pageData.page) or 1)
  frame.historyPage = page
  frame.historyTotalPages = totalPages
  local pageEntries = entries

  while #frame.historyButtons < #pageEntries do
    local button = CreateHistoryListButton(frame.listContent, frame.historyButtonWidth or 192, HISTORY_LIST_ENTRY_HEIGHT)
    button:SetScript("OnClick", function(btn)
      frame.selectedBoardId = TrimText(btn.__boardId)
      frame:RefreshHistory(frame.__lastProfileName)
    end)
    frame.historyButtons[#frame.historyButtons + 1] = button
  end

  local selectedEntry = nil
  for index, entry in ipairs(pageEntries) do
    local button = frame.historyButtons[index]
    button:SetWidth(frame.historyButtonWidth or 192)
    button:ClearAllPoints()
    button:SetPoint("TOPLEFT", 0, -((index - 1) * (HISTORY_LIST_ENTRY_HEIGHT + HISTORY_LIST_ENTRY_GAP)))
    button.__boardId = TrimText(type(entry) == "table" and entry.boardId or "")
    local boardStatus, awardedItems, totalItems = GetHistoryBoardStatus(entry)
    SetHistoryListButtonEntry(button, entry, boardStatus, awardedItems, totalItems)
    if frame.selectedBoardId ~= "" and frame.selectedBoardId == button.__boardId then
      selectedEntry = entry
    end
    SetHistoryListButtonSelected(button, frame.selectedBoardId ~= "" and frame.selectedBoardId == button.__boardId)
    button:Show()
  end
  for index = #pageEntries + 1, #frame.historyButtons do
    frame.historyButtons[index]:Hide()
  end
  frame.listContent:SetHeight(math.max(1, #pageEntries * (HISTORY_LIST_ENTRY_HEIGHT + HISTORY_LIST_ENTRY_GAP)))

  if not selectedEntry and type(pageEntries[1]) == "table" then
    selectedEntry = pageEntries[1]
    frame.selectedBoardId = TrimText(selectedEntry.boardId)
    if frame.historyButtons[1] then
      SetHistoryListButtonSelected(frame.historyButtons[1], true)
    end
  end

  local showPager = totalPages > 1
  if frame.historyPrevBtn then
    frame.historyPrevBtn:SetEnabled(page > 1)
    frame.historyPrevBtn:SetShown(showPager)
  end
  if frame.historyPageText then
    frame.historyPageText:SetText(showPager and string.format("Page %d / %d", page, totalPages) or "")
    frame.historyPageText:SetShown(showPager)
  end
  if frame.historyNextBtn then
    frame.historyNextBtn:SetEnabled(page < totalPages)
    frame.historyNextBtn:SetShown(showPager)
  end

  local selectedBoard = type(GetLiveBoardHistorySnapshot) == "function"
    and GetLiveBoardHistorySnapshot(profileName, frame.selectedBoardId)
    or nil
  local boardStatus, awardedItems, totalItems = GetHistoryBoardStatus(selectedBoard or selectedEntry)
  RefreshHistoryBoardDetails(frame, selectedBoard, boardStatus, awardedItems, totalItems)
end

local function CreateBoardHistoryPopup()
  if UIState.historyPopup then
    return UIState.historyPopup
  end

  local frame = CreateFrame("Frame", "LIDCOUNCIL_LiveBoardHistory", UIParent, "BackdropTemplate")
  frame:SetSize(860, 520)
  frame:SetPoint("CENTER", 0, 0)
  frame:SetFrameStrata("FULLSCREEN_DIALOG")
  frame:SetMovable(true)
  frame:EnableMouse(true)
  frame:SetToplevel(true)
  frame:RegisterForDrag("LeftButton")
  frame:SetScript("OnDragStart", frame.StartMoving)
  frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
  ApplyFrameSkin(frame)
  frame:HookScript("OnShow", RaisePopupFrameAboveEditor)
  frame:Hide()

  local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  title:SetPoint("TOPLEFT", 16, -14)
  title:SetText("Item History")
  frame.title = title

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

  local listTitle = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  listTitle:SetPoint("TOPLEFT", HISTORY_LIST_LEFT, -44)
  listTitle:SetText("Sessions")
  frame.listTitle = listTitle

  local listScroll = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
  listScroll:SetPoint("TOPLEFT", HISTORY_LIST_LEFT, -64)
  listScroll:SetPoint("BOTTOMLEFT", HISTORY_LIST_LEFT, 46)
  listScroll:SetWidth(HISTORY_LIST_WIDTH)
  frame.listScroll = listScroll

  local listContent = CreateFrame("Frame", nil, listScroll)
  listContent:SetSize(196, 1)
  listScroll:SetScrollChild(listContent)
  frame.listContent = listContent
  frame.historyButtonWidth = 192
  frame.historyButtons = {}
  BindHistoryMouseWheel(listScroll, listScroll)
  BindHistoryMouseWheel(listContent, listScroll)

  local historyPrevBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
  historyPrevBtn:SetSize(60, 20)
  historyPrevBtn:SetPoint("BOTTOMLEFT", HISTORY_LIST_LEFT, 16)
  historyPrevBtn:SetText("< Prev")
  historyPrevBtn:Hide()
  frame.historyPrevBtn = historyPrevBtn

  local historyPageText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  historyPageText:SetPoint("LEFT", historyPrevBtn, "RIGHT", 4, 0)
  historyPageText:SetJustifyH("CENTER")
  historyPageText:SetText("")
  historyPageText:Hide()
  frame.historyPageText = historyPageText

  local historyNextBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
  historyNextBtn:SetSize(60, 20)
  historyNextBtn:SetPoint("BOTTOMRIGHT", HISTORY_LIST_LEFT + HISTORY_LIST_WIDTH, 16)
  historyNextBtn:SetText("Next >")
  historyNextBtn:Hide()
  frame.historyNextBtn = historyNextBtn
  historyPageText:SetPoint("RIGHT", historyNextBtn, "LEFT", -4, 0)

  local detailTitle = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  detailTitle:SetPoint("TOPLEFT", HISTORY_DETAIL_LEFT, -44)
  detailTitle:SetText("Items from Selected Session")
  frame.detailTitle = detailTitle

  local detailScroll = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
  detailScroll:SetPoint("TOPLEFT", HISTORY_DETAIL_LEFT, -64)
  detailScroll:SetPoint("BOTTOMRIGHT", HISTORY_DETAIL_RIGHT, 16)
  frame.detailScroll = detailScroll
  BindHistoryMouseWheel(detailScroll, detailScroll)

  local detailContent = CreateFrame("Frame", nil, detailScroll)
  detailContent:SetSize(HISTORY_DETAIL_WIDTH, 1)
  detailScroll:SetScrollChild(detailContent)
  frame.detailContent = detailContent

  SetupHistoryDetailArea(frame)

  function frame:RefreshHistory(profileName)
    self.__lastProfileName = profileName
    RefreshPagedBoardHistory(self, profileName)
  end

  historyPrevBtn:SetScript("OnClick", function()
    frame.historyPage = math.max(1, (tonumber(frame.historyPage) or 1) - 1)
    frame.selectedBoardId = nil
    frame:RefreshHistory(frame.__lastProfileName)
  end)
  historyNextBtn:SetScript("OnClick", function()
    local totalPages = math.max(1, tonumber(frame.historyTotalPages) or 1)
    frame.historyPage = math.min(totalPages, (tonumber(frame.historyPage) or 1) + 1)
    frame.selectedBoardId = nil
    frame:RefreshHistory(frame.__lastProfileName)
  end)

  detailScroll:SetScript("OnSizeChanged", function()
    if frame:IsShown() and frame.RefreshHistory and TrimText(frame.__lastProfileName) ~= "" then
      frame:RefreshHistory(frame.__lastProfileName)
    end
  end)

  UIState.historyPopup = frame
  return frame
end

local function OpenBoardHistoryPopup(frame, profileName)
  local resolvedProfileName = TrimText(profileName or ResolveBoardProfileName(frame))
  if resolvedProfileName == "" then
    return
  end
  local popup = CreateBoardHistoryPopup()
  popup:RefreshHistory(resolvedProfileName)
  popup:Show()
end

local function CreateEmbeddedBoardHistoryFrame()
  if UIState.embeddedHistoryFrame then
    return UIState.embeddedHistoryFrame
  end

  local frame = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
  frame:SetFrameStrata("DIALOG")
  ApplyFrameSkin(frame)
  frame:Hide()

  local listPanel = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  ApplyFrameSkin(listPanel)
  listPanel:SetPoint("TOPLEFT", 8, -8)
  listPanel:SetPoint("BOTTOMLEFT", 8, 8)
  listPanel:SetWidth(244)
  frame.listPanel = listPanel

  local listPanelTitle = listPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  listPanelTitle:SetPoint("TOPLEFT", 10, -8)
  listPanelTitle:SetText("Sessions")

  local listScroll = CreateFrame("ScrollFrame", nil, listPanel, "UIPanelScrollFrameTemplate")
  listScroll:SetPoint("TOPLEFT", 8, -30)
  listScroll:SetPoint("BOTTOMRIGHT", -8, 38)
  frame.listScroll = listScroll

  local listContent = CreateFrame("Frame", nil, listScroll)
  listContent:SetSize(210, 1)
  listScroll:SetScrollChild(listContent)
  frame.listContent = listContent
  frame.historyButtonWidth = 210
  frame.historyButtons = {}
  BindHistoryMouseWheel(listScroll, listScroll)
  BindHistoryMouseWheel(listContent, listScroll)

  local historyPrevBtn = CreateFrame("Button", nil, listPanel, "UIPanelButtonTemplate")
  historyPrevBtn:SetSize(60, 20)
  historyPrevBtn:SetPoint("BOTTOMLEFT", 8, 10)
  historyPrevBtn:SetText("< Prev")
  historyPrevBtn:Hide()
  frame.historyPrevBtn = historyPrevBtn

  local historyNextBtn = CreateFrame("Button", nil, listPanel, "UIPanelButtonTemplate")
  historyNextBtn:SetSize(60, 20)
  historyNextBtn:SetPoint("BOTTOMRIGHT", -8, 10)
  historyNextBtn:SetText("Next >")
  historyNextBtn:Hide()
  frame.historyNextBtn = historyNextBtn

  local historyPageText = listPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
  historyPageText:SetPoint("LEFT", historyPrevBtn, "RIGHT", 4, 0)
  historyPageText:SetPoint("RIGHT", historyNextBtn, "LEFT", -4, 0)
  historyPageText:SetJustifyH("CENTER")
  historyPageText:SetText("")
  historyPageText:Hide()
  frame.historyPageText = historyPageText

  local detailPanel = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  ApplyFrameSkin(detailPanel)
  detailPanel:SetPoint("TOPLEFT", 260, -8)
  detailPanel:SetPoint("BOTTOMRIGHT", -8, 8)
  frame.detailPanel = detailPanel

  local detailPanelTitle = detailPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
  detailPanelTitle:SetPoint("TOPLEFT", 10, -8)
  detailPanelTitle:SetText("Items from Selected Session")

  local detailScroll = CreateFrame("ScrollFrame", nil, detailPanel, "UIPanelScrollFrameTemplate")
  detailScroll:SetPoint("TOPLEFT", 8, -30)
  detailScroll:SetPoint("BOTTOMRIGHT", -8, 8)
  frame.detailScroll = detailScroll
  BindHistoryMouseWheel(detailScroll, detailScroll)

  local detailContent = CreateFrame("Frame", nil, detailScroll)
  detailContent:SetSize(HISTORY_DETAIL_WIDTH, 1)
  detailScroll:SetScrollChild(detailContent)
  frame.detailContent = detailContent

  SetupHistoryDetailArea(frame)

  function frame:RefreshHistory(profileName)
    self.__lastProfileName = profileName
    RefreshPagedBoardHistory(self, profileName)
  end

  historyPrevBtn:SetScript("OnClick", function()
    frame.historyPage = math.max(1, (tonumber(frame.historyPage) or 1) - 1)
    frame.selectedBoardId = nil
    frame:RefreshHistory(frame.__lastProfileName)
  end)
  historyNextBtn:SetScript("OnClick", function()
    local totalPages = math.max(1, tonumber(frame.historyTotalPages) or 1)
    frame.historyPage = math.min(totalPages, (tonumber(frame.historyPage) or 1) + 1)
    frame.selectedBoardId = nil
    frame:RefreshHistory(frame.__lastProfileName)
  end)

  detailScroll:SetScript("OnSizeChanged", function()
    if frame:IsShown() and frame.RefreshHistory and TrimText(frame.__lastProfileName) ~= "" then
      frame:RefreshHistory(frame.__lastProfileName)
    end
  end)

  UIState.embeddedHistoryFrame = frame
  return frame
end

local function AttachLiveLootBoardHistory(parent, profileName)
  if not parent then
    return
  end

  local resolvedProfileName = TrimText(profileName)
  if resolvedProfileName == "" then
    return
  end

  if UIState.boardFrame and UIState.boardFrame.__embeddedParent then
    UIState.boardFrame:Hide()
  end

  local frame = CreateEmbeddedBoardHistoryFrame()
  frame:SetParent(parent)
  frame:ClearAllPoints()
  frame:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
  frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
  if parent.GetFrameLevel and frame.SetFrameLevel then
    frame:SetFrameLevel(parent:GetFrameLevel() + 20)
  end
  frame:RefreshHistory(resolvedProfileName)
  frame:Show()
end

INTERNAL_API.LIDCOUNCIL_LootRuntimeUiHistory_Impl = {
  FormatBoardTimestamp = FormatBoardTimestamp,
  BuildHistoryBoardDetailText = BuildHistoryBoardDetailText,
  CreateBoardHistoryPopup = CreateBoardHistoryPopup,
  OpenBoardHistoryPopup = OpenBoardHistoryPopup,
  CreateEmbeddedBoardHistoryFrame = CreateEmbeddedBoardHistoryFrame,
  AttachLiveLootBoardHistory = AttachLiveLootBoardHistory,
}

INTERNAL_API.LIDCOUNCIL_AttachLiveLootBoardHistory_Impl = AttachLiveLootBoardHistory
