defmodule Blogex.FeedTest do
  use ExUnit.Case

  import Blogex.Test.PostBuilder
  alias Blogex.Test.FakeBlog
  alias Blogex.Feed

  @base_url "https://example.com"

  setup do
    Blogex.Test.Setup.with_blog(
      %{},
      blog_id: :eng,
      title: "Eng Blog",
      description: "Tech articles",
      base_path: "/blog/eng"
    )
  end

  describe "rss/3" do
    test "produces valid RSS 2.0 XML", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ ~s(<?xml version="1.0")
      assert xml =~ ~s(<rss version="2.0")
      assert xml =~ ~s(</rss>)
    end

    test "includes blog title and description", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ "<title>Eng Blog</title>"
      assert xml =~ "<description>Tech articles</description>"
    end

    test "includes post entries with correct links", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ "<link>https://example.com/blog/eng/newest-post</link>"
    end

    test "wraps post body in CDATA", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ "<content:encoded><![CDATA["
    end

    test "includes post tags as categories", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ "<category>elixir</category>"
    end

    test "respects limit option" do
      {:ok, _} = FakeBlog.start(build_many(10))
      xml = Feed.rss(FakeBlog, @base_url, limit: 3)

      item_count = xml |> String.split("<item>") |> length() |> Kernel.-(1)
      assert item_count == 3
    end

    test "excludes drafts", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      refute xml =~ "draft-post"
    end

    test "excludes future-dated published posts", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)
      refute xml =~ "future-post"
    end

    test "includes self-referencing atom:link", %{blog: blog} do
      xml = Feed.rss(blog, @base_url)

      assert xml =~ ~s(href="https://example.com/blog/eng/feed.xml")
      assert xml =~ ~s(rel="self")
    end
  end

  describe "atom/3" do
    test "produces valid Atom XML", %{blog: blog} do
      xml = Feed.atom(blog, @base_url)

      assert xml =~ ~s(<feed xmlns="http://www.w3.org/2005/Atom">)
      assert xml =~ ~s(</feed>)
    end

    test "includes post entries", %{blog: blog} do
      xml = Feed.atom(blog, @base_url)

      assert xml =~ "<entry>"
      assert xml =~ ~s(href="https://example.com/blog/eng/newest-post")
    end

    test "respects limit option" do
      {:ok, _} = FakeBlog.start(build_many(10))
      xml = Feed.atom(FakeBlog, @base_url, limit: 2)

      entry_count = xml |> String.split("<entry>") |> length() |> Kernel.-(1)
      assert entry_count == 2
    end

    test "excludes future-dated published posts", %{blog: blog} do
      xml = Feed.atom(blog, @base_url)
      refute xml =~ "future-post"
    end
  end

  describe "XML escaping" do
    test "escapes special characters in titles" do
      {:ok, _} =
        FakeBlog.start(
          [build(title: "Foo & Bar <Baz>")],
          title: "A & B"
        )

      xml = Feed.rss(FakeBlog, @base_url)

      assert xml =~ "Foo &amp; Bar &lt;Baz&gt;"
      assert xml =~ "<title>A &amp; B</title>"
    end
  end

  describe "empty blog" do
    test "rss produces valid XML with no items" do
      {:ok, _} = FakeBlog.start([])

      xml = Feed.rss(FakeBlog, @base_url)

      assert xml =~ "<channel>"
      refute xml =~ "<item>"
    end

    test "atom produces valid XML with no entries" do
      {:ok, _} = FakeBlog.start([])

      xml = Feed.atom(FakeBlog, @base_url)

      assert xml =~ "<feed"
      refute xml =~ "<entry>"
    end
  end
end
