defmodule Blogex.RouterTest do
  use ExUnit.Case
  use Plug.Test

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

  setup do
    posts = [
      build(id: "first-post", title: "First", tags: ["elixir"], date: ~D[2026-03-10]),
      build(id: "second-post", title: "Second", tags: ["otp"], date: ~D[2026-02-01]),
      build(id: "draft", published: false, date: ~D[2026-03-12]),
      build(
        id: "future-post",
        title: "Future",
        tags: ["elixir"],
        date: ~D[2099-01-01],
        published: true
      )
    ]

    {:ok, _} =
      FakeBlog.start(posts,
        blog_id: :test,
        title: "Test Blog",
        description: "Test",
        base_path: "/blog/test"
      )

    :ok
  end

  defp call(method, path) do
    conn(method, path)
    |> Blogex.Router.call(Blogex.Router.init(blog: FakeBlog))
  end

  describe "GET /feed.xml" do
    test "returns RSS XML" do
      conn = call(:get, "/feed.xml")

      assert conn.status == 200
      assert get_content_type(conn) =~ "application/rss+xml"
      assert conn.resp_body =~ "<rss version=\"2.0\""
    end

    test "includes published posts" do
      conn = call(:get, "/feed.xml")

      assert conn.resp_body =~ "first-post"
      refute conn.resp_body =~ "draft"
    end

    test "excludes future-dated posts from feed" do
      conn = call(:get, "/feed.xml")

      refute conn.resp_body =~ "future-post"
    end
  end

  describe "GET /atom.xml" do
    test "returns Atom XML" do
      conn = call(:get, "/atom.xml")

      assert conn.status == 200
      assert get_content_type(conn) =~ "application/atom+xml"
      assert conn.resp_body =~ "<feed xmlns="
    end
  end

  describe "GET /:slug" do
    test "returns post as JSON" do
      conn = call(:get, "/first-post")

      assert conn.status == 200
      body = Jason.decode!(conn.resp_body)
      assert body["id"] == "first-post"
      assert body["title"] == "First"
    end

    test "returns 404 for unknown slug" do
      conn = call(:get, "/nonexistent")

      assert conn.status == 404
    end

    test "returns 200 for draft post accessed by slug" do
      conn = call(:get, "/draft")

      assert conn.status == 200
    end

    test "returns 200 for future-dated post accessed by slug" do
      conn = call(:get, "/future-post")

      assert conn.status == 200
      body = Jason.decode!(conn.resp_body)
      assert body["id"] == "future-post"
    end
  end

  describe "GET /tag/:tag" do
    test "returns posts matching tag" do
      conn = call(:get, "/tag/elixir")

      assert conn.status == 200
      body = Jason.decode!(conn.resp_body)
      assert body["tag"] == "elixir"
      assert length(body["posts"]) == 1
      assert hd(body["posts"])["id"] == "first-post"
    end

    test "excludes future-dated posts from tag results" do
      conn = call(:get, "/tag/elixir")

      body = Jason.decode!(conn.resp_body)
      ids = Enum.map(body["posts"], & &1["id"])
      refute "future-post" in ids
    end

    test "returns empty list for unknown tag" do
      conn = call(:get, "/tag/unknown")

      body = Jason.decode!(conn.resp_body)
      assert body["posts"] == []
    end
  end

  describe "GET /" do
    test "returns paginated post list" do
      conn = call(:get, "/")

      assert conn.status == 200
      body = Jason.decode!(conn.resp_body)
      assert is_list(body["posts"])
      assert body["total_entries"] == 2
    end

    test "excludes draft posts from listing" do
      conn = call(:get, "/")

      body = Jason.decode!(conn.resp_body)
      ids = Enum.map(body["posts"], & &1["id"])
      refute "draft" in ids
    end

    test "excludes future-dated posts from listing" do
      conn = call(:get, "/")

      body = Jason.decode!(conn.resp_body)
      ids = Enum.map(body["posts"], & &1["id"])
      refute "future-post" in ids
    end
  end

  defp get_content_type(conn) do
    conn
    |> Plug.Conn.get_resp_header("content-type")
    |> List.first("")
  end
end
