require 'active_support/concern'

module Pcasp::ValidaMovimentacoesConcern extend ActiveSupport::Concern

  attr_accessor :pcasp_valido, :erros_pcasp, :qtd_de_movimentacoes

  included do
    after_commit :valida_movimentacoes_pcasp, if: Proc.new { self.persisted? }
    after_destroy :limpar_erros
  end

  def valida_movimentacoes_pcasp
    self.limpar_erros

    self.pcasp_valido = true

    if self.class.name == "Contabilidade::TalaoDeReceita"
      if self.extra_orcamentario? || self.conta_extra_orcamentaria.present?
        self.valida_taloes_extra_orcamentarios
      elsif self.orcamentario?
        self.valida_taloes_de_receita_orcamentarios
      end
    elsif self.class.name == "Contabilidade::Empenho"
      self.valida_empenhos
    end

    self.gravar_erros

    return self.pcasp_valido
  end

  def gravar_erros
    if self.pcasp_valido == false
      LogMovimentacaoPcasp.create(
        gerador: self,
        qtd_total: self.qtd_de_movimentacoes,
        lista_de_erros: "[#{self.erros_pcasp.join(', ')}]",
        data_de_lancamento: self.extrair_data
      )
    end
  end

  def limpar_erros
    LogMovimentacaoPcasp.where(gerador: self).delete_all
  end

  def valida_empenhos
    self.erros_pcasp = []

    if self.movimentacoes_do_plano_de_contas.any? && ["aguardando_alteracao_do_orcamento", "solicitado", "enviado_para_controladoria", "enviado_para_copfin", "enviado_para_contabilidade"].include?(self.status)
      self.pcasp_valido = false
      self.erros_pcasp << "Não deve possuir movimentações"
    end

    if ["confirmado", "anulado"].include?(self.status)
      movimentacoes = self.movimentacoes_do_plano_de_contas
      self.qtd_de_movimentacoes = movimentacoes.size

      if self.qtd_de_movimentacoes > 0
        if movimentacoes.size % 2 != 0
          self.pcasp_valido = false
          self.erros_pcasp << "Tem lançamentos impares."
        end

        if movimentacoes.sum(:valor).to_d / self.qtd_de_movimentacoes != self.valor_total_do_empenho.to_d.abs
          self.pcasp_valido = false
          self.erros_pcasp << "Lançamento do PCASP não corresponde ao valor total"
        end

        contas_pcasp = movimentacoes.pluck(:codigo_da_conta)
        unless contas_pcasp.include?("622110000")
          self.pcasp_valido = false
          self.erros_pcasp << "Sem lançamento PCASP para a conta 622110000"
        end

        unless contas_pcasp.include?("522920101")
          self.pcasp_valido = false
          self.erros_pcasp << "Sem lançamento PCASP para a conta 522920101"
        end
        
        # validar a regra com o isaac
        if ['46907100', '46907200', '46907300', '46907400', '46907500', '46907600', '46907700', '33909100', '31909100', '44909100', '33906700'].include?(self.sub_elemento_de_despesa.elemento_de_despesa.codigo)
          unless self.movimentacoes_do_plano_de_contas.joins(conta_por_evento_contabil: [conta: :grupo_de_conta]).where("contabilidade_grupo_de_contas.codigo = 1").any?
            self.pcasp_valido = false
            self.erros_pcasp << "Sem lançamentos PCASP para conta patrimônial" # unless [1865, 34016, 31500, 34527, 41365, 41131, 41130, 41177, 41095, 41109, 41052, 41054, 41090, 41101, 30159, 41201, 41113, 41106, 34545, 41100, 34542, 41080, 41098, 41103, 34551, 41089, 41127, 41533, 41099, 41050, 41204, 41532, 41070, 34557, 41179, 41175, 41072, 34562, 41071, 41543, 41132, 41076, 41126, 41086, 41097, 41091, 41051, 41065, 41906, 44730, 44731, 45810, 45545, 46235, 46580, 46559, 46302, 46323, 46554, 46564, 45554, 45558, 46177].include?(self.id) # temporario para maracanau (2023) (empennhos anulados lançados sem subconta)
          end
        end

        if movimentacoes.pluck(:codigo_da_conta, :tipo_de_lancamento).group_by { |i| [i[0], i[1]] }.size != self.qtd_de_movimentacoes
          self.pcasp_valido = false
          self.erros_pcasp << "Possui contas lançadas em duplicidade."
        end
      else
        self.pcasp_valido = false
        self.erros_pcasp << "Não possui movimentações lançadas."
      end
    end
  end

  def valida_taloes_extra_orcamentarios
    self.erros_pcasp = []

    movimentacoes = self.movimentacoes_do_plano_de_contas
    self.qtd_de_movimentacoes = movimentacoes.size

    valor_no_banco = Contabilidade::MovimentacaoDaContaBancaria.where(modulo: self).all.sum(&:valor).abs

    if self.qtd_de_movimentacoes > 0
      if movimentacoes.size % 2 != 0
        self.pcasp_valido = false
        self.erros_pcasp << "Tem lançamentos impares."
      end

      if self.conta_bancaria_por_unidade_orcamentaria.present?
        if self.qtd_de_movimentacoes != 2 || (movimentacoes.sum(:valor).to_d / self.qtd_de_movimentacoes != self.valor.to_d.abs)
          self.pcasp_valido = false
          self.erros_pcasp << "Deve ter um par de movimentações"
        end

        if movimentacoes.pluck(:codigo_da_conta).count { |codigo| codigo.start_with?('11111') } != 1
          self.pcasp_valido = false
          self.erros_pcasp << "Deve haver um lançamento para a conta iniciada com '11111'"
        end

        if valor_no_banco.to_d != movimentacoes.pluck(:codigo_da_conta, :valor).select { |i| i[0].start_with?('11111') }.sum { |i| i[1] }.to_d
          self.pcasp_valido = false
          self.erros_pcasp << "O valor lançado no banco deve ser igual ao valor das movimentações iniciadas com '11111'"
        end
      end

      if self.pagamento_id.present? || self.retencao_id.present?
        if self.qtd_de_movimentacoes != 4 || (movimentacoes.sum(:valor).to_d / self.qtd_de_movimentacoes != self.valor.to_d.abs)
          self.pcasp_valido = false
          self.erros_pcasp << "Deve ter dois pares de movimentações"
        end

        if movimentacoes.pluck(:codigo_da_conta).count { |codigo| codigo.start_with?('11111') } != 2
          self.pcasp_valido = false
          self.erros_pcasp << "Deve haver dois lançamentos para as contas iniciadas com '11111'"
        end

        if valor_no_banco > 0
          self.pcasp_valido = false
          self.erros_pcasp << "O valor de pagamento no banco deve ser zero"
        end
      end

      if movimentacoes.pluck(:codigo_da_conta, :tipo_de_lancamento).group_by { |i| [i[0], i[1]] }.size != self.qtd_de_movimentacoes
        self.pcasp_valido = false
        self.erros_pcasp << "Possui contas lançadas em duplicidade."
      end
    else
      self.pcasp_valido = false
      self.erros_pcasp << "Não possui movimentações lançadas."
    end

  end

  def valida_taloes_de_receita_orcamentarios
    self.erros_pcasp = []

    movimentacoes = self.movimentacoes_do_plano_de_contas
    self.qtd_de_movimentacoes = movimentacoes.size

    valor_no_banco = Contabilidade::MovimentacaoDaContaBancaria.where(modulo: self).all.sum(&:valor).abs

    if self.qtd_de_movimentacoes > 0
      if movimentacoes.debito.sum(&:valor) != movimentacoes.credito.sum(&:valor)
        self.pcasp_valido = false
        self.erros_pcasp << "Total de Débitos e Créditos são divergentes."
      end

      if self.conta_bancaria_por_unidade_orcamentaria.present?
        unless movimentacoes.pluck(:codigo_da_conta).any? { |str| str.start_with?("11111") }
          self.pcasp_valido = false
          self.erros_pcasp << "Deve haver um lançamento para a conta iniciada com '11111'"
        end

        if valor_no_banco.to_d != movimentacoes.pluck(:codigo_da_conta, :valor).select { |i| i[0].start_with?('11111') }.sum { |i| i[1] }.to_d
          self.pcasp_valido = false
          self.erros_pcasp << "O valor lançado no banco deve ser igual ao valor das movimentações iniciadas com '11111'"
        end
      end

      contas_pcasp = movimentacoes.map { |i| i.codigo_da_conta }

      unless contas_pcasp.any? { |str| str.start_with?("6211") } || contas_pcasp.any? { |str| str.start_with?("6213") }
        erros_encontrados << "TALAO ORCAMENTARIO ID: #{self.id} - NUM: #{self.numero_do_talao} << SEM LANCAMENTO PCASP >> PARA A CONTA PCASP 6211_____ ou 6213_____"
      end

      self.complementos_por_fonte_do_talao_de_receita.each do |complemento|
        movs_incorretas = movimentacoes.select { |i| i.codigo_completo_fr == complemento.fonte_de_recursos.codigo_completo }.group_by { |m| [m.codigo_da_conta, m.tipo_de_lancamento] }.map { |_, movs| movs if movs.sum(&:valor).to_d != complemento.valor.abs}.compact
        if movs_incorretas.present?
          self.pcasp_valido = false
          self.erros_pcasp << "VALOR DIVERGENTE para o Complemento da fonte: #{complemento.fonte_de_recursos.codigo_completo} e seus Movimentos PCASP (DEV)"
        end
      end
    else
      self.pcasp_valido = false
      self.erros_pcasp << "Não possui movimentações lançadas."
    end
  end
end