local ADDON_NAME, ns = ...
local WF = ns.WF

local Price = WF.Price

-- Default to a stable, region-wide value.
-- Falls back to dbmarket if the custom string isn't available.
local DEFAULT_TSM_PRICE = "dbregionmarketavg"

local function Trim(s)
  if type(s) ~= "string" then
    return ""
  end
  return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end

local function ParseProvider(p)
  if p == nil then
    return "vendor", nil
  end
  if type(p) ~= "string" then
    p = tostring(p)
  end
  p = Trim(p)
  if p == "" then
    return "vendor", nil
  end

  local a, b = p:match("^([%a_]+)%:(.+)$")
  if a and b then
    a = a:lower()
    b = Trim(b)
    if b == "" then b = nil end
    return a, b
  end

  return p:lower(), nil
end

-- -------------------------------------------------------
-- Addon discovery helpers (avoid false "not installed" on Classic forks, etc.)
-- -------------------------------------------------------
local function _WFFindAddOnByNeedle(needle)
  if not (GetNumAddOns and GetAddOnInfo) then
    return nil
  end
  needle = tostring(needle or "")
  if needle == "" then return nil end
  needle = needle:lower()

  local n = GetNumAddOns()
  for i = 1, n do
    local name, title = GetAddOnInfo(i)
    if name and tostring(name):lower():find(needle, 1, true) then
      return name
    end
    if title and tostring(title):lower():find(needle, 1, true) then
      return name
    end
  end

  return nil
end

local function _WFAddOnExists(name)
  if not name or name == "" then return false end
  if GetAddOnInfo then
    local ok, title = pcall(GetAddOnInfo, name)
    if ok and title ~= nil then
      return true
    end
  end
  return false
end

local function _WFIsAddOnLoaded(name)
  if not name or name == "" then return false end
  if C_AddOns and C_AddOns.IsAddOnLoaded then
    local ok, v = pcall(C_AddOns.IsAddOnLoaded, name)
    if ok and v then return true end
  end
  if IsAddOnLoaded then
    local ok, v = pcall(IsAddOnLoaded, name)
    if ok and v then return true end
  end
  return false
end

local function _WFIsAddOnEnabled(name)
  if not name or name == "" then return nil end
  if GetAddOnEnableState and UnitName then
    local ok, state = pcall(GetAddOnEnableState, UnitName("player"), name)
    if ok and type(state) == "number" then
      return state > 0
    end
  end
  return nil
end

local function _WFResolveProviderAddOnName(provider)
  provider = tostring(provider or ""):lower()
  if provider == "auctionator" then
    if _WFAddOnExists("Auctionator") then
      return "Auctionator"
    end
    return _WFFindAddOnByNeedle("auctionator")
  elseif provider == "tsm" then
    if _WFAddOnExists("TradeSkillMaster") then
      return "TradeSkillMaster"
    end
    return _WFFindAddOnByNeedle("tradeskillmaster")
  end
  return nil
end


local function _WFNow()
  if GetTime then
    local ok, t = pcall(GetTime)
    if ok and type(t) == "number" then return t end
  end
  return 0
end

-- -------------------------------------------------------
-- Theme helpers
-- -------------------------------------------------------
local function Clamp01(x)
  x = tonumber(x) or 0
  if x < 0 then return 0 end
  if x > 1 then return 1 end
  return x
end

local function RGBToHex(r, g, b)
  r = math.floor(Clamp01(r) * 255 + 0.5)
  g = math.floor(Clamp01(g) * 255 + 0.5)
  b = math.floor(Clamp01(b) * 255 + 0.5)
  return string.format("%02x%02x%02x", r, g, b)
end

local function GetAccent()
  local Theme = (WF and WF.UI and WF.UI.Theme) or nil
  local a = (Theme and Theme.accent) or { 0.35, 0.78, 1.00, 1.00 }
  return a
end

-- -------------------------------------------------------
-- TSM API compatibility (TSM4/TSM5/etc)
-- -------------------------------------------------------
local function _WFHasTSMCustomPriceAPI()
  if TSM_API and type(TSM_API.GetCustomPriceValue) == "function" then
    return true
  end
  if TSMAPI_FOUR and TSMAPI_FOUR.CustomPrice and type(TSMAPI_FOUR.CustomPrice.GetValue) == "function" then
    return true
  end
  if TSMAPI and type(TSMAPI.GetCustomPriceValue) == "function" then
    return true
  end
  return false
