app/excnab/cnab240/templates/details/model_n.ex

defmodule ExCnab.Cnab240.Templates.Details.ModelN do
  @moduledoc """
  Template to generate an cnab 240 object from file, following the N-segment pattern;
  """
  import Helpers.ConvertPosition

  @doc """
  This function generate a Header Chunk Map from a Cnab240 file, using the **FEBRABAN** convention. The fields follows the O-segment hierarchy,
  and the returns is in PT BR.

  You can see all hierarchy in the FEBRABAN documentation.

  ```md
  CNAB
  ├── Controle
  │   ├── Banco (1..3)
  │   ├── Lote (4..7)
  │   └── Registro (8..8)
  │
  ├── Serviço
  │   ├── N do registro (9..13)
  │   ├── Segmento (14..14)
  │   └── Movimento
  │         ├── Tipo (15..15)
  │         └── Código (16..17)
  │
  ├── Pagamento
  │   ├── Seu numero (18..37)
  │   │
  │   ├── Nosso numero (38..57)
  │   │
  │   ├── Contribuinte (58..57)
  │   │
  │   ├── Data pagamento (88..95)
  │   │
  │   └── Valor pagamento (96..110)
  │
  ├── Informacoes complementares (111..230)
  │
  └── Ocorrências (231..240)
  """
  alias ExCnab.Cnab240.Validator.Details.ModelN, as: ModelNValidator

  @spec generate(String.t()) :: {:ok, Map.t()}
  def generate(raw_string) do
    control_field = control_field(raw_string)
    service_field = service_field(raw_string)
    payment_field = payment_field(raw_string)

    complementary_info =
      raw_string
      |> convert_position(133, 134)
      |> complementary_info_fields(raw_string)

    %{
      controle: control_field,
      servico: service_field,
      pagamento: payment_field,
      informacoes_complementares: complementary_info,
      ocorrencias: convert_position(raw_string, 231, 240)
    }
    |> ModelNValidator.call(raw_string)
  end

  defp control_field(raw_string) do
    %{
      banco: convert_position(raw_string, 1, 3),
      lote: convert_position(raw_string, 4, 7),
      registro: convert_position(raw_string, 8)
    }
  end

  defp service_field(raw_string) do
    %{
      n_registro: convert_position(raw_string, 9, 13),
      segmento: convert_position(raw_string, 14),
      movimento: %{
        tipo: convert_position(raw_string, 15),
        codigo: convert_position(raw_string, 16, 17)
      }
    }
  end

  defp payment_field(raw_string) do
    %{
      seu_numero: convert_position(raw_string, 18, 37),
      nosso_numero: convert_position(raw_string, 38, 57),
      contribuinte: convert_position(raw_string, 58, 87),
      data_pagamento: convert_position(raw_string, 88, 95),
      valor_pagamento: convert_position(raw_string, 96, 110)
    }
  end

  # DARF
  defp complementary_info_fields("16", raw_string) do
    %{
      receita: convert_position(raw_string, 111, 116),
      tipo_id_contribuinte: convert_position(raw_string, 117, 118),
      id_contribuinte: convert_position(raw_string, 119, 132),
      id_tributo: convert_position(raw_string, 133, 134),
      periodo: convert_position(raw_string, 135, 142),
      referencia: convert_position(raw_string, 143, 159),
      valor_principal: convert_position(raw_string, 160, 174),
      valor_multa: convert_position(raw_string, 175, 189),
      juros_e_encargos: convert_position(raw_string, 190, 204),
      data_vencimento: convert_position(raw_string, 205, 212),
      cnab: convert_position(raw_string, 213, 230)
    }
  end

  # GPS
  defp complementary_info_fields("17", raw_string) do
    %{
      receita: convert_position(raw_string, 111, 116),
      tipo_id_contribuinte: convert_position(raw_string, 117, 118),
      id_contribuinte: convert_position(raw_string, 119, 132),
      id_tributo: convert_position(raw_string, 133, 134),
      competencia: convert_position(raw_string, 135, 140),
      valor_tributo: convert_position(raw_string, 141, 155),
      valor_outras_entidades: convert_position(raw_string, 156, 170),
      atualizacao_monetaria: convert_position(raw_string, 171, 185),
      cnab: convert_position(raw_string, 186, 230)
    }
  end

  # DARF SIMPLES
  defp complementary_info_fields("18", raw_string) do
    %{
      receita: convert_position(raw_string, 111, 116),
      tipo_id_contribuinte: convert_position(raw_string, 117, 118),
      id_contribuinte: convert_position(raw_string, 119, 132),
      id_tributo: convert_position(raw_string, 133, 134),
      periodo: convert_position(raw_string, 135, 142),
      receita_bruta: convert_position(raw_string, 143, 157),
      percentual: convert_position(raw_string, 158, 164),
      valor_principal: convert_position(raw_string, 165, 179),
      valor_multa: convert_position(raw_string, 180, 194),
      juros_e_encargos: convert_position(raw_string, 195, 209),
      cnab: convert_position(raw_string, 210, 230)
    }
  end

  def encode(detail) do
    complementary_info =
      encode_complementary_info(detail.informacoes_complementares.id_tributo, detail)

    %{
      controle: %{
        banco: banco,
        lote: lote,
        registro: registro
      },
      servico: %{
        n_registro: n_registro,
        segmento: segmento,
        movimento: %{
          tipo: tipo_movimento,
          codigo: codigo_movimento
        }
      },
      pagamento: %{
        seu_numero: seu_numero,
        nosso_numero: nosso_numero,
        contribuinte: contribuinte,
        data_pagamento: data_pagamento,
        valor_pagamento: valor_pagamento
      },
      ocorrencias: ocorrencias
    } = detail

    [
      banco,
      lote,
      registro,
      n_registro,
      segmento,
      tipo_movimento,
      codigo_movimento,
      seu_numero,
      nosso_numero,
      contribuinte,
      data_pagamento,
      valor_pagamento,
      complementary_info,
      ocorrencias
    ]
    |> Enum.join()
  end

  defp encode_complementary_info("16", detail) do
    %{
      receita: receita,
      tipo_id_contribuinte: tipo_id_contribuinte,
      id_contribuinte: id_contribuinte,
      id_tributo: id_tributo,
      periodo: periodo,
      referencia: referencia,
      valor_principal: valor_principal,
      valor_multa: valor_multa,
      juros_e_encargos: juros_e_encargos,
      data_vencimento: data_vencimento,
      cnab: cnab
    } = detail

    [
      receita,
      tipo_id_contribuinte,
      id_contribuinte,
      id_tributo,
      periodo,
      referencia,
      valor_principal,
      valor_multa,
      juros_e_encargos,
      data_vencimento,
      cnab
    ]
    |> Enum.join()
  end

  defp encode_complementary_info("17", detail) do
    %{
      receita: receita,
      tipo_id_contribuinte: tipo_id_contribuinte,
      id_contribuinte: id_contribuinte,
      id_tributo: id_tributo,
      competencia: competencia,
      valor_tributo: valor_tributo,
      valor_outras_entidades: valor_outras_entidades,
      atualizacao_monetaria: atualizacao_monetaria,
      cnab: cnab
    } = detail

    [
      receita,
      tipo_id_contribuinte,
      id_contribuinte,
      id_tributo,
      competencia,
      valor_tributo,
      valor_outras_entidades,
      atualizacao_monetaria,
      cnab
    ]
    |> Enum.join()
  end

  defp encode_complementary_info("18", detail) do
    %{
      receita: receita,
      tipo_id_contribuinte: tipo_id_contribuinte,
      id_contribuinte: id_contribuinte,
      id_tributo: id_tributo,
      periodo: periodo,
      receita_bruta: receita_bruta,
      percentual: percentual,
      valor_principal: valor_principal,
      valor_multa: valor_multa,
      juros_e_encargos: juros_e_encargos,
      cnab: cnab
    } = detail

    [
      receita,
      tipo_id_contribuinte,
      id_contribuinte,
      id_tributo,
      periodo,
      receita_bruta,
      percentual,
      valor_principal,
      valor_multa,
      juros_e_encargos,
      cnab
    ]
    |> Enum.join()
  end
end