local API = rawget(_G, "LIDCOUNCIL_API")
if type(API) ~= "table" then
  API = {}
  _G.LIDCOUNCIL_API = API
end

local function TrimText(value)
  local text = tostring(value or "")
  text = text:gsub("^%s+", "")
  text = text:gsub("%s+$", "")
  return text
end

local function Now()
  return (type(GetServerTime) == "function" and GetServerTime()) or (time and time() or 0)
end

local function GetApiFunction(name)
  local value = API[name]
  if type(value) == "function" then
    return value
  end
  value = rawget(_G, name)
  if type(value) == "function" then
    return value
  end
  return nil
end

local function RootDB()
  local provider = GetApiFunction("LIDCOUNCIL_RootDB")
  return provider and provider() or nil
end

local function GetCharacterKey()
  local provider = GetApiFunction("LIDCOUNCIL_GetCharacterKey")
  local value = provider and provider() or ""
  value = TrimText(value)
  if value ~= "" then
    return value
  end
  local name = type(UnitName) == "function" and UnitName("player") or ""
  local realm = type(GetRealmName) == "function" and GetRealmName() or ""
  name = TrimText(name)
  realm = TrimText(realm):gsub("%s+", "")
  if name ~= "" and realm ~= "" then
    return name .. "-" .. realm
  end
  return name ~= "" and name or "LocalPlayer"
end

local function GetLootProfile(profileName)
  local provider = GetApiFunction("LIDCOUNCIL_GetLootProfile")
  if type(provider) ~= "function" then
    return nil, TrimText(profileName)
  end
  return provider(profileName)
end

local function ExtractItemID(value)
  local text = TrimText(value)
  local linkID = text:match("item:(%d+):") or text:match("item:(%d+)")
  if linkID then
    return tonumber(linkID)
  end
  return tonumber(text)
end

local function ResolveProfileStorageKey(profileName)
  local profile, resolvedName = GetLootProfile(profileName)
  local profileId = type(profile) == "table" and type(profile.meta) == "table" and TrimText(profile.meta.profileId) or ""
  if profileId ~= "" then
    return "profileId:" .. profileId
  end
  resolvedName = TrimText(resolvedName ~= "" and resolvedName or profileName)
  if resolvedName ~= "" then
    return "profileName:" .. string.lower(resolvedName)
  end
  return "profileName:unknown"
end

local function ResolveDifficultyStorageSuffix(options)
  if type(options) ~= "table" or options.perDifficulty ~= true then
    return ""
  end
  local difficultyID = tonumber(options.difficultyID or options.lootDifficultyID or options.difficulty)
  if difficultyID and difficultyID > 0 then
    return ":difficulty:" .. tostring(math.floor(difficultyID))
  end
  local difficultyName = TrimText(options.difficultyName)
  if difficultyName ~= "" then
    return ":difficultyName:" .. string.lower(difficultyName)
  end
  return ""
end

local function ResolveBaseItemStorageKey(itemID, itemLink, itemName)
  local resolvedItemID = ExtractItemID(itemID) or ExtractItemID(itemLink)
  if resolvedItemID then
    return "itemID:" .. tostring(resolvedItemID)
  end
  local link = TrimText(itemLink)
  if link ~= "" then
    return "link:" .. string.lower(link)
  end
  local name = TrimText(itemName)
  if name ~= "" then
    return "name:" .. string.lower(name)
  end
  return ""
end

local function ResolveItemStorageKey(itemID, itemLink, itemName, options)
  local itemKey = ResolveBaseItemStorageKey(itemID, itemLink, itemName)
  if itemKey == "" then
    return ""
  end
  return itemKey .. ResolveDifficultyStorageSuffix(options)
end

local function EnsureRecommendationBucket(profileName)
  local root = RootDB()
  if type(root) ~= "table" then
    return nil
  end
  root.ui = type(root.ui) == "table" and root.ui or {}
  root.ui.personalRecommendations = type(root.ui.personalRecommendations) == "table" and root.ui.personalRecommendations or {}
  local characterKey = GetCharacterKey()
  root.ui.personalRecommendations[characterKey] = type(root.ui.personalRecommendations[characterKey]) == "table"
    and root.ui.personalRecommendations[characterKey]
    or {}
  local profileKey = ResolveProfileStorageKey(profileName)
  root.ui.personalRecommendations[characterKey][profileKey] = type(root.ui.personalRecommendations[characterKey][profileKey]) == "table"
    and root.ui.personalRecommendations[characterKey][profileKey]
    or {}
  return root.ui.personalRecommendations[characterKey][profileKey]
