defmodule ToweropsWeb.AdminControllerTest do
  use ToweropsWeb.ConnCase, async: true

  import Towerops.AccountsFixtures
  import Towerops.OrganizationsFixtures

  describe "POST /admin/impersonate/:user_id" do
    test "superuser can start impersonating another user", %{conn: conn} do
      superuser =
        user_fixture()
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      _org = organization_fixture(superuser.id)
      target_user = user_fixture()
      _target_org = organization_fixture(target_user.id)

      conn =
        conn
        |> log_in_user(superuser)
        |> post(~p"/admin/impersonate/#{target_user.id}")

      assert redirected_to(conn) == ~p"/orgs"
      assert get_session(conn, :impersonating) == true
      assert get_session(conn, :superuser_id) == superuser.id
      assert get_session(conn, :target_user_id) == target_user.id
    end

    test "superuser cannot impersonate themselves", %{conn: conn} do
      superuser =
        user_fixture()
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      _org = organization_fixture(superuser.id)

      conn =
        conn
        |> log_in_user(superuser)
        |> post(~p"/admin/impersonate/#{superuser.id}")

      assert redirected_to(conn) == ~p"/admin/users"
      assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "cannot impersonate yourself"
    end

    test "non-superuser is redirected away", %{conn: conn} do
      user = user_fixture()
      _org = organization_fixture(user.id)
      target_user = user_fixture()

      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/admin/impersonate/#{target_user.id}")

      assert redirected_to(conn) == ~p"/orgs"
    end

    test "unauthenticated user is redirected to login", %{conn: conn} do
      target_user = user_fixture()

      conn = post(conn, ~p"/admin/impersonate/#{target_user.id}")

      assert redirected_to(conn) == ~p"/users/log-in"
    end
  end

  describe "DELETE /admin/impersonate" do
    test "superuser can stop impersonation of another superuser", %{conn: conn} do
      superuser =
        user_fixture()
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      _org = organization_fixture(superuser.id)

      # Impersonating another superuser so require_superuser passes
      # (scope.user is the target, which must be superuser to reach the action)
      other_superuser =
        user_fixture()
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      _target_org = organization_fixture(other_superuser.id)

      conn =
        conn
        |> log_in_user(superuser)
        |> put_session(:impersonating, true)
        |> put_session(:superuser_id, superuser.id)
        |> put_session(:target_user_id, other_superuser.id)
        |> delete(~p"/admin/impersonate")

      # stop_impersonation redirects to signed_in_path for the superuser
      assert redirected_to(conn) =~ ~r"^/(dashboard|orgs)$"
      assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Stopped impersonating"
    end

    test "impersonating non-superuser cannot reach stop endpoint via pipeline", %{conn: conn} do
      # When impersonating a non-superuser, require_superuser blocks the request
      # because scope.user is the target (non-superuser) user.
      superuser =
        user_fixture()
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      _org = organization_fixture(superuser.id)
      target_user = user_fixture()

      conn =
        conn
        |> log_in_user(superuser)
        |> put_session(:impersonating, true)
        |> put_session(:superuser_id, superuser.id)
        |> put_session(:target_user_id, target_user.id)
        |> delete(~p"/admin/impersonate")

      # Pipeline redirects because scope.user (target) is not a superuser
      assert redirected_to(conn) == ~p"/orgs"
    end

    test "non-superuser is redirected away", %{conn: conn} do
      user = user_fixture()
      _org = organization_fixture(user.id)

      conn =
        conn
        |> log_in_user(user)
        |> delete(~p"/admin/impersonate")

      assert redirected_to(conn) == ~p"/orgs"
    end
  end
end
