lib/bacen/ccs/accs002.ex

defmodule Bacen.CCS.ACCS002 do
  @moduledoc """
  The ACCS002 message.

  This message is a response from ACCS001 message.

  It has the following XML example:

  ```xml
  <CCSArqAtlzDiariaRespArq>
    <SitArq>R</SitArq>
    <ErroCCS>ECCS0023</ErroCCS>
    <UltNumRemessaArq>000000000000</UltNumRemessaArq>
    <DtHrBC>2004-06-16T05:04:00</DtHrBC>
    <DtMovto>2004-06-16</DtMovto>
  </CCSArqAtlzDiariaRespArq>
  ```

  """
  use Ecto.Schema

  import Ecto.Changeset

  @typedoc """
  The ACCS002 message type
  """
  @type t :: %__MODULE__{}

  @response_fields ~w(last_file_id status error reference_date movement_date)a
  @response_required_fields ~w(last_file_id status reference_date movement_date)a
  @response_fields_source_sequence ~w(SitArq ErroCCS UltNumRemessaArq DtHrBC DtMovto)a

  @allowed_status ~w(R A)

  @primary_key false
  embedded_schema do
    embeds_one :response, Response, source: :CCSArqAtlzDiariaRespArq, primary_key: false do
      field :last_file_id, :string, source: :UltNumRemessaArq
      field :status, :string, source: :SitArq
      field :error, :string, source: :ErroCCS
      field :reference_date, :utc_datetime, source: :DtHrBC
      field :movement_date, :date, source: :DtMovto
    end
  end

  @doc """
  Creates a new ACCS002 message from given attributes.
  """
  @spec new(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
  def new(attrs) when is_map(attrs) do
    attrs
    |> changeset()
    |> apply_action(:insert)
  end

  @doc false
  def changeset(accs002 \\ %__MODULE__{}, attrs) when is_map(attrs) do
    accs002
    |> cast(attrs, [])
    |> cast_embed(:response, with: &response_changeset/2, required: true)
  end

  @doc false
  def response_changeset(response, attrs) when is_map(attrs) do
    response
    |> cast(attrs, @response_fields)
    |> validate_required(@response_required_fields)
    |> validate_inclusion(:status, @allowed_status)
    |> validate_length(:status, is: 1)
    |> validate_length(:last_file_id, is: 12)
    |> validate_format(:last_file_id, ~r/[0-9]{12}/)
    |> validate_by_status()
  end

  defp validate_by_status(changeset) do
    case get_field(changeset, :status) do
      "R" ->
        changeset
        |> validate_required([:error])
        |> validate_length(:error, is: 8)
        |> validate_format(:error, ~r/E[A-Z]{3}[0-9]{4}/)

      _ ->
        changeset
    end
  end

  @doc """
  Returns the field sequence for given root xml element

  ## Examples

      iex> Bacen.CCS.ACCS002.sequence(:CCSArqAtlzDiariaRespArq)
      [:SitArq, :ErroCCS, :UltNumRemessaArq, :DtHrBC, :DtMovto]

  """
  @spec sequence(:CCSArqAtlzDiariaRespArq) :: list(atom())
  def sequence(:CCSArqAtlzDiariaRespArq), do: @response_fields_source_sequence
end