end

local function GetRecommendationBucket(profileName)
  local root = RootDB()
  if type(root) ~= "table" or type(root.ui) ~= "table" or type(root.ui.personalRecommendations) ~= "table" then
    return nil
  end
  local characterBucket = root.ui.personalRecommendations[GetCharacterKey()]
  if type(characterBucket) ~= "table" then
    return nil
  end
  local profileBucket = characterBucket[ResolveProfileStorageKey(profileName)]
  return type(profileBucket) == "table" and profileBucket or nil
end

local function SetPersonalRecommendation(profileName, itemID, itemLink, itemName, priority, option, options)
  local itemKey = ResolveItemStorageKey(itemID, itemLink, itemName, options)
  local resolvedPriority = math.floor(tonumber(priority) or 0)
  if itemKey == "" then
    return false, "Missing item identity."
  end
  if resolvedPriority <= 0 then
    return API.LIDCOUNCIL_ClearPersonalRecommendation(profileName, itemID, itemLink, itemName, options)
  end
  local bucket = EnsureRecommendationBucket(profileName)
  if type(bucket) ~= "table" then
    return false, "Personal recommendation storage is unavailable."
  end
  bucket[itemKey] = {
    priority = resolvedPriority,
    optionKey = TrimText(type(option) == "table" and (option.key or option.optionKey) or ""),
    optionLabel = TrimText(type(option) == "table" and (option.label or option.key or option.optionKey) or ""),
    itemID = ExtractItemID(itemID) or ExtractItemID(itemLink),
    itemLink = TrimText(itemLink),
    itemName = TrimText(itemName),
    perDifficulty = type(options) == "table" and options.perDifficulty == true or false,
    difficultyID = type(options) == "table" and tonumber(options.difficultyID or options.lootDifficultyID or options.difficulty) or nil,
    difficultyName = type(options) == "table" and TrimText(options.difficultyName) or "",
    updatedAt = Now(),
  }
  return true, bucket[itemKey]
end

local function ClearPersonalRecommendation(profileName, itemID, itemLink, itemName, options)
  local itemKey = ResolveItemStorageKey(itemID, itemLink, itemName, options)
  if itemKey == "" then
    return false, "Missing item identity."
  end
  local bucket = GetRecommendationBucket(profileName)
  if type(bucket) == "table" then
    bucket[itemKey] = nil
  end
  return true
end

local function GetPersonalRecommendation(profileName, itemID, itemLink, itemName, options)
  local itemKey = ResolveItemStorageKey(itemID, itemLink, itemName, options)
  if itemKey == "" then
    return nil
  end
  local bucket = GetRecommendationBucket(profileName)
  local stored = type(bucket) == "table" and bucket[itemKey] or nil
  return type(stored) == "table" and stored or nil
end

local function ResolvePersonalRecommendation(profileName, itemID, itemLink, itemName, rollOptions, options)
  local recommendation
  local difficultyID = type(options) == "table" and tonumber(options.difficultyID or options.lootDifficultyID or options.difficulty) or nil
  if (not difficultyID or difficultyID <= 0) and type(options) == "table" and options.exactDifficulty == true then
    return nil, nil, "NONE"
  end
  if difficultyID and difficultyID > 0 then
    recommendation = GetPersonalRecommendation(profileName, itemID, itemLink, itemName, {
      perDifficulty = true,
      difficultyID = difficultyID,
      difficultyName = type(options) == "table" and options.difficultyName or nil,
    })
    if type(recommendation) ~= "table" and type(options) == "table" and options.exactDifficulty == true then
      return nil, nil, "NONE"
    end
  end
  if type(recommendation) ~= "table" then
    recommendation = GetPersonalRecommendation(profileName, itemID, itemLink, itemName)
  end
  if type(recommendation) ~= "table" then
    return nil, nil, "NONE"
  end
  local priority = math.floor(tonumber(recommendation.priority) or 0)
  if priority <= 0 then
    return recommendation, nil, "MISSING"
  end
  local option = type(rollOptions) == "table" and rollOptions[priority] or nil
  if type(option) ~= "table" then
    return recommendation, nil, "MISSING"
  end
  return recommendation, option, "OK"
end

API.LIDCOUNCIL_SetPersonalRecommendation = SetPersonalRecommendation
API.LIDCOUNCIL_ClearPersonalRecommendation = ClearPersonalRecommendation
API.LIDCOUNCIL_GetPersonalRecommendation = GetPersonalRecommendation
API.LIDCOUNCIL_ResolvePersonalRecommendation = ResolvePersonalRecommendation
