defmodule <%= inspect @app_pascal_case %>.Identity.User do
  @moduledoc """
  Schema representing a user of the application.

  ## Considerations

  - Ecto.ULID is used to prevent user enumeration attacks while maintaining sortability.
    - Spec: https://github.com/ulid/spec
    - Context: https://www.honeybadger.io/blog/uuids-and-ulids/
    - UUIDv7 may provide the same functionality, but it is not fully supported as of 09/2023.
  - Email confirmation is not implemented.
  """
  use Ecto.Schema
  import Ecto.Changeset
  alias <%= inspect @app_pascal_case %>.Identity.UserKey
  alias <%= inspect @app_pascal_case %>.Identity.UserToken

  @type t :: %__MODULE__{
          id: binary(),
          email: String.t(),
          inserted_at: NaiveDateTime.t(),
          updated_at: NaiveDateTime.t()
        }

  @primary_key {:id, Ecto.ULID, autogenerate: true}
  @foreign_key_type Ecto.ULID
  schema "users" do
    field :email, :string
    has_many :keys, UserKey, preload_order: [desc: :last_used_at]
    has_many :tokens, UserToken, preload_order: [desc: :inserted_at]

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    fields = __MODULE__.__schema__(:fields)

    user
    |> cast(attrs, fields)
    |> validate_required([:email])
    |> validate_length(:email, min: 6, max: 120)
    |> validate_format(:email, ~r/@/)
    |> update_change(:email, &String.downcase/1)
    |> unique_constraint(:email)
    |> cast_assoc(:keys)
    |> cast_assoc(:tokens)
  end
end
