defmodule PhilomenaMedia.Sha512 do
  @moduledoc """
  Streaming SHA-512 processor.
  """

  @chunk_size 10_485_760

  @doc """
  Generate the SHA2-512 hash of the file at the given path as a string.

  The file is processed in 10MiB chunks.

  ## Example

      iex> Sha512.file("image.png")
      "97fd5243cd39e225f1478097acae71fbbff7f3027b24f0e6a8e06a0d7d3e6861cd05691d7470c76e7dfc4eb30459a906918d5ba0d144184fff02b8e34bd9ecf8"

  """
  @spec file(Path.t()) :: String.t()
  def file(path) do
    hash_ref = :crypto.hash_init(:sha512)

    path
    |> stream_file()
    |> Enum.reduce(hash_ref, &:crypto.hash_update(&2, &1))
    |> :crypto.hash_final()
    |> Base.encode16(case: :lower)
  end

  if Version.match?(System.version(), ">= 1.16.0") do
    # `stream!/2` was added in Elixir 1.16 to accept a shortened form,
    # where we only need to specify the size of each stream chunk
    defp stream_file(file) do
      File.stream!(file, @chunk_size)
    end
  else
    # Use legacy stream/3 for older Elixir versions
    defp stream_file(file) do
      File.stream!(file, [], @chunk_size)
    end
  end
end