end

local function _WFGetTSMCustomPriceValue(priceString, itemString)
  if not priceString or not itemString then
    return nil
  end

  -- TSM 5+
  if TSM_API and type(TSM_API.GetCustomPriceValue) == "function" then
    local ok, v = pcall(TSM_API.GetCustomPriceValue, priceString, itemString)
    if ok then
      return v
    end
  end

  -- Alternate globals (some forks)
  if TSMAPI and type(TSMAPI.GetCustomPriceValue) == "function" then
    local ok, v = pcall(TSMAPI.GetCustomPriceValue, priceString, itemString)
    if ok then
      return v
    end
  end

  -- TSM 4
  if TSMAPI_FOUR and TSMAPI_FOUR.CustomPrice and type(TSMAPI_FOUR.CustomPrice.GetValue) == "function" then
    local ok, v = pcall(TSMAPI_FOUR.CustomPrice.GetValue, priceString, itemString)
    if ok then
      return v
    end
    -- Some forks swap arg order; try alternate silently.
    ok, v = pcall(TSMAPI_FOUR.CustomPrice.GetValue, itemString, priceString)
    if ok then
      return v
    end
  end

  return nil
end

-- -------------------------------------------------------
-- Provider selection
-- -------------------------------------------------------
function Price:GetTSMPriceString()
  local s = nil
  if WF.DB and WF.DB.GetSetting then
    s = WF.DB:GetSetting("tsmPriceString")
  end
  s = Trim(s)
  if s == "" then
    s = nil
  end
  return s or DEFAULT_TSM_PRICE
end

function Price:GetProvider()
  local p = nil
  if WF.DB and WF.DB.GetSetting then
    p = WF.DB:GetSetting("priceSource")
  end

  local prov, extra = ParseProvider(p)

  -- Bake the selected TSM string into the provider so repricing reacts to changes.
  if prov == "tsm" then
    extra = extra or self:GetTSMPriceString()
    return "tsm:" .. tostring(extra)
  end

  if prov == "" then
    return "vendor"
  end

  return (extra and (prov .. ":" .. extra)) or prov
end

-- -------------------------------------------------------

-- -------------------------------------------------------
-- Dedicated price-source warning popup
--   Use a custom WhatFarm-styled frame instead of StaticPopup so it
--   renders the same with and without ElvUI.
-- -------------------------------------------------------
local PRICE_DIALOG_FRAME_NAME = "WF_PriceSourceWarningDialog"

local function _WFGetPriceDialogTheme()
  local Theme = (WF and WF.UI and WF.UI.Theme) or nil
  return {
    accent = (Theme and Theme.accent) or { 0.35, 0.78, 1.00, 1.00 },
    text = (Theme and Theme.text) or { 0.95, 0.95, 0.95, 1.00 },
    muted = (Theme and Theme.muted) or { 0.72, 0.72, 0.72, 1.00 },
    border = (Theme and Theme.border) or { 0.22, 0.22, 0.22, 0.92 },
  }
end

local function _WFPriceDialogFont(nameA, nameB)
  if nameA and _G[nameA] then
    return nameA
  end
  if nameB and _G[nameB] then
    return nameB
  end
  return nil
end

