-- LidCouncil_Compat.lua
-- Minimal compatibility surface. Keep only live helpers that still abstract
-- real runtime behavior differences for the addon.

_G.LIDCOUNCIL_Compat = _G.LIDCOUNCIL_Compat or {}
local C = _G.LIDCOUNCIL_Compat
local timerStats = {
  attemptsTotal = 0,
  nativeScheduledTotal = 0,
  nativeFailureTotal = 0,
  nativeUnavailableTotal = 0,
  frameFallbackTotal = 0,
  frameFailureTotal = 0,
  immediateFallbackTotal = 0,
  callbackInvalidTotal = 0,
  lastAt = 0,
  lastDelay = 0,
  lastReason = "",
  lastFallbackAt = 0,
  lastFallbackDelay = 0,
  lastFallbackReason = "",
  lastNativeError = "",
}

local function GetTimerTime()
  if type(GetTime) == "function" then
    return math.max(0, tonumber(GetTime()) or 0)
  end
  return math.max(0, tonumber(time and time() or 0) or 0)
end

local function BoundText(value, maxLength)
  local text = tostring(value or "")
  local limit = math.max(1, tonumber(maxLength) or 96)
  if #text > limit then
    return string.sub(text, 1, limit)
  end
  return text
end

local function RecordTimerOutcome(delay, reason)
  timerStats.lastAt = GetTimerTime()
  timerStats.lastDelay = math.max(0, tonumber(delay) or 0)
  timerStats.lastReason = BoundText(reason, 96)
end

local function RecordTimerFallback(delay, reason)
  RecordTimerOutcome(delay, reason)
  timerStats.lastFallbackAt = timerStats.lastAt
  timerStats.lastFallbackDelay = timerStats.lastDelay
  timerStats.lastFallbackReason = timerStats.lastReason
end

local function ScheduleOnUpdate(delay, callback)
  if type(CreateFrame) ~= "function" then
    return false, "frame_unavailable"
  end
  local created, frame = pcall(CreateFrame, "Frame")
  if not created or frame == nil or type(frame.SetScript) ~= "function" then
    return false, "frame_error"
  end

  local elapsedTotal = 0
  local scriptSet = pcall(frame.SetScript, frame, "OnUpdate", function(self, elapsed)
    elapsedTotal = elapsedTotal + math.max(0, tonumber(elapsed) or 0)
    if elapsedTotal >= delay then
      self:SetScript("OnUpdate", nil)
      self:Hide()
      callback()
    end
  end)
  if not scriptSet then
    return false, "frame_script_error"
  end
  if type(frame.Show) == "function" then
    frame:Show()
  end
  return true, "frame_fallback"
end

function C.After(delay, callback)
  timerStats.attemptsTotal = math.max(0, tonumber(timerStats.attemptsTotal) or 0) + 1
  if type(callback) ~= "function" then
    timerStats.callbackInvalidTotal = math.max(0, tonumber(timerStats.callbackInvalidTotal) or 0) + 1
    RecordTimerOutcome(delay, "callback_invalid")
    return false, "callback_invalid"
  end

  local resolvedDelay = math.max(0, tonumber(delay) or 0)
  local timerAfter = type(C_Timer) == "table" and C_Timer.After or nil
  if type(timerAfter) == "function" then
    local scheduled, timerError = pcall(timerAfter, resolvedDelay, callback)
    if scheduled then
      timerStats.nativeScheduledTotal = math.max(0, tonumber(timerStats.nativeScheduledTotal) or 0) + 1
      RecordTimerOutcome(resolvedDelay, "scheduled")
      return true, "scheduled"
    end
    timerStats.nativeFailureTotal = math.max(0, tonumber(timerStats.nativeFailureTotal) or 0) + 1
    timerStats.lastNativeError = BoundText(timerError, 160)
  else
    timerStats.nativeUnavailableTotal = math.max(0, tonumber(timerStats.nativeUnavailableTotal) or 0) + 1
  end

  local fallbackScheduled, fallbackReason = ScheduleOnUpdate(resolvedDelay, callback)
  if fallbackScheduled then
    timerStats.frameFallbackTotal = math.max(0, tonumber(timerStats.frameFallbackTotal) or 0) + 1
    RecordTimerFallback(resolvedDelay, fallbackReason)
    return true, fallbackReason
  end

  timerStats.frameFailureTotal = math.max(0, tonumber(timerStats.frameFailureTotal) or 0) + 1
  timerStats.immediateFallbackTotal = math.max(0, tonumber(timerStats.immediateFallbackTotal) or 0) + 1
  local immediateReason = "immediate_fallback:" .. tostring(fallbackReason)
  RecordTimerFallback(resolvedDelay, immediateReason)
  callback()
  return true, immediateReason
end

function C.GetTimerStatus()
  local frameFallbackTotal = math.max(0, tonumber(timerStats.frameFallbackTotal) or 0)
  local immediateFallbackTotal = math.max(0, tonumber(timerStats.immediateFallbackTotal) or 0)
  return {
    attemptsTotal = math.max(0, tonumber(timerStats.attemptsTotal) or 0),
    nativeScheduledTotal = math.max(0, tonumber(timerStats.nativeScheduledTotal) or 0),
    nativeFailureTotal = math.max(0, tonumber(timerStats.nativeFailureTotal) or 0),
    nativeUnavailableTotal = math.max(0, tonumber(timerStats.nativeUnavailableTotal) or 0),
    fallbackTotal = frameFallbackTotal + immediateFallbackTotal,
    frameFallbackTotal = frameFallbackTotal,
    frameFailureTotal = math.max(0, tonumber(timerStats.frameFailureTotal) or 0),
    immediateFallbackTotal = immediateFallbackTotal,
    callbackInvalidTotal = math.max(0, tonumber(timerStats.callbackInvalidTotal) or 0),
    lastAt = math.max(0, tonumber(timerStats.lastAt) or 0),
    lastDelay = math.max(0, tonumber(timerStats.lastDelay) or 0),
    lastReason = BoundText(timerStats.lastReason, 96),
    lastFallbackAt = math.max(0, tonumber(timerStats.lastFallbackAt) or 0),
    lastFallbackDelay = math.max(0, tonumber(timerStats.lastFallbackDelay) or 0),
    lastFallbackReason = BoundText(timerStats.lastFallbackReason, 96),
    lastNativeError = BoundText(timerStats.lastNativeError, 160),
  }
end
