local EnsureApiRegistry = rawget(_G, "LIDCOUNCIL_EnsureApiRegistry")
local API, INTERNAL_API
if type(EnsureApiRegistry) == "function" then
  API, INTERNAL_API = EnsureApiRegistry()
else
  API = rawget(_G, "LIDCOUNCIL_API")
  if type(API) ~= "table" then
    API = {}
    _G.LIDCOUNCIL_API = API
  end
  if type(API.__internal) ~= "table" then
    API.__internal = {}
  end
  INTERNAL_API = API.__internal
end

INTERNAL_API.LIDCOUNCIL_CreateLootSessionIdentityHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}
  local TrimText = type(deps.TrimText) == "function" and deps.TrimText or function(value)
    return tostring(value or "")
  end
  local NormalizeCharacterKey = type(deps.NormalizeCharacterKey) == "function"
    and deps.NormalizeCharacterKey
    or TrimText

  local function NormalizeRealmName(value)
    local text = TrimText(value)
    text = text:gsub("%s+", "")
    return text
  end

  local function GetLocalRealmName()
    if type(GetNormalizedRealmName) == "function" then
      local normalizedRealm = NormalizeRealmName(GetNormalizedRealmName())
      if normalizedRealm ~= "" then
        return normalizedRealm
      end
    end
    if type(GetRealmName) == "function" then
      local realmName = NormalizeRealmName(GetRealmName())
      if realmName ~= "" then
        return realmName
      end
    end
    return ""
  end

  local function SplitCharacterKey(value)
    local key = NormalizeCharacterKey(value)
    local name, realm = key:match("^([^%-]+)%-(.+)$")
    if name then
      return string.lower(name), string.lower(NormalizeRealmName(realm))
    end
    return string.lower(key), ""
  end

  local function AreCharacterKeysSame(left, right)
    local leftKey = NormalizeCharacterKey(left)
    local rightKey = NormalizeCharacterKey(right)
    if leftKey == "" or rightKey == "" then
      return false
    end
    if string.lower(leftKey) == string.lower(rightKey) then
      return true
    end

    local leftName, leftRealm = SplitCharacterKey(leftKey)
    local rightName, rightRealm = SplitCharacterKey(rightKey)
    if leftName == "" or rightName == "" or leftName ~= rightName then
      return false
    end
    return leftRealm == "" or rightRealm == "" or leftRealm == rightRealm
  end

  local function BuildCharacterKeyFromUnit(unit)
    if TrimText(unit) == "" then
      return ""
    end

    local name, realm = nil, nil
    if type(UnitFullName) == "function" then
      name, realm = UnitFullName(unit)
    end
    if not name and type(UnitName) == "function" then
      name = UnitName(unit)
    end

    local resolvedName = TrimText(name)
    if resolvedName == "" then
      return ""
    end

    local resolvedRealm = NormalizeRealmName(realm)
    if resolvedRealm == "" and TrimText(unit) == "player" then
      resolvedRealm = GetLocalRealmName()
    end
    if resolvedRealm ~= "" then
      return NormalizeCharacterKey(resolvedName .. "-" .. resolvedRealm)
    end
    return NormalizeCharacterKey(resolvedName)
  end

  local function CollectGroupCharacterKeys()
    local keys = {}
    local seen = {}
    local function AddUnit(unit)
      local key = BuildCharacterKeyFromUnit(unit)
      if key ~= "" and not seen[key] then
        seen[key] = true
        keys[#keys + 1] = key
      end
    end

    AddUnit("player")
    if type(IsInRaid) == "function" and IsInRaid() and type(GetNumGroupMembers) == "function" then
      local count = tonumber(GetNumGroupMembers()) or 0
      for index = 1, count do
        AddUnit("raid" .. tostring(index))
      end
    elseif type(IsInGroup) == "function" and IsInGroup() and type(GetNumSubgroupMembers) == "function" then
      local count = tonumber(GetNumSubgroupMembers()) or 0
      for index = 1, count do
        AddUnit("party" .. tostring(index))
      end
    end

    table.sort(keys)
    return keys
  end

  local function IsCurrentGroupCharacter(characterKey)
    local resolvedKey = NormalizeCharacterKey(characterKey)
    if resolvedKey == "" then
      return false
    end
    for _, groupKey in ipairs(CollectGroupCharacterKeys()) do
      if AreCharacterKeysSame(groupKey, resolvedKey) then
        return true
      end
    end
    return false
  end

  local function NormalizeSenderKey(sender)
    local senderKey = NormalizeCharacterKey(sender)
    if senderKey == "" or senderKey:find("-", 1, true) then
      return senderKey
    end

    -- CHAT_MSG_ADDON can omit the sender realm. Never attach the receiver's
    -- realm: that makes a valid cross-realm host appear to be a different player.
    local senderName = select(1, SplitCharacterKey(senderKey))
    local matchingGroupMembers = 0
    local matchingGroupKey = ""
    for _, groupKey in ipairs(CollectGroupCharacterKeys()) do
      local groupName = select(1, SplitCharacterKey(groupKey))
      if senderName ~= "" and groupName == senderName then
        matchingGroupMembers = matchingGroupMembers + 1
        matchingGroupKey = groupKey
        if matchingGroupMembers > 1 then
          return ""
        end
      end
    end

    return matchingGroupMembers == 1 and matchingGroupKey or ""
  end

  local function IsClaimedActorSenderMatch(sender, claimedCharacterKey)
    local senderKey = NormalizeSenderKey(sender)
    local claimedKey = NormalizeCharacterKey(claimedCharacterKey)
    return senderKey ~= "" and claimedKey ~= "" and AreCharacterKeysSame(senderKey, claimedKey)
  end

  local function IsActiveSessionHostSender(activeSession, sender)
    return AreCharacterKeysSame(type(activeSession) == "table" and activeSession.host or "", NormalizeSenderKey(sender))
  end

  local function IsJoinedSessionCharacter(activeSession, characterKey)
    local resolvedKey = NormalizeCharacterKey(characterKey)
    if resolvedKey == "" then
      return false
    end
    if AreCharacterKeysSame(type(activeSession) == "table" and activeSession.host or "", resolvedKey) then
      return true
    end
    local member = type(activeSession) == "table" and type(activeSession.members) == "table" and activeSession.members[resolvedKey] or nil
    if type(member) == "table" and tostring(member.status or "JOINED") == "JOINED" then
      return true
    end
    if type(activeSession) == "table" and type(activeSession.members) == "table" then
      for storedKey, storedMember in pairs(activeSession.members) do
        if AreCharacterKeysSame(storedKey, resolvedKey)
          and type(storedMember) == "table"
          and tostring(storedMember.status or "JOINED") == "JOINED"
        then
          return true
        end
      end
    end
    return false
  end

  local function DoesActiveSessionMatch(activeSession, activeProfileName, sessionId, profileName)
    if type(activeSession) ~= "table" or TrimText(activeSession.id) ~= TrimText(sessionId) then
      return false
    end
    local expectedProfileName = TrimText(profileName)
    return expectedProfileName == ""
      or TrimText(activeProfileName) == expectedProfileName
      or TrimText(activeSession.profileName) == expectedProfileName
      or TrimText(activeSession.remoteProfileName) == expectedProfileName
  end

  local function GetGroupDistribution()
    local instanceCategory = rawget(_G, "LE_PARTY_CATEGORY_INSTANCE") or 2
    if type(IsInGroup) == "function" and IsInGroup(instanceCategory) then
      return "INSTANCE_CHAT"
    end
    if type(IsInRaid) == "function" and IsInRaid() then
      return "RAID"
    end
    if type(IsInGroup) == "function" and IsInGroup() then
      return "PARTY"
    end
    return nil
  end

  return {
    GetGroupDistribution = GetGroupDistribution,
    CollectGroupCharacterKeys = CollectGroupCharacterKeys,
    IsCurrentGroupCharacter = IsCurrentGroupCharacter,
    NormalizeSenderKey = NormalizeSenderKey,
    IsClaimedActorSenderMatch = IsClaimedActorSenderMatch,
    IsActiveSessionHostSender = IsActiveSessionHostSender,
    IsJoinedSessionCharacter = IsJoinedSessionCharacter,
    DoesActiveSessionMatch = DoesActiveSessionMatch,
  }
end
