defmodule TarakanWeb.AdminLiveTest do
  use TarakanWeb.ConnCase, async: true

  import Phoenix.LiveViewTest

  alias Tarakan.Accounts
  alias Tarakan.Accounts.Account
  alias Tarakan.Repo

  setup %{conn: conn} do
    admin =
      account_fixture()
      |> Account.authorization_changeset(%{
        state: "active",
        platform_role: "admin",
        trust_tier: "reviewer"
      })
      |> Repo.update!()

    %{admin: admin, conn: log_in_account(conn, admin)}
  end

  test "admin sees account summary, search, and management links", %{conn: conn} do
    target = account_fixture(%{handle: "managed-account"})

    {:ok, view, _html} = live(conn, ~p"/admin")

    assert has_element?(view, "#admin-dashboard")
    assert has_element?(view, "#admin-summary-admins")
    assert has_element?(view, "#admin-account-filter")
    assert has_element?(view, "#admin-account-#{target.id}-manage")

    view
    |> form("#admin-account-filter", filters: %{query: "managed-account"})
    |> render_change()

    assert has_element?(view, "#admin-account-#{target.id}-manage")
  end

  test "admin updates account standing, role, and trust tier", %{conn: conn} do
    target = account_fixture()

    {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{target.id}")

    assert has_element?(view, "#admin-account")
    assert has_element?(view, "#admin-authorization-form")

    view
    |> form("#admin-authorization-form",
      authorization: %{
        state: "active",
        platform_role: "moderator",
        trust_tier: "reviewer"
      }
    )
    |> render_submit()

    updated = Accounts.get_account!(target.id)
    assert updated.state == "active"
    assert updated.platform_role == "moderator"
    assert updated.trust_tier == "reviewer"
    assert has_element?(view, "#admin-authorization-form")
  end

  test "ordinary members cannot open platform administration", %{conn: _admin_conn} do
    member =
      account_fixture() |> Account.authorization_changeset(%{state: "active"}) |> Repo.update!()

    conn = log_in_account(build_conn(), member)

    assert {:error, {:redirect, %{to: path, flash: flash}}} = live(conn, ~p"/admin")
    assert path == ~p"/"
    assert flash["error"] == "Administrator access is required."
  end

  test "last administrator cannot remove their own authority", %{conn: conn, admin: admin} do
    {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{admin.id}")

    view
    |> form("#admin-authorization-form",
      authorization: %{
        state: "active",
        platform_role: "member",
        trust_tier: "reviewer"
      }
    )
    |> render_submit()

    assert Accounts.get_account!(admin.id).platform_role == "admin"
    assert has_element?(view, "#admin-authorization-form")
  end

  test "filtering requires a still-valid sudo session", %{conn: conn} do
    {:ok, view, _html} = live(conn, ~p"/admin")

    expire_sudo_mode(view)

    assert {:error, {:live_redirect, %{to: "/accounts/log-in?return_to=%2Fadmin"}}} =
             view
             |> form("#admin-account-filter", filters: %{query: "anything"})
             |> render_change()
  end

  test "saving requires a still-valid sudo session", %{conn: conn} do
    target = account_fixture()
    {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{target.id}")

    expire_sudo_mode(view)

    assert {:error,
            {:live_redirect, %{to: "/accounts/log-in?return_to=%2Fadmin%2Faccounts%2F" <> _}}} =
             view
             |> form("#admin-authorization-form",
               authorization: %{
                 state: "active",
                 platform_role: "moderator",
                 trust_tier: "reviewer"
               }
             )
             |> render_submit()

    assert Accounts.get_account!(target.id).platform_role == target.platform_role
  end

  # Simulates the two-hour sudo window expiring while the LiveView stays open.
  defp expire_sudo_mode(view) do
    stale = DateTime.add(DateTime.utc_now(), -3, :hour)

    :sys.replace_state(view.pid, fn state ->
      account = %{state.socket.assigns.current_scope.account | authenticated_at: stale}
      put_in(state.socket.assigns.current_scope.account, account)
    end)
  end
end
