class Contabilidade::LoteDeReceita < ApplicationRecord
  has_paper_trail

  validates_presence_of :data_de_arrecadacao, :convenio, :valor

  belongs_to :conta_bancaria, class_name: "Base::ContaBancaria"
  has_many :itens_do_lote_de_receita

  accepts_nested_attributes_for :itens_do_lote_de_receita#, reject_if: :all_blank
  validates_associated :itens_do_lote_de_receita, on: :create

  validate :valida_valor_total_dos_itens
  validate :valida_quantidade_de_itens
  validate :valida_conteudo
  validates_length_of :motivo, maximum: 255

  before_create :gerar_protocolo
  before_validation :ler_conteudo, on: :create
  after_validation :lanca_erro_dos_itens

  attr_default :status, :recebido

  enum status: {
    recebido: 1,
    aprovado: 2,
    rejeitado: 3,
    aprovado_parcialmente: 4
  }

  def gerar_protocolo
    self.protocolo = SecureRandom.uuid
  end

  def ler_conteudo
    orcamento = Orcamento.where(exercicio: data_de_arrecadacao.try(:year)).first
    if self.conteudo.present? && orcamento.present?
      itens = JSON.parse(self.conteudo)

      itens.each do |item|
        item["rubrica"] = item["rubrica"].to_s.gsub(".", "")
        item["rubrica"] = "00#{item["rubrica"]}000000" if item["rubrica"].to_s.size == 8 #ajuste para a forma como é cadastrado no sistema tributário intersol
        natureza_da_receita = Base::NaturezaDaReceita.where(modulo: orcamento, codigo: item["rubrica"]).first

        item_do_lote_de_receita = self.itens_do_lote_de_receita.build(
          natureza_da_receita_id: natureza_da_receita.try(:id),
          valor: item["valor"].to_f
        )
      end
    end
  end

  def valida_conteudo
    errros.add(:conteudo, "Conteúdo não informado") if self.conteudo.nil?
  end

  def valida_quantidade_de_itens
    errors.add(:base, "Nenhum item informado") if self.itens_do_lote_de_receita.size == 0
  end

  def valida_valor_total_dos_itens
    if self.itens_do_lote_de_receita.size > 0
      valor_total_dos_itens = self.itens_do_lote_de_receita.sum(&:valor).to_f

      if valor_total_dos_itens.to_f == 0
        errors.add(:base, "Valor total dos itens não pode ser zero")
      elsif valor_total_dos_itens.to_f > 0 && valor_total_dos_itens.to_f != valor.to_f
        errors.add(:base, "Valor total dos itens não pode ser diferente do valor informado nos parâmetros")
      end
    end
  end

  def lanca_erro_dos_itens
    self.itens_do_lote_de_receita.each do |item|
      unless item.valid?
        if item.errors[:natureza_da_receita_id].include? "não pode ficar em branco"
          errors.add(:base, "Receita no valor de #{item.valor} não localizada.")
        else
          errors.add(:base, "Preencha corretamente todos os itens")
        end
        break
      end
    end
  end

  def aprovar
    self.lancar_taloes_de_receita

    if self.itens_do_lote_de_receita.pluck(:status).uniq.include? "natureza_pertence_a_outra_unidade" # inclui o item aprovado_parcialmente
      self.update_attribute(:status, :aprovado_parcialmente)
    elsif self.itens_do_lote_de_receita.pluck(:status).uniq == ["lancado"] # somente um item aprovado
      self.update_attribute(:status, :aprovado)
    end
  end

  def lancar_taloes_de_receita
    orcamento = Orcamento.where(exercicio: data_de_arrecadacao.try(:year)).first
    pessoa = Base::Pessoa.diversos_contribuintes.first
    conta_bancaria_por_unidade_orcamentaria = self.conta_bancaria.contas_bancarias_por_unidade_orcamentaria.joins(unidade_orcamentaria: [ orgao: :orcamento]).where("principal is true and loa_orgaos.orcamento_id = ? ", orcamento.id).first
  
    if orcamento.present?
      self.itens_do_lote_de_receita.where("talao_de_receita_id is null or talao_de_receita_id = 0").each do |item|
        historico = item.natureza_da_receita.present? && item.natureza_da_receita.try(:historico_padrao).present? ? item.natureza_da_receita.try(:historico_padrao) : "Receita Tributária"
        talao_de_receita = Contabilidade::TalaoDeReceita.new(
          orcamento_id: orcamento.id,
          data_do_talao: self.data_de_arrecadacao,
          unidade_orcamentaria_id: conta_bancaria_por_unidade_orcamentaria.unidade_orcamentaria.id,
          natureza_da_receita_id: item.natureza_da_receita.id,
          valor: item.valor,
          pessoa_id: pessoa.try(:id),
          historico: historico,
          conta_bancaria_por_unidade_orcamentaria_id: conta_bancaria_por_unidade_orcamentaria.id,
          documento_de_credito: '0',
          tipo_de_documento: :outro_tipo_de_documento_de_credito,
          tipo_do_talao: :original,
          origem_do_talao: :orcamentario,
          lancamento_automatico_retencao: true,
          skip_callback: true,
          sub_conta_pcasp: item.natureza_da_receita.sub_conta_pcasp
        )
  
        unidade_por_natureza_da_receita = Loa::UnidadeOrcamentariaPorNaturezaDaReceita.find_by(
          natureza_da_receita_id: item.natureza_da_receita.id,
          unidade_orcamentaria_id: conta_bancaria_por_unidade_orcamentaria.unidade_orcamentaria.id
        )
  
        orcamentos_da_receita = []
  
        if unidade_por_natureza_da_receita
          orcamentos_da_receita = Loa::OrcamentoDaReceita.where(unidade_orcamentaria_por_natureza_da_receita_id: unidade_por_natureza_da_receita.id).order(:fonte_de_recursos_id)
        end
  
        orcamentos_da_receita.each do |orcamento_da_receita|
          percentual_da_fonte = orcamentos_da_receita.size == 1 ? 100 : (orcamento_da_receita.valor.to_f == 0 || orcamento_da_receita.valor_total.to_f == 0) ? orcamento_da_receita.fonte_de_recursos.percentual_minimo_de_destinacao.to_d : ((100 * orcamento_da_receita.valor) / orcamento_da_receita.valor_total)
  
          valor = orcamento_da_receita == orcamentos_da_receita.last ? (talao_de_receita.valor - talao_de_receita.complementos_por_fonte_do_talao_de_receita.sum(&:valor)) : (percentual_da_fonte * talao_de_receita.valor) / 100
  
          if orcamento_da_receita
            complemento = talao_de_receita.complementos_por_fonte_do_talao_de_receita.build(
              orcamento_da_receita: orcamento_da_receita,
              valor: valor,
              complementacao_da_fonte_de_recurso: nil
            )
          end
        end
  
        sobra_dos_complementos = talao_de_receita.valor.to_d - talao_de_receita.complementos_por_fonte_do_talao_de_receita.inject(0){|total, complemento_por_fonte| total + complemento_por_fonte.valor.to_d }
  
        if sobra_dos_complementos.positive?
          ultimo_complemento = talao_de_receita.complementos_por_fonte_do_talao_de_receita.last
  
          if ultimo_complemento
            ultimo_complemento.valor += sobra_dos_complementos
          else
            talao_de_receita.complementos_por_fonte_do_talao_de_receita.build(valor: sobra_dos_complementos)
          end
        end
  
        existe_natureza_da_receita_para_unidade = orcamento.naturezas_da_receita.joins(:unidades_orcamentarias_por_natureza_da_receita).where(base_naturezas_da_receita: { id: talao_de_receita.natureza_da_receita_id }, loa_unidades_orcamentarias_por_natureza_da_receita: { unidade_orcamentaria_id: talao_de_receita.unidade_orcamentaria_id }).exists?
  
        if existe_natureza_da_receita_para_unidade
          if talao_de_receita.save
            item.update_attributes(talao_de_receita_id: talao_de_receita.id, status: :lancado)

            # necessário pq o talão é salvo primeiro sem os complementos.
            talao_de_receita.skip_callback = false
            talao_de_receita.gerar_todos_os_movimentos(:data_do_talao, orcamento)
          end
        else
          item.update_attribute(:status, :natureza_pertence_a_outra_unidade)
        end
      end
    end
  end
end  

