defmodule Towerops.AccountsFixtures do
  @moduledoc """
  This module defines test helpers for creating
  entities via the `Towerops.Accounts` context.
  """

  import Ecto.Query

  alias Towerops.Accounts
  alias Towerops.Accounts.Scope

  def unique_user_email, do: "user#{System.unique_integer()}@example.com"
  def valid_user_password, do: "hello world!"

  def valid_user_attributes(attrs \\ %{}) do
    Enum.into(attrs, %{
      email: unique_user_email(),
      password: valid_user_password(),
      privacy_policy_consent: true,
      terms_of_service_consent: true
    })
  end

  def unconfirmed_user_fixture(attrs \\ %{}) do
    {:ok, user} =
      attrs
      |> valid_user_attributes()
      |> Accounts.register_user()

    # Set confirmed_at to nil for testing unconfirmed user flows
    user
    |> Ecto.Changeset.change(confirmed_at: nil)
    |> Towerops.Repo.update!()
  end

  def user_fixture(attrs \\ %{}) do
    # Extract enable_totp option before passing to register_user
    enable_totp = get_option(attrs, :enable_totp, true)

    {:ok, user} =
      attrs
      |> remove_option(:enable_totp)
      |> valid_user_attributes()
      |> Accounts.register_user()

    # Enable TOTP by default for all test users
    # Tests can explicitly pass enable_totp: false to skip this
    if enable_totp do
      secret = Accounts.generate_totp_secret()
      {:ok, user} = Accounts.enable_totp(user, secret)
      user
    else
      user
    end
  end

  defp get_option(attrs, key, default) when is_map(attrs), do: Map.get(attrs, key, default)
  defp get_option(attrs, key, default) when is_list(attrs), do: Keyword.get(attrs, key, default)

  defp remove_option(attrs, key) when is_map(attrs), do: Map.delete(attrs, key)
  defp remove_option(attrs, key) when is_list(attrs), do: Keyword.delete(attrs, key)

  def user_scope_fixture do
    user = user_fixture()
    user_scope_fixture(user)
  end

  def user_scope_fixture(user) do
    Scope.for_user(user)
  end

  def set_password(user) do
    {:ok, {user, _expired_tokens}} =
      Accounts.update_user_password(user, %{password: valid_user_password()})

    user
  end

  def extract_user_token(fun) do
    {:ok, captured_email} = fun.(&"[TOKEN]#{&1}[TOKEN]")
    [_, token | _] = String.split(captured_email.text_body, "[TOKEN]")
    token
  end

  def override_token_authenticated_at(token, authenticated_at) when is_binary(token) do
    Towerops.Repo.update_all(
      from(t in Accounts.UserToken,
        where: t.token == ^token
      ),
      set: [authenticated_at: authenticated_at]
    )
  end

  def generate_user_magic_link_token(user) do
    {encoded_token, user_token} = Accounts.UserToken.build_email_token(user, "login")
    Towerops.Repo.insert!(user_token)
    {encoded_token, user_token.token}
  end

  def offset_user_token(token, amount_to_add, unit) do
    dt = DateTime.add(DateTime.utc_now(:second), amount_to_add, unit)

    Towerops.Repo.update_all(
      from(ut in Accounts.UserToken, where: ut.token == ^token),
      set: [inserted_at: dt, authenticated_at: dt]
    )
  end
end
