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

  import Towerops.AccountsFixtures

  alias Towerops.Accounts

  setup do
    # Create user with TOTP enabled (required for sudo mode)
    user = user_fixture(enable_totp: true)
    %{user: user}
  end

  describe "GET /users/sudo-verify" do
    test "renders sudo verification page for authenticated user", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> get(~p"/users/sudo-verify")

      response = html_response(conn, 200)
      assert response =~ "Re-authenticate"
      assert response =~ "Enter the 6-digit code from your authenticator app"
    end

    test "redirects unauthenticated users to login", %{conn: conn} do
      conn = get(conn, ~p"/users/sudo-verify")
      assert redirected_to(conn) == ~p"/users/log-in"
      assert Phoenix.Flash.get(conn.assigns.flash, :error) == "You must log in to access this page."
    end

    test "preserves return_to path from session", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> init_test_session(user_return_to: "/users/settings")
        |> get(~p"/users/sudo-verify")

      assert html_response(conn, 200) =~ "Re-authenticate"
      assert get_session(conn, :user_return_to) == "/users/settings"
    end

    test "redirects to destination if already in sudo mode", %{conn: conn, user: user} do
      # Grant sudo mode first
      {:ok, _user} = Accounts.grant_sudo_mode(user)

      conn =
        conn
        |> log_in_user(user)
        |> init_test_session(user_return_to: "/users/settings")
        |> get(~p"/users/sudo-verify")

      assert redirected_to(conn) == "/users/settings"
      assert get_session(conn, :user_return_to) == nil
    end

    test "redirects to /users/settings if already in sudo mode and no return_to", %{
      conn: conn,
      user: user
    } do
      # Grant sudo mode first
      {:ok, _user} = Accounts.grant_sudo_mode(user)

      conn =
        conn
        |> log_in_user(user)
        |> get(~p"/users/sudo-verify")

      assert redirected_to(conn) == ~p"/users/settings"
    end

    test "redirects to TOTP enrollment if user has no TOTP", %{conn: conn} do
      # Create user without TOTP
      user_no_totp = user_fixture(enable_totp: false)

      conn =
        conn
        |> log_in_user(user_no_totp)
        |> get(~p"/users/sudo-verify")

      assert redirected_to(conn) == ~p"/account/totp-enrollment"

      assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
               "Two-factor authentication is required for this action."
    end
  end

  describe "POST /users/sudo-verify" do
    test "grants sudo mode and redirects on valid TOTP code", %{conn: conn, user: user} do
      # Get valid TOTP code for user
      code = NimbleTOTP.verification_code(user.totp_secret)

      conn =
        conn
        |> log_in_user(user)
        |> init_test_session(user_return_to: "/users/settings")
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => code}})

      # Should redirect to return_to path (implies sudo mode was granted)
      assert redirected_to(conn) == "/users/settings"

      # Should show success flash message
      assert Phoenix.Flash.get(conn.assigns.flash, :info) == "Identity verified."

      # Should clear return_to from session
      assert get_session(conn, :user_return_to) == nil

      # Should update last_sudo_at timestamp
      updated_user = Accounts.get_user!(user.id)
      assert updated_user.last_sudo_at
      assert DateTime.diff(updated_user.last_sudo_at, DateTime.utc_now(), :second) <= 1
    end

    test "grants sudo mode and redirects to /users/settings if no return_to", %{
      conn: conn,
      user: user
    } do
      code = NimbleTOTP.verification_code(user.totp_secret)

      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => code}})

      # Should redirect to /users/settings (default)
      assert redirected_to(conn) == ~p"/users/settings"

      # Should show success flash message
      assert Phoenix.Flash.get(conn.assigns.flash, :info) == "Identity verified."

      # Should update last_sudo_at timestamp
      updated_user = Accounts.get_user!(user.id)
      assert updated_user.last_sudo_at
      assert DateTime.diff(updated_user.last_sudo_at, DateTime.utc_now(), :second) <= 1
    end

    test "shows error on invalid TOTP code", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => "000000"}})

      response = html_response(conn, 200)
      assert response =~ "Invalid authentication code"
      assert response =~ "Re-authenticate"

      # Should not grant sudo mode
      user = Accounts.get_user!(user.id)
      refute Accounts.sudo_mode?(user)
    end

    test "shows error when recovery code is attempted", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => "ABCD-EFGH"}})

      response = html_response(conn, 200)
      assert response =~ "Recovery codes are not allowed for sudo mode verification"
      assert response =~ "Re-authenticate"

      # Should not grant sudo mode
      user = Accounts.get_user!(user.id)
      refute Accounts.sudo_mode?(user)
    end

    test "shows error when TOTP code is too long", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => "1234567"}})

      response = html_response(conn, 200)
      assert response =~ "Recovery codes are not allowed for sudo mode verification"
      assert response =~ "Re-authenticate"
    end

    test "shows error when TOTP code contains letters", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => "12AB34"}})

      response = html_response(conn, 200)
      assert response =~ "Recovery codes are not allowed for sudo mode verification"
      assert response =~ "Re-authenticate"
    end

    test "redirects unauthenticated users to login", %{conn: conn} do
      conn = post(conn, ~p"/users/sudo-verify", %{"user" => %{"totp_code" => "123456"}})
      assert redirected_to(conn) == ~p"/users/log-in"

      assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
               "You must log in to access this page."
    end

    test "preserves return_to path on verification failure", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> init_test_session(user_return_to: "/users/settings")
        |> post(~p"/users/sudo-verify", %{"user" => %{"totp_code" => "000000"}})

      assert html_response(conn, 200) =~ "Invalid authentication code"
      # return_to should be preserved for next attempt
      assert get_session(conn, :user_return_to) == "/users/settings"
    end

    test "handles missing TOTP code parameter", %{conn: conn, user: user} do
      conn =
        conn
        |> log_in_user(user)
        |> post(~p"/users/sudo-verify", %{"user" => %{}})

      response = html_response(conn, 200)
      assert response =~ "Re-authenticate"
      # Should render form again (no crash)
    end
  end
end