local function _WFStylePriceDialogButton(btn)
  if not btn then return end

  local theme = _WFGetPriceDialogTheme()
  local border = theme.border
  local textColor = theme.text
  local muted = theme.muted
  local accent = theme.accent

  btn:SetBackdrop({
    bgFile = "Interface/Buttons/WHITE8x8",
    edgeFile = "Interface/Buttons/WHITE8x8",
    edgeSize = 1,
  })

  local baseText = btn._wfButtonText
  if not baseText or baseText == "" then
    baseText = (btn.GetText and btn:GetText()) or ""
    btn._wfButtonText = baseText
  end

  if btn.SetText then
    btn:SetText("")
  end

  local label = btn._wfLabel
  if not label then
    label = btn:CreateFontString(nil, "OVERLAY", _WFPriceDialogFont("WF_GameFontNormal", "GameFontNormal") or "GameFontNormal")
    label:SetPoint("CENTER", btn, "CENTER", 0, 0)
    if label.SetShadowOffset then label:SetShadowOffset(0, 0) end
    if label.SetShadowColor then label:SetShadowColor(0, 0, 0, 0) end
    btn._wfLabel = label
  end
  label:SetText(baseText or "")

  if btn.SetNormalFontObject then
    btn:SetNormalFontObject(_WFPriceDialogFont("WF_GameFontNormal", "GameFontNormal") or "GameFontNormal")
  end
  if btn.SetHighlightFontObject then
    btn:SetHighlightFontObject(_WFPriceDialogFont("WF_GameFontNormal", "GameFontNormal") or "GameFontNormal")
  end
  if btn.SetDisabledFontObject then
    btn:SetDisabledFontObject(_WFPriceDialogFont("WF_GameFontNormal", "GameFontDisable") or "GameFontDisable")
  end

  if not btn._wfHighlightTex then
    local ht = btn:CreateTexture(nil, "HIGHLIGHT")
    ht:SetTexture("Interface/Buttons/WHITE8x8")
    ht:SetBlendMode("BLEND")
    ht:SetPoint("TOPLEFT", btn, "TOPLEFT", 1, -1)
    ht:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -1, 1)
    btn._wfHighlightTex = ht
  end
  btn._wfHighlightTex:SetVertexColor(accent[1], accent[2], accent[3], 0.10)

  if not btn._wfPushedTex then
    local pt = btn:CreateTexture(nil, "ARTWORK")
    pt:SetTexture("Interface/Buttons/WHITE8x8")
    pt:SetPoint("TOPLEFT", btn, "TOPLEFT", 1, -1)
    pt:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -1, 1)
    btn._wfPushedTex = pt
  end
  btn._wfPushedTex:SetVertexColor(0, 0, 0, 0.00)

  if not btn._wfPriceDialogMouseHooks then
    btn._wfPriceDialogMouseHooks = true
    btn:HookScript("OnMouseDown", function(self)
      if self._wfPushedTex then
        self._wfPushedTex:SetVertexColor(0, 0, 0, 0.18)
      end
    end)
    btn:HookScript("OnMouseUp", function(self)
      if self._wfPushedTex then
        self._wfPushedTex:SetVertexColor(0, 0, 0, 0.00)
      end
    end)
    btn:HookScript("OnHide", function(self)
      if self._wfPushedTex then
        self._wfPushedTex:SetVertexColor(0, 0, 0, 0.00)
      end
    end)
  end

  local function apply(self)
    local enabled = (not self.IsEnabled) or self:IsEnabled()
    if enabled then
      self:SetBackdropColor(0.04, 0.04, 0.04, 0.98)
      self:SetBackdropBorderColor(border[1], border[2], border[3], 0.92)
      if self._wfLabel and self._wfLabel.SetTextColor then
        self._wfLabel:SetTextColor(textColor[1], textColor[2], textColor[3], 1.00)
      end
    else
      self:SetBackdropColor(0.04, 0.04, 0.04, 0.92)
      self:SetBackdropBorderColor(border[1], border[2], border[3], 0.55)
      if self._wfLabel and self._wfLabel.SetTextColor then
        self._wfLabel:SetTextColor(muted[1], muted[2], muted[3], 0.80)
      end
    end
  end

  if not btn._wfPriceDialogHooks then
    btn._wfPriceDialogHooks = true
    btn:HookScript("OnEnable", apply)
    btn:HookScript("OnDisable", apply)
    btn:HookScript("OnShow", apply)
  end

  apply(btn)
end

