defmodule Blogex.Registry do
  @moduledoc """
  Registry that tracks all blog modules in the host application.

  Configure in your app's config:

      config :blogex,
        blogs: [MyApp.EngineeringBlog, MyApp.ReleaseNotes]

  Then you can query across all blogs:

      Blogex.Registry.all_posts()       # posts from all blogs, sorted by date
      Blogex.Registry.blogs()           # list of blog modules
      Blogex.Registry.get_blog!(:engineering)  # get a specific blog module
  """

  @doc "Returns the list of configured blog modules."
  def blogs do
    Application.get_env(:blogex, :blogs, [])
  end

  @doc "Returns a blog module by its blog_id, or raises."
  def get_blog!(blog_id) do
    Enum.find(blogs(), fn mod -> mod.blog_id() == blog_id end) ||
      raise Blogex.NotFoundError, "blog #{inspect(blog_id)} not found"
  end

  @doc "Returns a blog module by its blog_id, or nil."
  def get_blog(blog_id) do
    Enum.find(blogs(), fn mod -> mod.blog_id() == blog_id end)
  end

  @doc "Returns all posts from all blogs, sorted newest first."
  def all_posts do
    blogs()
    |> Enum.flat_map(& &1.all_posts())
    |> Enum.sort_by(& &1.date, {:desc, Date})
  end

  @doc "Returns all posts from all blogs (unfiltered), sorted newest first."
  def all_posts_unfiltered do
    blogs()
    |> Enum.flat_map(& &1.unfiltered_posts())
    |> Enum.sort_by(& &1.date, {:desc, Date})
  end

  @doc "Returns all unique tags across all blogs."
  def all_tags do
    blogs()
    |> Enum.flat_map(& &1.all_tags())
    |> Enum.uniq()
    |> Enum.sort()
  end

  @doc "Returns a map of %{blog_id => blog_module} for all registered blogs."
  def blogs_map do
    Map.new(blogs(), fn mod -> {mod.blog_id(), mod} end)
  end
end
