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

INTERNAL_API.LIDCOUNCIL_CreateLootBoardRenderHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  local function SetBoardItemButtonState(button, entry, itemKey, titleText, selected)
    if not button then
      return
    end
    button.__itemKey = itemKey
    button.__itemTooltipLink = type(entry) == "table" and entry.link or nil
    button.__itemTooltipItemID = type(entry) == "table" and (entry.itemID or entry.link) or nil
    button.__itemTooltipName = type(entry) == "table" and (entry.name or itemKey) or itemKey
    button.__itemSelected = selected and true or false
    if button.itemIcon then
      local texture = type(entry) == "table" and entry.icon or nil
      button.itemIcon:SetTexture(texture or "Interface\\Icons\\INV_Misc_QuestionMark")
      button.itemIcon:Show()
    end
    if button.itemText then
      button.itemText:SetText(titleText)
      if selected then
        button.itemText:SetTextColor(1.00, 0.90, 0.42, 1)
      else
        button.itemText:SetTextColor(0.92, 0.92, 0.96, 1)
      end
    end
    if button.__bg then
      if selected then
        button.__bg:SetVertexColor(0.22, 0.18, 0.08, 0.96)
      else
        button.__bg:SetVertexColor(0.09, 0.08, 0.10, 0.55)
      end
    end
    if button.__highlight then
      button.__highlight:SetShown(selected)
      if selected then
        button.__highlight:SetVertexColor(0.90, 0.74, 0.18, 0.20)
      end
    end
    button:Show()
  end

  local function BuildBoardItemButtonTitle(entry, itemKey)
    local title = tostring((type(entry) == "table" and (entry.link or entry.name)) or itemKey or "?")
    if type(entry) == "table" and entry.awardedAt then
      title = "|TInterface\\Buttons\\UI-CheckBox-Check:14:14:0:0|t " .. title
    end
    return title
  end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    if selectedMemberName then
      local assignRow = selectedMember or { key = preview.selectedMemberKey, name = selectedMemberName }
      frame.assignTarget:SetText("|cffffd100Current Target|r\n" .. BuildMemberDisplayText(assignRow, { iconSize = 16 }) .. " | " .. tostring(selectedMemberLabel or "Pending"))
    elseif preview.isSimulation and resolvedRollCount <= 0 then
      frame.assignTarget:SetText("|cffffd100Current Target|r\n(awaiting simulated rolls)")
    else
      frame.assignTarget:SetText("|cffffd100Current Target|r\n(preview)")
    end

    if selected.awardedAt and selected.awardedTo then
      SetBoardAwardStatus(
        frame,
        "success",
        "Award Complete",
        BuildAwardStatusDetail(tostring(preview.profileName or "Simulation"), selected)
      )
    else
      local projectedPoints = ResolveBoardProjectedPoints(
        tostring(preview.profileName or "Simulation"),
        nil,
        selected,
        selectedMember and selectedMember.key or nil
      )
      selected.projectedPoints = projectedPoints
      if pendingRollCount > 0 then
        local waitingText = canAssign
          and string.format(
            "%d participant(s) still pending. You can still assign now.\nProjected award: %d point(s).",
            pendingRollCount,
            projectedPoints
          )
          or string.format(
            "%d participant(s) still pending.\nProjected award: %d point(s).",
            pendingRollCount,
            projectedPoints
          )
        SetBoardAwardStatus(
          frame,
          "waiting",
          "Await Rolls",
          waitingText
        )
      else
        SetBoardAwardStatus(
          frame,
          "ready",
          "All Participants Selected",
          string.format("Projected award: %d point(s).", projectedPoints)
        )
      end
    end

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

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