function Price:_EnsurePriceSourceDialogFrame()
  if self._wfPriceSourceDialog then
    return self._wfPriceSourceDialog
  end

  local theme = _WFGetPriceDialogTheme()
  local accent = theme.accent
  local textColor = theme.text
  local border = theme.border

  local f = CreateFrame("Frame", PRICE_DIALOG_FRAME_NAME, UIParent, "BackdropTemplate")
  f:SetSize(350, 105)
  f:SetFrameStrata("DIALOG")
  f:SetFrameLevel(200)
  f:SetClampedToScreen(true)
  f:SetPoint("TOP", UIParent, "TOP", 0, -140)
  f:Hide()

  f:SetBackdrop({
    bgFile = "Interface/Buttons/WHITE8x8",
    edgeFile = "Interface/Buttons/WHITE8x8",
    edgeSize = 1,
  })
  f:SetBackdropColor(0.03, 0.03, 0.03, 0.98)
  f:SetBackdropBorderColor(border[1], border[2], border[3], 0.94)

  local text = f:CreateFontString(nil, "OVERLAY", _WFPriceDialogFont("WF_GameFontHighlight", "GameFontHighlight") or "GameFontHighlight")
  text:SetPoint("TOPLEFT", f, "TOPLEFT", 18, -16)
  text:SetPoint("TOPRIGHT", f, "TOPRIGHT", -18, -16)
  text:SetJustifyH("CENTER")
  text:SetJustifyV("MIDDLE")
  text:SetSpacing(2)
  text:SetTextColor(textColor[1], textColor[2], textColor[3], 1.00)
  f.text = text

  local buttonRow = CreateFrame("Frame", nil, f)
  buttonRow:SetSize(252, 20)
  buttonRow:SetPoint("BOTTOM", f, "BOTTOM", 0, 14)
  f.buttonRow = buttonRow

  local openBtn = CreateFrame("Button", nil, buttonRow, "BackdropTemplate")
  openBtn:SetSize(118, 20)
  openBtn:SetPoint("LEFT", buttonRow, "LEFT", 0, 0)
  openBtn:SetText("Open Settings")
  _WFStylePriceDialogButton(openBtn)
  f.openButton = openBtn

  local okBtn = CreateFrame("Button", nil, buttonRow, "BackdropTemplate")
  okBtn:SetSize(118, 20)
  okBtn:SetPoint("RIGHT", buttonRow, "RIGHT", 0, 0)
  okBtn:SetText("Okay")
  _WFStylePriceDialogButton(okBtn)
  f.okButton = okBtn

  openBtn:SetScript("OnClick", function()
    f:Hide()
    if WF and WF.UI and WF.UI.Help and WF.UI.Help.Show then
      pcall(WF.UI.Help.Show, WF.UI.Help, "Settings")
    end
  end)

  okBtn:SetScript("OnClick", function()
    f:Hide()
  end)

  f:SetScript("OnShow", function(self)
    _WFStylePriceDialogButton(self.openButton)
    _WFStylePriceDialogButton(self.okButton)
  end)

  local found = false
  if type(UISpecialFrames) == "table" then
    for i = 1, #UISpecialFrames do
      if UISpecialFrames[i] == PRICE_DIALOG_FRAME_NAME then
        found = true
        break
      end
    end
    if not found then
      table.insert(UISpecialFrames, PRICE_DIALOG_FRAME_NAME)
    end
  end

  self._wfPriceSourceDialog = f
  return f
end

function Price:_ShowPriceSourceDialogNow(msg)
  msg = tostring(msg or "")
  if msg == "" then
    return false
  end

  local a = GetAccent()
  local hex = RGBToHex(a[1], a[2], a[3])

  local body = msg
  body = body:gsub("TSM", "|cff" .. hex .. "TSM|r")
  body = body:gsub("Auctionator", "|cff" .. hex .. "Auctionator|r")
  body = body:gsub("Vendor", "|cff" .. hex .. "Vendor|r")
  body = body .. "\n\n|cff" .. hex .. "Fix:|r enable that addon and /reload, or switch to Vendor."

  local dlg = self:_EnsurePriceSourceDialogFrame()
  if not dlg then
    return false
  end

  dlg.text:SetText(body)
  dlg:Show()
  dlg:Raise()
  _WFStylePriceDialogButton(dlg.openButton)
  _WFStylePriceDialogButton(dlg.okButton)
  return true
end

function Price:_QueuePriceSourceDialog(msg)
  msg = tostring(msg or "")
  if msg == "" then return end

  self._wf_queuedDlgMsg = msg
  if self._wf_dlgArmed then
    return
  end
  self._wf_dlgArmed = true

  local function TryShow()
    if not (WF and WF.Price) then
      return
    end

    if InCombatLockdown and InCombatLockdown() then
      if C_Timer and C_Timer.After then
        C_Timer.After(1, TryShow)
      else
        WF.Price._wf_queuedDlgMsg = nil
        WF.Price._wf_dlgArmed = nil
      end
      return
    end

    WF.Price._wf_dlgArmed = nil

    local q = WF.Price._wf_queuedDlgMsg
    WF.Price._wf_queuedDlgMsg = nil
    if not q or q == "" then return end

    WF.Price:_ShowPriceSourceDialogNow(q)
  end

  if C_Timer and C_Timer.After then
    C_Timer.After(0.1, TryShow)
  else
    TryShow()
  end
