defmodule Web.Live.Actors.ShowTest do
  use Web.ConnCase, async: true

  test "redirects to sign in page for unauthorized user", %{conn: conn} do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)

    path = ~p"/#{account}/actors/#{actor}"

    assert live(conn, path) ==
             {:error,
              {:redirect,
               %{
                 to: ~p"/#{account}?#{%{redirect_to: path}}",
                 flash: %{"error" => "You must log in to access this page."}
               }}}
  end

  test "renders deleted actor without action buttons", %{conn: conn} do
    account = Fixtures.Accounts.create_account()

    actor =
      Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
      |> Fixtures.Actors.delete()

    auth_actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    auth_identity = Fixtures.Auth.create_identity(account: account, actor: auth_actor)

    {:ok, _lv, html} =
      conn
      |> authorize_conn(auth_identity)
      |> live(~p"/#{account}/actors/#{actor}")

    assert html =~ "(deleted)"
    assert active_buttons(html) == []
  end

  test "renders breadcrumbs item", %{conn: conn} do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    identity = Fixtures.Auth.create_identity(account: account, actor: actor)

    {:ok, _lv, html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    assert item = Floki.find(html, "[aria-label='Breadcrumb']")
    breadcrumbs = String.trim(Floki.text(item))
    assert breadcrumbs =~ "Actors"
    assert breadcrumbs =~ actor.name
  end

  test "renders clients table", %{
    conn: conn
  } do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    identity = Fixtures.Auth.create_identity(account: account, actor: actor)
    client = Fixtures.Clients.create_client(account: account, actor: actor)

    {:ok, lv, _html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    [row] =
      lv
      |> element("#clients")
      |> render()
      |> table_to_map()

    assert row["name"] == client.name
    assert row["status"] == "Offline"

    assert Domain.Clients.connect_client(client) == :ok

    {:ok, lv, _html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    [row] =
      lv
      |> element("#clients")
      |> render()
      |> table_to_map()

    assert row["status"] == "Online"
  end

  test "renders flows table", %{
    conn: conn
  } do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    identity = Fixtures.Auth.create_identity(account: account, actor: actor)
    client = Fixtures.Clients.create_client(account: account, actor: actor)

    flow =
      Fixtures.Flows.create_flow(
        account: account,
        client: client
      )

    flow = Repo.preload(flow, [:client, gateway: [:group], policy: [:actor_group, :resource]])

    {:ok, lv, _html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    [row] =
      lv
      |> element("#flows")
      |> render()
      |> table_to_map()

    assert row["authorized at"]
    assert row["expires at"]
    assert row["policy"] =~ flow.policy.actor_group.name
    assert row["policy"] =~ flow.policy.resource.name

    assert row["client (ip)"] ==
             "#{flow.client.name} (#{client.last_seen_remote_ip})"

    assert row["gateway (ip)"] ==
             "#{flow.gateway.group.name}-#{flow.gateway.name} (189.172.73.153)"
  end

  test "renders flows even for deleted policies", %{
    conn: conn
  } do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    identity = Fixtures.Auth.create_identity(account: account, actor: actor)
    client = Fixtures.Clients.create_client(account: account, actor: actor)

    flow =
      Fixtures.Flows.create_flow(
        account: account,
        client: client
      )

    flow = Repo.preload(flow, [:client, gateway: [:group], policy: [:actor_group, :resource]])
    Fixtures.Policies.delete_policy(flow.policy)

    {:ok, lv, _html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    [row] =
      lv
      |> element("#flows")
      |> render()
      |> table_to_map()

    assert row["authorized at"]
    assert row["expires at"]
    assert row["policy"] =~ flow.policy.actor_group.name
    assert row["policy"] =~ flow.policy.resource.name

    assert row["client (ip)"] ==
             "#{flow.client.name} (#{client.last_seen_remote_ip})"

    assert row["gateway (ip)"] ==
             "#{flow.gateway.group.name}-#{flow.gateway.name} (189.172.73.153)"
  end

  test "renders flows even for deleted policy assocs", %{
    conn: conn
  } do
    account = Fixtures.Accounts.create_account()
    actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
    identity = Fixtures.Auth.create_identity(account: account, actor: actor)
    client = Fixtures.Clients.create_client(account: account, actor: actor)

    flow =
      Fixtures.Flows.create_flow(
        account: account,
        client: client
      )

    flow = Repo.preload(flow, [:client, gateway: [:group], policy: [:actor_group, :resource]])
    Fixtures.Actors.delete_group(flow.policy.actor_group)
    Fixtures.Resources.delete_resource(flow.policy.resource)

    {:ok, lv, _html} =
      conn
      |> authorize_conn(identity)
      |> live(~p"/#{account}/actors/#{actor}")

    [row] =
      lv
      |> element("#flows")
      |> render()
      |> table_to_map()

    assert row["authorized at"]
    assert row["expires at"]
    assert row["policy"] =~ flow.policy.actor_group.name
    assert row["policy"] =~ flow.policy.resource.name

    assert row["client (ip)"] ==
             "#{flow.client.name} (#{client.last_seen_remote_ip})"

    assert row["gateway (ip)"] ==
             "#{flow.gateway.group.name}-#{flow.gateway.name} (189.172.73.153)"
  end

  describe "users" do
    setup do
      account = Fixtures.Accounts.create_account()
      Domain.Config.put_env_override(:outbound_email_adapter_configured?, true)
      provider = Fixtures.Auth.create_email_provider(account: account)
      actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      %{
        account: account,
        actor: actor,
        provider: provider,
        identity: identity
      }
    end

    test "renders (you) next to subject actor title", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, _lv, html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert html =~ "(you)"

      other_actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: other_actor)

      {:ok, _lv, html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute html =~ "(you)"
    end

    test "renders actor details", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      group = Fixtures.Actors.create_group(account: account)
      Fixtures.Actors.create_membership(account: account, actor: actor, group: group)

      {:ok, lv, html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert html =~ actor.name
      assert html =~ "User"

      table =
        lv
        |> element("#actor")
        |> render()
        |> vertical_table_to_map()

      assert table["groups"] == group.name
      assert table["name"] == actor.name
      assert table["role"] == "admin"
      assert around_now?(table["last signed in"])
    end

    test "renders actor identities", %{
      account: account,
      actor: actor,
      identity: admin_identity,
      conn: conn
    } do
      invited_identity =
        Fixtures.Auth.create_identity(account: account, actor: actor)
        |> Ecto.Changeset.change(
          created_by: :identity,
          created_by_identity_id: admin_identity.id
        )
        |> Repo.update!()

      synced_identity =
        Fixtures.Auth.create_identity(account: account, actor: actor)
        |> Ecto.Changeset.change(created_by: :provider)
        |> Repo.update!()

      admin_identity = Repo.preload(admin_identity, :provider)
      invited_identity = Repo.preload(invited_identity, :provider)
      synced_identity = Repo.preload(synced_identity, :provider)

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("#actors")
      |> render()
      |> table_to_map()
      |> with_table_row(
        "identity",
        "#{admin_identity.provider.name} #{admin_identity.provider_identifier}",
        fn row ->
          assert row["actions"] =~ "Delete"
          assert around_now?(row["last signed in"])
          assert around_now?(row["created"])
        end
      )
      |> with_table_row(
        "identity",
        "#{invited_identity.provider.name} #{invited_identity.provider_identifier}",
        fn row ->
          assert row["actions"] =~ "Delete"
          assert row["created"] =~ "by #{actor.name}"
          assert row["last signed in"] == "never"
        end
      )
      |> with_table_row(
        "identity",
        "#{synced_identity.provider.name} #{synced_identity.provider_identifier}",
        fn row ->
          refute row["actions"]
          assert row["created"] =~ "synced"
          assert row["created"] =~ "from #{synced_identity.provider.name}"
          assert row["last signed in"] == "never"
        end
      )
    end

    test "allows sending welcome email", %{
      account: account,
      actor: actor,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      email_identity =
        Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)
        |> Ecto.Changeset.change(
          created_by: :identity,
          created_by_identity_id: admin_identity.id
        )
        |> Repo.update!()

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("#identity-#{email_identity.id} button", "Send Welcome Email")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Welcome email sent to #{email_identity.provider_identifier}"

      assert_email_sent(fn email ->
        assert email.subject == "Welcome to Firezone"
        assert email.text_body =~ account.slug
      end)
    end

    test "shows email button for identities with email", %{
      account: account,
      actor: actor,
      provider: email_provider,
      identity: admin_identity,
      conn: conn
    } do
      {google_provider, _bypass} =
        Fixtures.Auth.start_and_create_google_workspace_provider(account: account)

      google_identity =
        Fixtures.Auth.create_identity(
          account: account,
          actor: actor,
          provider: google_provider,
          provider_state: %{
            "userinfo" => %{"email" => Fixtures.Auth.email()}
          }
        )

      oidc_identity = Fixtures.Auth.create_identity(account: account, actor: actor)

      email_identity =
        Fixtures.Auth.create_identity(account: account, actor: actor, provider: email_provider)
        |> Ecto.Changeset.change(
          created_by: :identity,
          created_by_identity_id: admin_identity.id
        )
        |> Repo.update!()

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("#identity-#{email_identity.id} button", "Send Welcome Email")
             |> has_element?()

      assert lv
             |> element("#identity-#{google_identity.id} button", "Send Welcome Email")
             |> has_element?()

      refute lv
             |> element("#identity-#{oidc_identity.id} button", "Send Welcome Email")
             |> has_element?()
    end

    test "allows deleting identities", %{
      account: account,
      actor: actor,
      identity: admin_identity,
      conn: conn
    } do
      other_identity =
        Fixtures.Auth.create_identity(account: account, actor: actor)
        |> Ecto.Changeset.change(
          created_by: :identity,
          created_by_identity_id: admin_identity.id
        )
        |> Repo.update!()

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("#identity-#{other_identity.id} button", "Delete")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Identity was deleted."
    end

    test "allows creating identities", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("a", "Add Identity")
      |> render_click()

      assert_redirect(lv, ~p"/#{account}/actors/users/#{actor}/new_identity")

      actor = Fixtures.Actors.update(actor, last_synced_at: DateTime.utc_now())

      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("a", "Add Identity")
      |> render_click()

      assert_redirect(lv, ~p"/#{account}/actors/users/#{actor}/new_identity")
    end

    test "renders actor tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      Fixtures.Tokens.create_token(
        type: :client,
        account: account,
        identity: identity
      )

      Fixtures.Tokens.create_token(
        account: account,
        identity: identity,
        last_seen_at: DateTime.utc_now(),
        last_seen_remote_ip: Fixtures.Auth.remote_ip()
      )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      [row1, row2] =
        lv
        |> element("#tokens")
        |> render()
        |> table_to_map()

      assert row1["type"] == "browser"
      assert row1["expires"] in ["tomorrow", "in 24 hours"]
      assert row1["last used (ip)"] == "never"
      assert around_now?(row1["created"])
      assert row1["actions"] == "Revoke"

      assert row2["type"] == "client"
      assert row2["expires"] in ["tomorrow", "in 24 hours"]
      assert row2["last used (ip)"] == "never"
      assert around_now?(row2["created"])
      assert row2["actions"] == "Revoke"
    end

    test "allows revoking tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      token =
        Fixtures.Tokens.create_token(
          type: :client,
          account: account,
          identity: identity
        )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("td button", "Revoke")
             |> render_click()

      assert lv
             |> element("#tokens")
             |> render()
             |> table_to_map() == []

      assert Repo.get_by(Domain.Tokens.Token, id: token.id).deleted_at
    end

    test "allows revoking all tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      token =
        Fixtures.Tokens.create_token(
          type: :client,
          account: account,
          identity: identity
        )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("button", "Revoke All")
             |> render_click()

      assert lv
             |> element("#tokens")
             |> render()
             |> table_to_map() == []

      assert Repo.get_by(Domain.Tokens.Token, id: token.id).deleted_at
    end

    test "allows editing actors", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("a", "Edit User")
             |> render_click() ==
               {:error,
                {:live_redirect, %{to: ~p"/#{account}/actors/#{actor}/edit", kind: :push}}}
    end

    test "allows deleting actors", %{
      account: account,
      identity: identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)

      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("button", "Delete User")
      |> render_click()

      assert_redirect(lv, ~p"/#{account}/actors")

      assert Repo.get(Domain.Actors.Actor, actor.id).deleted_at
    end

    test "renders error when trying to delete last admin", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("button", "Delete User")
             |> render_click()
             |> Floki.find(".flash-error")
             |> element_to_text() =~ "You can't delete the last admin of an account."

      refute Repo.get(Domain.Actors.Actor, actor.id).deleted_at
    end

    test "allows disabling actors", %{
      account: account,
      identity: identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)

      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute has_element?(lv, "button", "Enable User")

      assert lv
             |> element("button", "Disable User")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Actor was disabled."

      assert Repo.get(Domain.Actors.Actor, actor.id).disabled_at
    end

    test "renders error when trying to disable last admin", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("button", "Disable User")
             |> render_click()
             |> Floki.find(".flash-error")
             |> element_to_text() =~ "You can't disable the last admin of an account."

      refute Repo.get(Domain.Actors.Actor, actor.id).disabled_at
    end

    test "allows enabling actors", %{
      account: account,
      identity: identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account)
      actor = Fixtures.Actors.disable(actor)

      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute has_element?(lv, "button", "Disable User")

      assert lv
             |> element("button", "Enable User")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Actor was enabled."

      refute Repo.get(Domain.Actors.Actor, actor.id).disabled_at
    end
  end

  describe "service accounts" do
    setup do
      account = Fixtures.Accounts.create_account()
      actor = Fixtures.Actors.create_actor(type: :service_account, account: account)

      Domain.Config.put_env_override(:outbound_email_adapter_configured?, true)
      provider = Fixtures.Auth.create_email_provider(account: account)

      identity =
        Fixtures.Auth.create_identity(
          actor: [type: :account_admin_user],
          account: account
        )

      %{
        account: account,
        actor: actor,
        provider: provider,
        identity: identity
      }
    end

    test "renders actor details", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      group = Fixtures.Actors.create_group(account: account)
      Fixtures.Actors.create_membership(account: account, actor: actor, group: group)

      {:ok, lv, html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert html =~ "Service Account"

      assert lv
             |> element("#actor")
             |> render()
             |> vertical_table_to_map() == %{
               "groups" => group.name,
               "last signed in" => "never",
               "name" => actor.name,
               "role" => "service account"
             }
    end

    test "does not render actor identities", %{
      account: account,
      actor: actor,
      identity: admin_identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute has_element?(lv, "#identities")
    end

    test "allows creating tokens", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("a:first-child", "Create Token")
      |> render_click()

      assert_redirect(lv, ~p"/#{account}/actors/service_accounts/#{actor}/new_identity")
    end

    test "allows editing actors", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("a", "Edit Service Account")
             |> render_click() ==
               {:error,
                {:live_redirect, %{to: ~p"/#{account}/actors/#{actor}/edit", kind: :push}}}
    end

    test "allows deleting actors", %{
      account: account,
      identity: identity,
      actor: actor,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      lv
      |> element("button", "Delete Service Account")
      |> render_click()

      assert_redirect(lv, ~p"/#{account}/actors")

      assert Repo.get(Domain.Actors.Actor, actor.id).deleted_at
    end

    test "allows disabling actors", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute has_element?(lv, "button", "Enable Service Account")

      assert lv
             |> element("button", "Disable Service Account")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Actor was disabled."

      assert Repo.get(Domain.Actors.Actor, actor.id).disabled_at
    end

    test "allows enabling actors", %{
      account: account,
      actor: actor,
      identity: identity,
      conn: conn
    } do
      actor = Fixtures.Actors.disable(actor)

      {:ok, lv, _html} =
        conn
        |> authorize_conn(identity)
        |> live(~p"/#{account}/actors/#{actor}")

      refute has_element?(lv, "button", "Disable Service Account")

      assert lv
             |> element("button", "Enable Service Account")
             |> render_click()
             |> Floki.find(".flash-info")
             |> element_to_text() =~ "Actor was enabled."

      refute Repo.get(Domain.Actors.Actor, actor.id).disabled_at
    end

    test "renders actor tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      Fixtures.Tokens.create_token(
        type: :client,
        account: account,
        identity: identity
      )

      Fixtures.Tokens.create_token(
        account: account,
        identity: identity,
        last_seen_at: DateTime.utc_now(),
        last_seen_remote_ip: Fixtures.Auth.remote_ip()
      )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      [row1, row2] =
        lv
        |> element("#tokens")
        |> render()
        |> table_to_map()

      assert row1["type"] == "browser"
      assert row1["expires"] in ["tomorrow", "in 24 hours"]
      assert row1["last used (ip)"] == "never"
      assert around_now?(row1["created"])
      assert row1["actions"] == "Revoke"

      assert row2["type"] == "client"
      assert row2["expires"] in ["tomorrow", "in 24 hours"]
      assert row2["last used (ip)"] == "never"
      assert around_now?(row2["created"])
      assert row2["actions"] == "Revoke"
    end

    test "allows revoking tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      token =
        Fixtures.Tokens.create_token(
          type: :client,
          account: account,
          identity: identity
        )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("td button", "Revoke")
             |> render_click()

      assert lv
             |> element("#tokens")
             |> render()
             |> table_to_map() == []

      assert Repo.get_by(Domain.Tokens.Token, id: token.id).deleted_at
    end

    test "allows revoking all tokens", %{
      account: account,
      provider: provider,
      identity: admin_identity,
      conn: conn
    } do
      actor = Fixtures.Actors.create_actor(account: account)
      identity = Fixtures.Auth.create_identity(account: account, actor: actor, provider: provider)

      token =
        Fixtures.Tokens.create_token(
          type: :client,
          account: account,
          identity: identity
        )

      {:ok, lv, _html} =
        conn
        |> authorize_conn(admin_identity)
        |> live(~p"/#{account}/actors/#{actor}")

      assert lv
             |> element("button", "Revoke All")
             |> render_click()

      assert lv
             |> element("#tokens")
             |> render()
             |> table_to_map() == []

      assert Repo.get_by(Domain.Tokens.Token, id: token.id).deleted_at
    end
  end
end
