defmodule MvWeb.RoleLive.ShowTest do
  @moduledoc """
  Tests for the role show page.

  Tests cover:
  - Displaying role information
  - System role badge display
  - User count display
  - Navigation
  - Error handling
  - Delete functionality
  """
  use MvWeb.ConnCase, async: false
  import Phoenix.LiveViewTest
  require Ash.Query
  use Gettext, backend: MvWeb.Gettext

  alias Mv.Authorization
  alias Mv.Authorization.Role

  # Helper to create a role (authorize?: false for test data setup)
  defp create_role(attrs \\ %{}) do
    default_attrs = %{
      name: "Test Role #{System.unique_integer([:positive])}",
      description: "Test description",
      permission_set_name: "read_only"
    }

    attrs = Map.merge(default_attrs, attrs)

    case Authorization.create_role(attrs, authorize?: false) do
      {:ok, role} -> role
      {:error, error} -> raise "Failed to create role: #{inspect(error)}"
    end
  end

  # Helper to create admin user with admin role
  defp create_admin_user(conn, actor) do
    # Create admin role
    admin_role =
      case Authorization.list_roles(authorize?: false) do
        {:ok, roles} ->
          case Enum.find(roles, &(&1.name == "Admin")) do
            nil ->
              # Create admin role if it doesn't exist
              create_role(%{
                name: "Admin",
                description: "Administrator with full access",
                permission_set_name: "admin"
              })

            role ->
              role
          end

        _ ->
          # Create admin role if list_roles fails
          create_role(%{
            name: "Admin",
            description: "Administrator with full access",
            permission_set_name: "admin"
          })
      end

    # Create user
    {:ok, user} =
      Mv.Accounts.User
      |> Ash.Changeset.for_create(:register_with_password, %{
        email: "admin#{System.unique_integer([:positive])}@mv.local",
        password: "testpassword123"
      })
      |> Ash.create(actor: actor)

    # Assign admin role using manage_relationship
    {:ok, user} =
      user
      |> Ash.Changeset.for_update(:update, %{})
      |> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
      |> Ash.update(actor: actor)

    # Load role for authorization checks (must be loaded for can?/3 to work)
    user_with_role = Ash.load!(user, :role, domain: Mv.Accounts, actor: actor)

    # Store user with role in session for LiveView
    conn = conn_with_password_user(conn, user_with_role)
    {conn, user_with_role, admin_role}
  end

  describe "mount and display" do
    setup %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {conn, _user, _admin_role} = create_admin_user(conn, system_actor)
      %{conn: conn, actor: system_actor}
    end

    @tag :slow
    test "mounts successfully with valid role ID", %{conn: conn} do
      role = create_role()

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ role.name
    end

    @tag :slow
    test "displays role name", %{conn: conn} do
      role = create_role(%{name: "Test Role Name"})

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ "Test Role Name"
      assert html =~ gettext("Name")
    end

    test "displays role description when present", %{conn: conn} do
      role = create_role(%{description: "This is a test description"})

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ "This is a test description"
      assert html =~ gettext("Description")
    end

    test "displays 'No description' when description is missing", %{conn: conn} do
      role = create_role(%{description: nil})

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ gettext("No description")
    end

    @tag :slow
    test "displays permission set name", %{conn: conn} do
      role = create_role(%{permission_set_name: "read_only"})

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ "read_only"
      assert html =~ gettext("Permission Set")
    end

    test "displays system role badge when is_system_role is true", %{conn: conn, actor: actor} do
      system_role =
        Role
        |> Ash.Changeset.for_create(:create_role, %{
          name: "System Role",
          permission_set_name: "own_data"
        })
        |> Ash.Changeset.force_change_attribute(:is_system_role, true)
        |> Ash.create!(actor: actor)

      {:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}")

      assert html =~ gettext("System Role")
      assert html =~ gettext("Yes")
    end

    @tag :slow
    test "displays non-system role badge when is_system_role is false", %{conn: conn} do
      role = create_role()

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      assert html =~ gettext("System Role")
      assert html =~ gettext("No")
    end

    test "displays user count", %{conn: conn} do
      role = create_role()

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      # User count should be displayed (might be 0 or more)
      assert html =~ gettext("User") || html =~ "0" || html =~ "users"
    end
  end

  describe "navigation" do
    setup %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {conn, _user, _admin_role} = create_admin_user(conn, system_actor)
      %{conn: conn, actor: system_actor}
    end

    @tag :slow
    test "back button navigates to role list", %{conn: conn} do
      role = create_role()

      {:ok, view, _html} = live(conn, "/admin/roles/#{role.id}")

      assert {:error, {:live_redirect, %{to: to}}} =
               view
               |> element(
                 "a[aria-label='#{gettext("Back to roles list")}'], button[aria-label='#{gettext("Back to roles list")}']"
               )
               |> render_click()

      assert to == "/admin/roles"
    end

    test "edit button navigates to edit form", %{conn: conn} do
      role = create_role()

      {:ok, view, _html} = live(conn, "/admin/roles/#{role.id}")

      assert {:error, {:live_redirect, %{to: to}}} =
               view
               |> element(
                 "a[href='/admin/roles/#{role.id}/edit'], button[href='/admin/roles/#{role.id}/edit']"
               )
               |> render_click()

      assert to == "/admin/roles/#{role.id}/edit"
    end
  end

  describe "error handling" do
    setup %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {conn, _user, _admin_role} = create_admin_user(conn, system_actor)
      %{conn: conn, actor: system_actor}
    end

    test "redirects to role list with error for invalid role ID", %{conn: conn} do
      invalid_id = Ecto.UUID.generate()

      # Should redirect to index with error message
      result = live(conn, "/admin/roles/#{invalid_id}")

      assert match?({:error, {:redirect, %{to: "/admin/roles"}}}, result) or
               match?({:error, {:live_redirect, %{to: "/admin/roles"}}}, result)
    end
  end

  describe "delete functionality" do
    setup %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {conn, _user, _admin_role} = create_admin_user(conn, system_actor)
      %{conn: conn, actor: system_actor}
    end

    test "delete button is not shown for system roles", %{conn: conn, actor: actor} do
      system_role =
        Role
        |> Ash.Changeset.for_create(:create_role, %{
          name: "System Role",
          permission_set_name: "own_data"
        })
        |> Ash.Changeset.force_change_attribute(:is_system_role, true)
        |> Ash.create!(actor: actor)

      {:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}")

      # Delete button should not be visible for system roles
      refute html =~ ~r/Delete.*Role.*#{system_role.id}/i
    end

    test "delete button is shown for non-system roles", %{conn: conn} do
      role = create_role()

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      # Delete button should be visible for non-system roles
      assert html =~ gettext("Delete Role") || html =~ "delete"
    end
  end

  describe "page title" do
    setup %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {conn, _user, _admin_role} = create_admin_user(conn, system_actor)
      %{conn: conn, actor: system_actor}
    end

    test "sets correct page title", %{conn: conn} do
      role = create_role()

      {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}")

      # Check that page title is set (might be in title tag or header)
      assert html =~ gettext("Show Role") || html =~ role.name
    end
  end
end
