app/validators/details/chunk_footer.validator.ex

defmodule ExCnab.Cnab240.Validator.Details.ChunkFooter do
  @moduledoc """
  An implementation of chain of responsibility to validate the chunk footer.
  """

  @spec call(Map.t(), String.t(), Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
  def call(builded_footer, raw_footer, builded_details) do
    with :ok <- validate_length(raw_footer),
         :ok <- validate_record_type(builded_footer.controle.registro),
         :ok <- validate_value_amount(builded_footer, builded_details) do
      {:ok, builded_footer}
    else
      {:error, reason} ->
        {:error, reason, raw_footer}
    end
  end

  @cnab_size 240
  defp validate_length(raw_footer) do
    case String.length(raw_footer) do
      @cnab_size ->
        :ok

      number ->
        {:error, "Tamanho inválido para o footer: #{number}, deveria ser #{@cnab_size}"}
    end
  end

  @record_type "5"
  defp validate_record_type(record_type) do
    case record_type == @record_type do
      true ->
        :ok

      false ->
        {:error, "Tipo de registro incorreto: #{record_type}, deveria ser #{@record_type}}"}
    end
  end

  defp validate_value_amount(builded_footer, builded_details) do
    details_value = ExCnab.Cnab240.Services.CalcAmount.run(builded_details)
    footer_value = String.to_integer(builded_footer.total.valor)

    case details_value == footer_value do
      true ->
        :ok

      false ->
        {:error, "Valor total do lote incorreto: #{footer_value}, deveria ser #{details_value}"}
    end
  end
end