defmodule MvWeb.GroupLive.ShowAuthorizationTest do
  @moduledoc """
  Tests for authorization and security in Add/Remove Member functionality.
  Tests server-side authorization checks and UI permission enforcement.
  """

  use MvWeb.ConnCase, async: false
  import Phoenix.LiveViewTest
  use Gettext, backend: MvWeb.Gettext

  alias Mv.Membership
  alias Mv.Fixtures

  describe "server-side authorization" do
    test "add member event handler checks :update permission", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Alice",
            last_name: "Smith",
            email: "alice@example.com"
          },
          actor: system_actor
        )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      # Open inline input and try to add member
      view
      |> element("button", "Add Member")
      |> render_click()

      view
      |> element("#member-search-input")
      |> render_focus()

      # phx-change is on the form, so we need to trigger it via the form
      view
      |> element("form[phx-change='search_members']")
      |> render_change(%{"member_search" => "Alice"})

      view
      |> element("[data-member-id='#{member.id}']")
      |> render_click()

      # Try to add (should succeed for admin)
      view
      |> element("button[phx-click='add_selected_members']")
      |> render_click()

      # Should succeed (admin has :update permission, member should appear in list)
      assert has_element?(view, "[data-testid=group-show-members-table]", "Alice")
    end

    @tag role: :read_only
    test "unauthorized user cannot add member (server-side check)", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, _member} =
        Membership.create_member(
          %{
            first_name: "Bob",
            last_name: "Jones",
            email: "bob@example.com"
          },
          actor: system_actor
        )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      # Try to trigger add event directly (even if button is hidden)
      # This tests server-side authorization
      # Note: If button is hidden, we can't click it, but we test the event handler
      # by trying to send the event directly if possible

      refute has_element?(view, "button[phx-click='show_add_member_input']")
    end

    test "remove member event handler checks :update permission", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Charlie",
            last_name: "Brown",
            email: "charlie@example.com"
          },
          actor: system_actor
        )

      Membership.create_member_group(%{member_id: member.id, group_id: group.id},
        actor: system_actor
      )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      view
      |> element("[data-testid=group-show-remove-member][phx-value-member_id='#{member.id}']")
      |> render_click()

      refute has_element?(view, "[data-testid=group-show-members-table]", "Charlie")
    end

    @tag role: :read_only
    test "unauthorized user cannot remove member (server-side check)", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "David",
            last_name: "Wilson",
            email: "david@example.com"
          },
          actor: system_actor
        )

      Membership.create_member_group(%{member_id: member.id, group_id: group.id},
        actor: system_actor
      )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      refute has_element?(view, "[data-testid=group-show-remove-member]")
    end

    test "error flash message on unauthorized access", %{conn: conn} do
      # This test verifies that error messages are shown for unauthorized access
      # Implementation will handle this in event handlers
      _system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, _view, _html} = live(conn, "/groups/#{group.slug}")

      # For admin, should not see error
      # For non-admin, buttons are hidden (UI-level check)
      # Server-side check will show error if event is somehow triggered
    end
  end

  describe "UI permission checks" do
    test "buttons are hidden for unauthorized users", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Eve",
            last_name: "Davis",
            email: "eve@example.com"
          },
          actor: system_actor
        )

      Membership.create_member_group(%{member_id: member.id, group_id: group.id},
        actor: system_actor
      )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      assert has_element?(view, "button[phx-click='show_add_member_input']")
      assert has_element?(view, "[data-testid=group-show-remove-member]")
    end

    @tag role: :read_only
    test "Add Member button is hidden for read-only users", %{conn: conn} do
      _system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      refute has_element?(view, "button[phx-click='show_add_member_input']")
    end

    @tag role: :read_only
    test "Remove button is hidden for read-only users", %{conn: conn} do
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      group = Fixtures.group_fixture()

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Frank",
            last_name: "Moore",
            email: "frank@example.com"
          },
          actor: system_actor
        )

      Membership.create_member_group(%{member_id: member.id, group_id: group.id},
        actor: system_actor
      )

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      refute has_element?(view, "[data-testid=group-show-remove-member]")
    end

    @tag role: :read_only
    test "inline add member area cannot be opened for unauthorized users", %{conn: conn} do
      group = Fixtures.group_fixture()

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      refute has_element?(view, "button[phx-click='show_add_member_input']")
    end
  end

  describe "member (own_data) page access" do
    # Members have no page permission for /groups or /groups/:slug; they are redirected.
    # This tests that limited access for the member role is enforced.
    @tag role: :member
    test "member is redirected when accessing group show page", %{conn: conn} do
      group = Fixtures.group_fixture()

      result = live(conn, "/groups/#{group.slug}")

      assert {:error, {:redirect, %{to: path, flash: %{"error" => _}}}} = result
      assert path =~ ~r|^/users/[^/]+$|
    end

    @tag role: :member
    test "member is redirected when accessing groups index", %{conn: conn} do
      result = live(conn, "/groups")

      assert {:error, {:redirect, %{to: path, flash: %{"error" => _}}}} = result
      assert path =~ ~r|^/users/[^/]+$|
    end
  end

  describe "security edge cases" do
    test "slug injection attempts are prevented", %{conn: conn} do
      # Try to inject malicious content in slug
      malicious_slug = "'; DROP TABLE groups; --"

      result = live(conn, "/groups/#{malicious_slug}")

      # Should not execute SQL, should return 404 or error
      assert match?({:error, {:redirect, %{to: "/groups"}}}, result) ||
               match?({:error, {:live_redirect, %{to: "/groups"}}}, result)
    end

    @tag :skip
    test "non-existent member IDs are handled", %{conn: conn} do
      # Future: test add_selected_members with invalid ID (would require pushing event with forged selected_member_ids)
      group = Fixtures.group_fixture()

      {:ok, view, _html} = live(conn, "/groups/#{group.slug}")

      assert has_element?(view, "button", "Add Member")
    end

    test "non-existent group IDs are handled", %{conn: conn} do
      # Accessing non-existent group should redirect
      non_existent_slug = "non-existent-group-#{System.unique_integer([:positive])}"

      result = live(conn, "/groups/#{non_existent_slug}")

      assert match?({:error, {:redirect, %{to: "/groups"}}}, result) ||
               match?({:error, {:live_redirect, %{to: "/groups"}}}, result)
    end
  end
end
