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

  import Towerops.AccountsFixtures

  alias Towerops.Accounts

  describe "POST /api/webauthn/registration/challenge" do
    setup :register_and_log_in_user

    test "returns challenge for authenticated user with confirmed email", %{conn: conn, user: user} do
      conn = post(conn, ~p"/api/webauthn/registration/challenge")
      assert %{"challenge" => challenge, "user" => user_data} = json_response(conn, 200)
      assert is_binary(challenge)
      assert user_data["name"] == user.email
    end

    test "returns error for user without confirmed email" do
      # Create unconfirmed user
      user = unconfirmed_user_fixture()
      conn = log_in_user(build_conn(), user)

      conn = post(conn, ~p"/api/webauthn/registration/challenge")
      assert %{"error" => error} = json_response(conn, 403)
      assert error =~ "Email confirmation required"
    end

    test "requires authentication" do
      conn = build_conn()
      conn = post(conn, ~p"/api/webauthn/registration/challenge")
      assert redirected_to(conn) == ~p"/users/log-in"
    end
  end

  describe "POST /api/webauthn/register" do
    setup :register_and_log_in_user

    test "registers credential successfully", %{conn: conn} do
      # Set challenge in session
      conn = put_session(conn, :webauthn_challenge, "test_challenge_123")

      # Mock successful registration params
      params = %{
        "name" => "My Passkey",
        "rawId" => Base.url_encode64("credential_id", padding: false),
        "response" => %{
          "clientDataJSON" =>
            Base.url_encode64(~s({"type":"webauthn.create","challenge":"test_challenge_123"}), padding: false),
          "attestationObject" => Base.url_encode64("mock_attestation", padding: false)
        },
        "type" => "public-key"
      }

      conn = post(conn, ~p"/api/webauthn/register", params)

      # Should return error since WebAuthn verification will fail in test
      # But this tests the controller flow
      assert conn.status in [201, 422]
    end

    test "returns error when email not confirmed", %{conn: _conn} do
      # Create unconfirmed user
      unconfirmed_user = unconfirmed_user_fixture()
      conn = log_in_user(build_conn(), unconfirmed_user)
      conn = put_session(conn, :webauthn_challenge, "test_challenge")

      params = %{
        "name" => "My Passkey",
        "rawId" => Base.url_encode64("credential_id", padding: false)
      }

      conn = post(conn, ~p"/api/webauthn/register", params)
      assert %{"error" => error} = json_response(conn, 403)
      assert error =~ "Email confirmation required"
    end

    test "returns error on registration failure", %{conn: conn} do
      conn = put_session(conn, :webauthn_challenge, "test_challenge")

      # Invalid params
      params = %{"invalid" => "data"}

      conn = post(conn, ~p"/api/webauthn/register", params)
      assert %{"error" => _} = json_response(conn, 422)
    end

    test "requires authentication" do
      conn = build_conn()
      conn = post(conn, ~p"/api/webauthn/register", %{})
      assert redirected_to(conn) == ~p"/users/log-in"
    end
  end

  describe "POST /api/webauthn/authentication/challenge" do
    test "returns challenge for user with passkeys", %{conn: conn} do
      user = user_fixture()
      _credential = credential_fixture(user, %{name: "Test Key"})

      conn = post(conn, ~p"/api/webauthn/authentication/challenge", %{"email" => user.email})
      assert %{"challenge" => challenge} = json_response(conn, 200)
      assert is_binary(challenge)
    end

    test "returns challenge for discoverable authentication", %{conn: conn} do
      # No email provided - discoverable credential flow
      conn = post(conn, ~p"/api/webauthn/authentication/challenge", %{})
      assert %{"challenge" => challenge} = json_response(conn, 200)
      assert is_binary(challenge)
    end

    test "returns error for user without passkeys", %{conn: conn} do
      user = user_fixture()

      conn = post(conn, ~p"/api/webauthn/authentication/challenge", %{"email" => user.email})
      assert %{"error" => _} = json_response(conn, 404)
    end

    test "returns error for non-existent user", %{conn: conn} do
      conn =
        post(conn, ~p"/api/webauthn/authentication/challenge", %{
          "email" => "nonexistent@example.com"
        })

      assert %{"error" => _} = json_response(conn, 404)
    end

    test "does not require authentication", %{conn: conn} do
      user = user_fixture()
      conn = post(conn, ~p"/api/webauthn/authentication/challenge", %{"email" => user.email})
      # Should not redirect to login
      assert conn.status in [200, 404]
    end
  end

  describe "POST /api/webauthn/authenticate" do
    test "returns error when no challenge in session" do
      conn = build_conn()
      conn = post(conn, ~p"/api/webauthn/authenticate", %{})
      assert %{"error" => _} = json_response(conn, 401)
    end
  end

  describe "DELETE /users/credentials/:id" do
    setup :register_and_log_in_user

    test "deletes credential successfully", %{conn: conn, user: user} do
      # Create a test credential
      credential = credential_fixture(user, %{name: "Test Key"})

      conn = delete(conn, ~p"/users/credentials/#{credential.id}")
      assert redirected_to(conn) == ~p"/users/settings"
      assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "deleted successfully"

      # Verify credential is deleted
      assert Accounts.list_user_credentials(user.id) == []
    end

    test "cannot delete another user's credential", %{conn: conn} do
      other_user = user_fixture()
      credential = credential_fixture(other_user, %{name: "Other Key"})

      conn = delete(conn, ~p"/users/credentials/#{credential.id}")
      assert redirected_to(conn) == ~p"/users/settings"
      assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "not found"

      # Verify credential still exists
      assert length(Accounts.list_user_credentials(other_user.id)) == 1
    end

    test "requires authentication" do
      conn = build_conn()
      conn = delete(conn, ~p"/users/credentials/#{Ecto.UUID.generate()}")
      assert redirected_to(conn) == ~p"/users/log-in"
    end
  end
end
