--[[--------------------------------------------------------------------
  PaladinHolyPower - modules/shared_media_support.lua
  LibSharedMedia integration (safe & simple, one place).
  - Lazy-inits via LibStub("LibSharedMedia-3.0", true)
  - Queues registrations until LSM is ready
  - Helper APIs:
      M:IsReady()
      M:List(kind) -> { "Name1", "Name2", ... }        -- unsorted copy
      M:ListSorted(kind) -> sorted copy
      M:Fetch(kind, name) -> file path or nil          -- raw fetch
      M:GetFontPath(name) -> path or default FRIZ
      M:GetSoundPath(nameOrPath) -> path or nil        -- accepts raw paths
      M:PlaySound(nameOrPath, channel) -> true/false   -- "Master" default
      M:Register(kind, name, path)                     -- generic
      M:RegisterSound(name, path)
      M:RegisterFont(name, path)
      M:AddChangeListener(fn)                          -- notified on ready or new media
----------------------------------------------------------------------]]

local PHP = _G.PaladinHolyPower
if not PHP then return end

local M = {}
PHP.SharedMedia = M

----------------------------------------------------------------------
-- Internals
----------------------------------------------------------------------
local LibSM         = nil
local ready         = false
local listeners     = {}          -- { fn, ... }  notified on ready or new media
local q_register    = {}          -- queued items until ready: { {kind=, name=, path=} , ... }

local function safe(fn, ...)
  if fn then
    local ok = pcall(fn, ...)
    return ok
  end
  return false
end

local function TryReady()
  if ready then return true end
  local LS = _G.LibStub
  if not LS then return false end
  LibSM = LibSM or LS("LibSharedMedia-3.0", true)
  if not LibSM then return false end

  -- Hook callbacks if available (LSM uses CallbackHandler)
  if type(LibSM.RegisterCallback) == "function" and not M._cbRegistered then
    -- Fires when new media is registered: args (event, mediatype, key)
    safe(LibSM.RegisterCallback, LibSM, "LibSharedMedia_Registered", function(_, mediatype, key)
      -- Notify listeners about the new media type/name
      for i = 1, #listeners do
        local fn = listeners[i]
        if type(fn) == "function" then pcall(fn, "registered", mediatype, key) end
      end
    end)
    M._cbRegistered = true
  end

  ready = true
  return true
end

local function flushQueued()
  if not (ready and LibSM) then return end
  if #q_register > 0 then
    for i = 1, #q_register do
      local it = q_register[i]
      if it and it.kind and it.name and it.path then
        safe(LibSM.Register, LibSM, it.kind, it.name, it.path)
      end
    end
    wipe(q_register)
  end
  -- Tell listeners we're ready
  for i = 1, #listeners do
    local fn = listeners[i]
    if type(fn) == "function" then pcall(fn, "ready") end
  end
end

-- Become ready when the core loads
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("ADDON_LOADED")
f:SetScript("OnEvent", function()
  if TryReady() then
    flushQueued()
  end
end)

----------------------------------------------------------------------
-- Public API
----------------------------------------------------------------------

function M:IsReady()
  return TryReady()
end

-- Return a *copy* of the list to avoid accidental modifications
function M:List(kind)
  if not TryReady() or not LibSM then return {} end
  local out = {}
  local list = LibSM:List(kind) or {}
  for i = 1, #list do out[i] = list[i] end
  return out
end

function M:ListSorted(kind)
  local t = self:List(kind)
  table.sort(t, function(a,b)
    a = tostring(a or ""):lower()
    b = tostring(b or ""):lower()
    return a < b
  end)
  return t
end

function M:Fetch(kind, name)
  if not name then return nil end
  if TryReady() and LibSM then
    -- 3rd arg 'true' = strict, returns nil when unknown (no fallback)
    local path = LibSM:Fetch(kind, name, true)
    if path then return path end
  end
  return nil
end

-- Fonts: try LSM first, then any bundled PHP.fonts entries, then Blizzard default
function M:GetFontPath(name)
  -- Prefer an explicit font via LSM
  local p = self:Fetch("font", name)
  if p then return p end

  -- Fallback to bundled list if provided by the addon
  local list = PHP.fonts or {}
  for i = 1, #list do
    local it = list[i]
    if it and it.name == name and it.path then
      return it.path
    end
  end

  -- Last resort
  return "Fonts\\FRIZQT__.TTF"
end

-- Sounds: accept either an LSM label or a raw file path; returns a playable path or nil
function M:GetSoundPath(nameOrPath)
  if not nameOrPath or nameOrPath == "" then return nil end

  -- If it looks like a path, trust the caller (e.g., Interface\\AddOns\\...)
  if type(nameOrPath) == "string" and nameOrPath:find("Interface\\") then
    return nameOrPath
  end

  -- Otherwise, request it by LSM label
  local p = self:Fetch("sound", nameOrPath)
  if p then return p end

  return nil
end

-- Convenience: play a named/path sound on a channel (default "Master")
function M:PlaySound(nameOrPath, channel)
  local path = self:GetSoundPath(nameOrPath)
  if not path then return false end
  channel = channel or "Master"
  return safe(_G.PlaySoundFile, path, channel)
end

-- Generic register; if LSM not ready, queue
function M:Register(kind, name, path)
  if not (kind and name and path) then return end
  if TryReady() and LibSM then
    safe(LibSM.Register, LibSM, kind, name, path)
    -- Notify listeners that new media is available
    for i = 1, #listeners do
      local fn = listeners[i]
      if type(fn) == "function" then pcall(fn, "registered", kind, name) end
    end
  else
    q_register[#q_register + 1] = { kind = kind, name = name, path = path }
  end
end

function M:RegisterSound(name, path)
  self:Register("sound", name, path)
end

function M:RegisterFont(name, path)
  self:Register("font", name, path)
end

-- Subscribe to changes: fn(event, kind?, name?)
--   event = "ready" when LSM becomes available,
--            "registered" when new media is registered
function M:AddChangeListener(fn)
  if type(fn) ~= "function" then return end
  listeners[#listeners + 1] = fn
  -- If already ready, fire once so UI can populate immediately
  if ready then pcall(fn, "ready") end
end

return M