defmodule Mobilizon.Users.PushSubscription do
  @moduledoc """
  Represents informations about a push subscription for a specific user
  """
  use Ecto.Schema
  alias Mobilizon.Users.User
  import Ecto.Changeset

  @type t :: %__MODULE__{
          digest: String.t(),
          user: User.t(),
          endpoint: String.t(),
          auth: String.t(),
          p256dh: String.t()
        }

  @primary_key {:id, :binary_id, autogenerate: true}
  schema "user_push_subscriptions" do
    field(:digest, :string)
    belongs_to(:user, User)
    field(:endpoint, :string)
    field(:auth, :string)
    field(:p256dh, :string)
    timestamps()
  end

  @doc false
  @spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
  def changeset(push_subscription, attrs) do
    push_subscription
    |> cast(attrs, [:user_id, :endpoint, :auth, :p256dh])
    |> put_change(:digest, compute_digest(attrs))
    |> validate_required([:digest, :user_id, :endpoint, :auth, :p256dh])
    |> unique_constraint([:digest, :user_id], name: :user_push_subscriptions_user_id_digest_index)
  end

  @spec compute_digest(map()) :: String.t()
  defp compute_digest(attrs) do
    data =
      Jason.encode!(%{endpoint: attrs.endpoint, keys: %{auth: attrs.auth, p256dh: attrs.p256dh}})

    :sha256
    |> :crypto.hash(data)
    |> Base.encode16()
  end
end