# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT

defmodule AshAuthentication.Strategy.Password.ResetTokenValidation do
  @moduledoc """
  Validate that the token is a valid password reset request token.
  """

  use Ash.Resource.Validation
  alias Ash.{Changeset, Error.Changes.InvalidArgument, Resource.Validation}
  alias AshAuthentication.{Info, Jwt}

  @doc false
  @impl true
  @spec validate(Changeset.t(), keyword, Validation.Context.t()) :: :ok | {:error, Exception.t()}
  def validate(changeset, _, context) do
    with {:ok, strategy} <- Info.strategy_for_action(changeset.resource, changeset.action.name),
         token when is_binary(token) <- Changeset.get_argument(changeset, :reset_token),
         {:ok, %{"act" => token_action}, _} <-
           Jwt.verify(token, changeset.resource, Ash.Context.to_opts(context)),
         {:ok, resettable} <- Map.fetch(strategy, :resettable),
         true <- to_string(resettable.password_reset_action_name) == token_action do
      :ok
    else
      _ ->
        {:error, InvalidArgument.exception(field: :reset_token, message: "is not valid")}
    end
  end

  @impl true
  def atomic(changeset, opts, context) do
    validate(changeset, opts, context)
  end
end