end

function Price:_NotifyWarn(msg)
  -- Popup only (no UIErrorsFrame / no chat spam).
  self:_QueuePriceSourceDialog(msg)
end
-- Provider availability (LootAppraiser-style)
--   Warn only when addon is missing/disabled/not loaded.
--   Avoid false "not installed" by searching addon list + accepting globals/APIs.
-- -------------------------------------------------------
function Price:IsProviderAvailable(providerStr)
  local prov = providerStr
  local provider = nil
  if type(prov) == "string" then
    provider = (Trim(prov)):lower()
  else
    provider = tostring(prov or ""):lower()
  end

  local p, _ = ParseProvider(provider)
  provider = p

  if provider == "tsm" then
    local hasAPI = _WFHasTSMCustomPriceAPI()
    local name = _WFResolveProviderAddOnName("tsm")

    if not hasAPI and not name then
      return false, "TSM is selected, but TradeSkillMaster isn't installed."
    end

    if name then
      local enabled = _WFIsAddOnEnabled(name)
      if enabled == false then
        return false, "TSM is selected, but TradeSkillMaster is disabled."
      end
    end

    local loaded = hasAPI or (name and _WFIsAddOnLoaded(name))
    if not loaded then
      return false, "TSM is selected, but TradeSkillMaster isn't loaded."
    end

    return true, nil
  end

  if provider == "auctionator" then
    local hasGlobal = (Auctionator ~= nil)
    local name = _WFResolveProviderAddOnName("auctionator")

    if not hasGlobal and not name then
      return false, "Auctionator is selected, but Auctionator isn't installed."
    end

    if name then
      local enabled = _WFIsAddOnEnabled(name)
      if enabled == false then
        return false, "Auctionator is selected, but Auctionator is disabled."
      end
    end

    local loaded = hasGlobal or (name and _WFIsAddOnLoaded(name))
    if not loaded then
      return false, "Auctionator is selected, but Auctionator isn't loaded."
    end

    return true, nil
  end

  return true, nil
end

function Price:GetProviderWarning(providerStr)
  local ok, msg = self:IsProviderAvailable(providerStr)
  if ok then
    return nil
  end
  return msg
end

function Price:WarnIfProviderUnavailable(providerStr, force)
  local provider, _ = ParseProvider(providerStr)

  if provider ~= "tsm" and provider ~= "auctionator" then
    return false
  end

  local ok, msg = self:IsProviderAvailable(providerStr)
  if ok then
    return false
  end

  -- Prevent early false positives: if addon is enabled but not loaded yet, defer once (unless forced).
  if not force then
    if provider == "auctionator" then
      local name = _WFResolveProviderAddOnName("auctionator")
      if name and _WFIsAddOnEnabled(name) ~= false and not (_WFIsAddOnLoaded(name) or (Auctionator ~= nil)) then
        if not self._wf_deferredAu then
          self._wf_deferredAu = true
          if C_Timer and C_Timer.After then
            C_Timer.After(2.0, function()
              Price._wf_deferredAu = nil
              Price:WarnIfProviderUnavailable("auctionator", true)
            end)
          end
        end
        return true
      end
    elseif provider == "tsm" then
      local name = _WFResolveProviderAddOnName("tsm")
      if name and _WFIsAddOnEnabled(name) ~= false and not (_WFIsAddOnLoaded(name) or _WFHasTSMCustomPriceAPI()) then
        if not self._wf_deferredTSM then
          self._wf_deferredTSM = true
          if C_Timer and C_Timer.After then
            C_Timer.After(2.0, function()
              Price._wf_deferredTSM = nil
              Price:WarnIfProviderUnavailable("tsm", true)
            end)
          end
        end
        return true
      end
    end
  end

  self._wf_warned = self._wf_warned or {}
  self._wf_warnThrottleAt = self._wf_warnThrottleAt or 0

  local now = _WFNow()
  if (now - self._wf_warnThrottleAt) < 1.0 then
    return true
  end

  if not force and self._wf_warned[provider] then
    return true
  end

  self._wf_warnThrottleAt = now
  self._wf_warned[provider] = true

  self:_NotifyWarn(msg)
  return true
end

