defmodule MvWeb.Plugs.CheckPagePermissionTest do
  @moduledoc """
  Tests for the CheckPagePermission plug.
  """
  use MvWeb.ConnCase, async: true

  alias MvWeb.Plugs.CheckPagePermission
  alias Mv.Fixtures

  defp conn_with_user(path, user) do
    build_conn(:get, path)
    |> Phoenix.ConnTest.init_test_session(%{})
    |> Plug.Conn.put_private(:phoenix_router, MvWeb.Router)
    |> Plug.Conn.assign(:current_user, user)
  end

  defp conn_without_user(path) do
    build_conn(:get, path)
    |> Phoenix.ConnTest.init_test_session(%{})
    |> Plug.Conn.put_private(:phoenix_router, MvWeb.Router)
  end

  describe "static routes" do
    test "user with permission for \"/members\" can access (conn not halted)" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/members", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "user without permission for \"/members\" is denied (conn halted, redirected to user profile)" do
      user = Fixtures.user_with_role_fixture("own_data")
      conn = conn_with_user("/members", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    test "flash error message present after denial" do
      user = Fixtures.user_with_role_fixture("own_data")
      conn = conn_with_user("/members", user) |> CheckPagePermission.call([])

      assert Phoenix.Flash.get(conn.assigns[:flash] || %{}, :error) ==
               "You don't have permission to access this page."
    end
  end

  describe "dynamic routes" do
    test "user with \"/members/:id\" permission can access \"/members/123\"" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/members/123", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "user with \"/members/:id/edit\" permission can access \"/members/456/edit\"" do
      user = Fixtures.user_with_role_fixture("normal_user")
      conn = conn_with_user("/members/456/edit", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "user with only \"/members/:id\" cannot access \"/members/123/edit\"" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/members/123/edit", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    test "own_data user with linked member can access /members/:id/edit (plug direct call)" do
      member = Fixtures.member_fixture()
      user = Fixtures.user_with_role_fixture("own_data")
      user_with_member = Mv.Authorization.Actor.ensure_loaded(user)
      # Simulate user with linked member (struct may not have member_id after session load)
      user_with_member = %{user_with_member | member_id: member.id}

      assert CheckPagePermission.user_can_access_page?(
               user_with_member,
               "/members/#{member.id}/edit"
             ),
             "plug must allow own_data user with linked member to access member edit"

      conn =
        conn_with_user("/members/#{member.id}/edit", user_with_member)
        |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "own_data user with linked member can access /members/:id/show/edit (plug direct call)" do
      member = Fixtures.member_fixture()
      user = Fixtures.user_with_role_fixture("own_data")
      user_with_member = Mv.Authorization.Actor.ensure_loaded(user)
      user_with_member = %{user_with_member | member_id: member.id}

      assert CheckPagePermission.user_can_access_page?(
               user_with_member,
               "/members/#{member.id}/show/edit"
             )

      conn =
        conn_with_user("/members/#{member.id}/show/edit", user_with_member)
        |> CheckPagePermission.call([])

      refute conn.halted
    end
  end

  describe "statistics route /statistics" do
    test "read_only can access /statistics" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/statistics", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "normal_user can access /statistics" do
      user = Fixtures.user_with_role_fixture("normal_user")
      conn = conn_with_user("/statistics", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "admin can access /statistics" do
      user = Fixtures.user_with_role_fixture("admin")
      conn = conn_with_user("/statistics", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "own_data cannot access /statistics" do
      user = Fixtures.user_with_role_fixture("own_data")
      conn = conn_with_user("/statistics", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  describe "read_only and normal_user denied on admin routes" do
    test "read_only cannot access /admin/roles" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/admin/roles", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    test "normal_user cannot access /admin/roles" do
      user = Fixtures.user_with_role_fixture("normal_user")
      conn = conn_with_user("/admin/roles", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    test "read_only cannot access /members/new" do
      user = Fixtures.user_with_role_fixture("read_only")
      conn = conn_with_user("/members/new", user) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  describe "wildcard" do
    test "admin with \"*\" permission can access any page" do
      user = Fixtures.user_with_role_fixture("admin")
      conn = conn_with_user("/admin/roles", user) |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "admin can access \"/members/999/edit\"" do
      user = Fixtures.user_with_role_fixture("admin")
      conn = conn_with_user("/members/999/edit", user) |> CheckPagePermission.call([])

      refute conn.halted
    end
  end

  describe "unauthenticated user" do
    test "nil current_user is denied and redirected to \"/sign-in\"" do
      conn = conn_without_user("/members") |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/sign-in"

      assert Phoenix.Flash.get(conn.assigns[:flash] || %{}, :error) ==
               "You don't have permission to access this page."
    end
  end

  describe "public paths" do
    test "unauthenticated user can access /auth/sign-in (no redirect)" do
      conn = conn_without_user("/auth/sign-in") |> CheckPagePermission.call([])

      refute conn.halted
    end

    test "unauthenticated user can access /register" do
      conn = conn_without_user("/register") |> CheckPagePermission.call([])

      refute conn.halted
    end
  end

  describe "error handling" do
    test "user with no role is denied" do
      user = Fixtures.user_with_role_fixture("admin")
      user_without_role = %{user | role: nil}
      conn = conn_with_user("/members", user_without_role) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    test "user with invalid permission_set_name is denied" do
      user = Fixtures.user_with_role_fixture("admin")
      bad_role = %{user.role | permission_set_name: "invalid_set"}
      user_bad_role = %{user | role: bad_role}
      conn = conn_with_user("/members", user_bad_role) |> CheckPagePermission.call([])

      assert conn.halted
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  # Integration: dispatch through full router (endpoint) so pipeline and load_from_session run.
  # These tests ensure a Mitglied (own_data) user is denied on every forbidden path.
  describe "integration: Mitglied (own_data) denied on all forbidden paths via full router" do
    @tag role: :member
    test "GET /members redirects to user profile with error flash", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/members")
      assert redirected_to(conn) == "/users/#{user.id}"

      assert Phoenix.Flash.get(conn.assigns[:flash] || %{}, :error) =~
               "don't have permission"
    end

    @tag role: :member
    test "GET /members/new redirects to user profile", %{conn: conn, current_user: user} do
      assert user.role.permission_set_name == "own_data",
             "setup must provide Mitglied (own_data) user"

      conn = get(conn, "/members/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /settings redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /membership_fee_settings redirects to user profile", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/membership_fee_settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /membership_fee_settings/new_fee_type redirects to user profile", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/membership_fee_settings/new_fee_type")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /groups redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/groups")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /groups/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/groups/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /admin/roles redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/admin/roles")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /admin/roles/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/admin/roles/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  # Dynamic routes need a valid path segment; use a real UUID from fixtures.
  describe "integration: Mitglied denied on dynamic forbidden paths via full router" do
    setup %{conn: conn, current_user: current_user} do
      member = Mv.Fixtures.member_fixture()
      role = Mv.Fixtures.role_fixture("admin")
      group = Mv.Fixtures.group_fixture()

      {:ok,
       conn: conn,
       current_user: current_user,
       member_id: member.id,
       role_id: role.id,
       group_slug: group.slug}
    end

    @tag role: :member
    test "GET /members/:id/edit redirects to user profile", %{
      conn: conn,
      member_id: id,
      current_user: user
    } do
      conn = get(conn, "/members/#{id}/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /members/:id/show/edit redirects to user profile", %{
      conn: conn,
      member_id: id,
      current_user: user
    } do
      conn = get(conn, "/members/#{id}/show/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /members/:id (unlinked member show) redirects to user profile", %{
      conn: conn,
      member_id: id,
      current_user: user
    } do
      conn = get(conn, "/members/#{id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users/:id redirects to user profile", %{conn: conn, current_user: user} do
      other_user = Mv.Fixtures.user_with_role_fixture("admin")
      conn = get(conn, "/users/#{other_user.id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users/:id/edit redirects to user profile", %{conn: conn, current_user: user} do
      other_user = Mv.Fixtures.user_with_role_fixture("admin")
      conn = get(conn, "/users/#{other_user.id}/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users/:id/show/edit redirects to user profile", %{conn: conn, current_user: user} do
      other_user = Mv.Fixtures.user_with_role_fixture("admin")
      conn = get(conn, "/users/#{other_user.id}/show/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /membership_fee_settings/:id/edit_fee_type redirects to user profile", %{
      conn: conn,
      current_user: user
    } do
      type =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.Query.limit(1)
        |> Ash.read!(actor: Mv.Helpers.SystemActor.get_system_actor())
        |> List.first()

      if type do
        conn = get(conn, "/membership_fee_settings/#{type.id}/edit_fee_type")
        assert redirected_to(conn) == "/users/#{user.id}"
      end
    end

    @tag role: :member
    test "GET /groups/:slug redirects to user profile", %{
      conn: conn,
      current_user: user,
      group_slug: slug
    } do
      assert redirected_to(get(conn, "/groups/#{slug}")) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /admin/roles/:id redirects to user profile", %{
      conn: conn,
      role_id: id,
      current_user: user
    } do
      conn = get(conn, "/admin/roles/#{id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /admin/roles/:id/edit redirects to user profile", %{
      conn: conn,
      role_id: id,
      current_user: user
    } do
      conn = get(conn, "/admin/roles/#{id}/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  describe "integration: Mitglied (own_data) can access allowed paths via full router" do
    @tag role: :member
    test "GET / redirects to user profile (root not allowed for own_data)", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :member
    test "GET /users/:id (own profile) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}")
      assert conn.status == 200
    end

    @tag role: :member
    test "GET /users/:id/edit (own profile edit) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}/edit")
      assert conn.status == 200
    end

    @tag role: :member
    test "GET /users/:id/show/edit (own profile show edit) returns 200", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/users/#{user.id}/show/edit")
      assert conn.status == 200
    end

    # Full-router test: session may not preserve member_id; plug logic covered by unit test "own_data user with linked member can access /members/:id/edit (plug direct call)"
    @tag role: :member
    @tag :skip
    test "GET /members/:id/edit (linked member edit) returns 200 when user has linked member", %{
      conn: conn,
      current_user: user
    } do
      member = Mv.Fixtures.member_fixture()
      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      {:ok, user_after_update} =
        user
        |> Ash.Changeset.for_update(:update, %{})
        |> Ash.Changeset.force_set_argument(:member, %{id: member.id})
        |> Ash.update(actor: system_actor)

      user_with_member =
        user_after_update
        |> Ash.load!([:role], domain: Mv.Accounts)
        |> Mv.Authorization.Actor.ensure_loaded()
        |> Map.put(:member_id, member.id)

      conn = conn_with_password_user(conn, user_with_member)

      conn = get(conn, "/members/#{member.id}/edit")
      assert conn.status == 200
    end

    @tag role: :member
    @tag :skip
    test "GET /members/:id/show/edit (linked member show edit) returns 200 when user has linked member",
         %{
           conn: conn,
           current_user: user
         } do
      member = Mv.Fixtures.member_fixture()
      system_actor = Mv.Helpers.SystemActor.get_system_actor()

      {:ok, user_after_update} =
        user
        |> Ash.Changeset.for_update(:update, %{})
        |> Ash.Changeset.force_set_argument(:member, %{id: member.id})
        |> Ash.update(actor: system_actor)

      user_with_member =
        user_after_update
        |> Ash.load!([:role], domain: Mv.Accounts)
        |> Mv.Authorization.Actor.ensure_loaded()
        |> Map.put(:member_id, member.id)

      conn = conn_with_password_user(conn, user_with_member)

      conn = get(conn, "/members/#{member.id}/show/edit")
      assert conn.status == 200
    end

    # Skipped: MemberLive.Show requires membership fee cycle data; plug allows access (page loads then LiveView may error).
    @tag role: :member
    @tag :skip
    test "GET /members/:id for linked member returns 200", %{conn: conn, current_user: user} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      member = Mv.Fixtures.member_fixture()

      user =
        user
        |> Ash.Changeset.for_update(:update_user, %{})
        |> Ash.Changeset.force_set_argument(:member, %{id: member.id})
        |> Ash.update(actor: system_actor)
        |> case do
          {:ok, u} -> Ash.load!(u, :role, domain: Mv.Accounts, actor: system_actor)
          {:error, _} -> user
        end

      conn =
        conn
        |> MvWeb.ConnCase.conn_with_password_user(user)
        |> get("/members/#{member.id}")

      assert conn.status == 200
    end
  end

  # read_only (Vorstand/Buchhaltung): allowed /, /members, /members/:id, /groups, /groups/:slug
  describe "integration: read_only (Vorstand/Buchhaltung) allowed paths via full router" do
    setup %{conn: conn, current_user: current_user} do
      member = Mv.Fixtures.member_fixture()
      group = Mv.Fixtures.group_fixture()

      {:ok, conn: conn, current_user: current_user, member_id: member.id, group_slug: group.slug}
    end

    @tag role: :read_only
    test "GET / returns 200", %{conn: conn} do
      conn = get(conn, "/")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /members returns 200", %{conn: conn} do
      conn = get(conn, "/members")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /members/:id returns 200", %{conn: conn, member_id: id} do
      conn = get(conn, "/members/#{id}")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /groups returns 200", %{conn: conn} do
      conn = get(conn, "/groups")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /groups/:slug returns 200", %{conn: conn, group_slug: slug} do
      conn = get(conn, "/groups/#{slug}")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /users/:id (own profile) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /users/:id/edit (own profile edit) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}/edit")
      assert conn.status == 200
    end

    @tag role: :read_only
    test "GET /users/:id/show/edit (own profile show edit) returns 200", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/users/#{user.id}/show/edit")
      assert conn.status == 200
    end
  end

  describe "integration: read_only denied paths via full router" do
    setup %{conn: conn, current_user: current_user} do
      member = Mv.Fixtures.member_fixture()
      role = Mv.Fixtures.role_fixture("admin")
      group = Mv.Fixtures.group_fixture()

      type =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.Query.limit(1)
        |> Ash.read!(actor: Mv.Helpers.SystemActor.get_system_actor())
        |> List.first()

      {:ok,
       conn: conn,
       current_user: current_user,
       member_id: member.id,
       role_id: role.id,
       group_slug: group.slug,
       fee_type_id: type && type.id}
    end

    @tag role: :read_only
    test "GET /members/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/members/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /members/:id/edit redirects to user profile", %{
      conn: conn,
      member_id: id,
      current_user: user
    } do
      conn = get(conn, "/members/#{id}/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /users redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /users/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /users/:id (other user) redirects to user profile", %{
      conn: conn,
      current_user: user,
      role_id: _role_id
    } do
      other_user = Mv.Fixtures.user_with_role_fixture("admin")
      conn = get(conn, "/users/#{other_user.id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /settings redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /membership_fee_settings redirects to user profile", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/membership_fee_settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /groups/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/groups/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /groups/:slug/edit redirects to user profile", %{
      conn: conn,
      current_user: user,
      group_slug: slug
    } do
      conn = get(conn, "/groups/#{slug}/edit")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /admin/roles redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/admin/roles")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :read_only
    test "GET /admin/roles/:id redirects to user profile", %{
      conn: conn,
      role_id: id,
      current_user: user
    } do
      conn = get(conn, "/admin/roles/#{id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  # normal_user (Kassenwart): allowed /, /members, /members/new, /members/:id, /members/:id/edit, /groups, /groups/:slug
  describe "integration: normal_user (Kassenwart) allowed paths via full router" do
    setup %{conn: conn, current_user: current_user} do
      member = Mv.Fixtures.member_fixture()
      group = Mv.Fixtures.group_fixture()

      {:ok, conn: conn, current_user: current_user, member_id: member.id, group_slug: group.slug}
    end

    @tag role: :normal_user
    test "GET / returns 200", %{conn: conn} do
      conn = get(conn, "/")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /members returns 200", %{conn: conn} do
      conn = get(conn, "/members")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /members/new returns 200", %{conn: conn} do
      conn = get(conn, "/members/new")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /members/:id returns 200", %{conn: conn, member_id: id} do
      conn = get(conn, "/members/#{id}")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /members/:id/edit returns 200", %{conn: conn, member_id: id} do
      conn = get(conn, "/members/#{id}/edit")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /groups returns 200", %{conn: conn} do
      conn = get(conn, "/groups")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /groups/:slug returns 200", %{conn: conn, group_slug: slug} do
      conn = get(conn, "/groups/#{slug}")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /groups/new returns 200", %{conn: conn} do
      conn = get(conn, "/groups/new")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /groups/:slug/edit returns 200", %{conn: conn, group_slug: slug} do
      conn = get(conn, "/groups/#{slug}/edit")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /members/:id/show/edit returns 200", %{conn: conn, member_id: id} do
      conn = get(conn, "/members/#{id}/show/edit")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /users/:id (own profile) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /users/:id/edit (own profile edit) returns 200", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/#{user.id}/edit")
      assert conn.status == 200
    end

    @tag role: :normal_user
    test "GET /users/:id/show/edit (own profile show edit) returns 200", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/users/#{user.id}/show/edit")
      assert conn.status == 200
    end
  end

  describe "integration: normal_user denied paths via full router" do
    setup %{conn: conn, current_user: current_user} do
      other_user = Mv.Fixtures.user_with_role_fixture("admin")
      role = Mv.Fixtures.role_fixture("admin")
      group = Mv.Fixtures.group_fixture()

      {:ok,
       conn: conn,
       current_user: current_user,
       other_user_id: other_user.id,
       role_id: role.id,
       group_slug: group.slug}
    end

    @tag role: :normal_user
    test "GET /users redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /users/new redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/users/new")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /users/:id redirects to user profile", %{
      conn: conn,
      current_user: user,
      other_user_id: id
    } do
      conn = get(conn, "/users/#{id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /settings redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /membership_fee_settings redirects to user profile", %{
      conn: conn,
      current_user: user
    } do
      conn = get(conn, "/membership_fee_settings")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /admin/roles redirects to user profile", %{conn: conn, current_user: user} do
      conn = get(conn, "/admin/roles")
      assert redirected_to(conn) == "/users/#{user.id}"
    end

    @tag role: :normal_user
    test "GET /admin/roles/:id redirects to user profile", %{
      conn: conn,
      role_id: id,
      current_user: user
    } do
      conn = get(conn, "/admin/roles/#{id}")
      assert redirected_to(conn) == "/users/#{user.id}"
    end
  end

  describe "integration: admin can access all protected routes via full router" do
    setup %{conn: conn, current_user: current_user} do
      member = Mv.Fixtures.member_fixture()
      role = Mv.Fixtures.role_fixture("admin")
      group = Mv.Fixtures.group_fixture()

      {:ok,
       conn: conn,
       current_user: current_user,
       member_id: member.id,
       role_id: role.id,
       group_slug: group.slug}
    end

    @tag role: :admin
    test "GET / returns 200", %{conn: conn} do
      conn = get(conn, "/")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /members returns 200", %{conn: conn} do
      conn = get(conn, "/members")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /users returns 200", %{conn: conn} do
      conn = get(conn, "/users")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /settings returns 200", %{conn: conn} do
      conn = get(conn, "/settings")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /membership_fee_settings returns 200", %{conn: conn} do
      conn = get(conn, "/membership_fee_settings")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /admin/roles returns 200", %{conn: conn} do
      conn = get(conn, "/admin/roles")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /members/:id returns 200", %{conn: conn, member_id: id} do
      conn = get(conn, "/members/#{id}")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /admin/roles/:id returns 200", %{conn: conn, role_id: id} do
      conn = get(conn, "/admin/roles/#{id}")
      assert conn.status == 200
    end

    @tag role: :admin
    test "GET /groups/:slug returns 200", %{conn: conn, group_slug: slug} do
      conn = get(conn, "/groups/#{slug}")
      assert conn.status == 200
    end
  end
end
