defmodule Blogex.RegistryTest do
  use ExUnit.Case

  import Blogex.Test.PostBuilder
  alias Blogex.Registry

  defmodule AlphaBlog do
    def blog_id, do: :alpha

    def all_posts,
      do: [Blogex.Test.PostBuilder.build(id: "a1", date: ~D[2026-03-01], blog: :alpha)]

    def all_tags, do: ["elixir"]

    def unfiltered_posts,
      do: [
        Blogex.Test.PostBuilder.build(id: "a1", date: ~D[2026-03-01], blog: :alpha),
        Blogex.Test.PostBuilder.build(
          id: "a-draft",
          date: ~D[2026-03-05],
          blog: :alpha,
          published: false
        )
      ]
  end

  defmodule BetaBlog do
    def blog_id, do: :beta

    def all_posts,
      do: [Blogex.Test.PostBuilder.build(id: "b1", date: ~D[2026-03-15], blog: :beta)]

    def all_tags, do: ["devops"]

    def unfiltered_posts,
      do: [
        Blogex.Test.PostBuilder.build(id: "b1", date: ~D[2026-03-15], blog: :beta),
        Blogex.Test.PostBuilder.build(id: "b-future", date: ~D[2099-01-01], blog: :beta)
      ]
  end

  setup do
    Application.put_env(:blogex, :blogs, [AlphaBlog, BetaBlog])
    on_exit(fn -> Application.delete_env(:blogex, :blogs) end)
  end

  describe "blogs/0" do
    test "returns configured blog modules" do
      assert Registry.blogs() == [AlphaBlog, BetaBlog]
    end

    test "returns empty list when unconfigured" do
      Application.delete_env(:blogex, :blogs)

      assert Registry.blogs() == []
    end
  end

  describe "get_blog!/1" do
    test "returns module by blog_id" do
      assert Registry.get_blog!(:alpha) == AlphaBlog
    end

    test "raises for unknown blog_id" do
      assert_raise Blogex.NotFoundError, fn ->
        Registry.get_blog!(:nonexistent)
      end
    end
  end

  describe "get_blog/1" do
    test "returns nil for unknown blog_id" do
      assert Registry.get_blog(:nonexistent) == nil
    end
  end

  describe "all_posts/0" do
    test "merges posts from all blogs" do
      ids = Registry.all_posts() |> Enum.map(& &1.id)

      assert "a1" in ids
      assert "b1" in ids
    end

    test "sorts merged posts newest first" do
      [first, second] = Registry.all_posts()

      assert first.id == "b1"
      assert second.id == "a1"
    end
  end

  describe "all_tags/0" do
    test "merges and deduplicates tags from all blogs" do
      tags = Registry.all_tags()

      assert "elixir" in tags
      assert "devops" in tags
      assert length(tags) == length(Enum.uniq(tags))
    end
  end

  describe "all_posts_unfiltered/0" do
    test "returns all posts including drafts and future-dated" do
      ids = Registry.all_posts_unfiltered() |> Enum.map(& &1.id)
      assert "a1" in ids
      assert "a-draft" in ids
      assert "b1" in ids
      assert "b-future" in ids
    end

    test "sorts by date descending" do
      dates = Registry.all_posts_unfiltered() |> Enum.map(& &1.date)
      assert dates == Enum.sort(dates, {:desc, Date})
    end
  end

  describe "blogs_map/0" do
    test "returns map keyed by blog_id" do
      map = Registry.blogs_map()

      assert map[:alpha] == AlphaBlog
      assert map[:beta] == BetaBlog
    end
  end
end