function Price:WarnIfConfiguredProviderUnavailable(force)
  local raw = (WF and WF.DB and WF.DB.GetSetting) and (WF.DB:GetSetting("priceSource") or "vendor") or "vendor"
  return self:WarnIfProviderUnavailable(raw, force)
end

-- -------------------------------------------------------
-- Pricing
-- -------------------------------------------------------
local function GetTSMValue(itemID, priceString)
  if not _WFHasTSMCustomPriceAPI() then
    return nil
  end

  if not itemID then
    return nil
  end

  priceString = Trim(priceString)
  if priceString == "" then
    priceString = DEFAULT_TSM_PRICE
  end

  local itemString = "i:" .. itemID

  local v = _WFGetTSMCustomPriceValue(priceString, itemString)

  -- Fallbacks so a bad/unsupported string doesn't break pricing.
  if v == nil and priceString ~= "dbmarket" then
    v = _WFGetTSMCustomPriceValue("dbmarket", itemString)
  end
  if v == nil and priceString ~= DEFAULT_TSM_PRICE and DEFAULT_TSM_PRICE ~= "dbmarket" then
    v = _WFGetTSMCustomPriceValue(DEFAULT_TSM_PRICE, itemString)
  end

  if v ~= nil then
    return tonumber(v) or 0
  end

  return nil
end

function Price:GetTSMMarketMeta(itemID)
  if not _WFHasTSMCustomPriceAPI() then
    return nil
  end
  itemID = tonumber(itemID)
  if not itemID then return nil end
  local itemString = "i:" .. itemID

  local minBuyout = _WFGetTSMCustomPriceValue("dbminbuyout", itemString)
  local saleRate  = _WFGetTSMCustomPriceValue("dbregionsalerate", itemString)

  return {
    minBuyout = tonumber(minBuyout) or 0,
    saleRate  = tonumber(saleRate) or 0,
  }
end

function Price:IsTSMUnverified(itemID, unitValue)
  local provider = self:GetProvider() or ""
  if not provider:match("^tsm:") then
    return false, nil
  end

  if WF.DB and WF.DB.GetSetting then
    if not WF.DB:GetSetting("lootFlagUnverifiedTSM") then
      return false, nil
    end
  end

  local meta = self:GetTSMMarketMeta(itemID)
  if not meta then
    return false, nil
  end

  local threshold = 0
  if WF.DB and WF.DB.GetSetting then
    threshold = tonumber(WF.DB:GetSetting("lootUnverifiedMinUnit")) or 0
  end

  local v = tonumber(unitValue) or 0
  if threshold > 0 and v < threshold then
    return false, meta
  end

  -- No postings + no sale history => unverified pricing.
  if (tonumber(meta.minBuyout) or 0) <= 0 and (tonumber(meta.saleRate) or 0) <= 0 then
    return true, meta
  end

  return false, meta
end

function Price:GetItemValue(itemID, itemLink)
  itemID = tonumber(itemID)
  if not itemID then
    return 0
  end

  local provStr = self:GetProvider()

  -- Warn once per session when the configured provider addon is missing/disabled.
  if self._wf_lastProvChecked ~= provStr then
    self._wf_lastProvChecked = provStr
    local raw = (WF and WF.DB and WF.DB.GetSetting) and (WF.DB:GetSetting("priceSource") or "vendor") or "vendor"
    self:WarnIfProviderUnavailable(raw, false)
  end

  local provider, extra = ParseProvider(provStr)

  if provider == "tsm" then
    local priceString = extra or self:GetTSMPriceString()
    local v = GetTSMValue(itemID, priceString)
    if v ~= nil then
      return v
    end
  end

  if provider == "auctionator" then
    if Auctionator and Auctionator.API and Auctionator.API.v1 and Auctionator.API.v1.GetAuctionPriceByItemID then
      local ok, price = pcall(Auctionator.API.v1.GetAuctionPriceByItemID, ADDON_NAME, itemID)
      if ok and price then
        return tonumber(price) or 0
      end
    end
  end

  -- Vendor fallback.
  local infoKey = itemLink or itemID
  local sellPrice = select(11, GetItemInfo(infoKey))

  if sellPrice == nil and itemID and C_Item and C_Item.RequestLoadItemDataByID then
    C_Item.RequestLoadItemDataByID(itemID)
  end

  if sellPrice then
    return tonumber(sellPrice) or 0
  end

  return 0
end
