require 'active_support/concern'

module ReversorDeEventosContabeis extend ActiveSupport::Concern

	attr_accessor :evento_contabil_padrao

	attr_accessor :eventos_contabeis

	module ClassMethods
		attr_reader :acao, :atributo_pai, :atributo_data, :atributo_codigo_movimentacao

		private
		def reversor_de_eventos_contabeis(params={})
			@codigo_da_acao = params[:acao]
			@atributo_pai   = params[:atributo_pai]
			@atributo_data  = params[:atributo_data]
			@atributo_codigo_movimentacao = params[:atributo_codigo_movimentacao]
		end
	end

	included do
		has_many :eventos_contabeis_por_gerador, as: :gerador, dependent: :destroy
		has_many :movimentacoes_do_plano_de_contas, as: :gerador, dependent: :destroy

		before_validation :atribui_evento_contabil_padrao
		before_validation :atribui_eventos_contabeis

		after_save :cria_eventos_contabeis_por_gerador, if: Proc.new{ |objeto| pode_gerar_movimentacao? }
		after_save :gera_movimentacao, if: Proc.new{ |objeto| pode_gerar_movimentacao? }

		def pode_gerar_movimentacao?
			((self.has_attribute?('status') && self.confirmado?) || !self.has_attribute?('status'))
		end

		def self.acao_do_sistema
			Contabilidade::AcaoDoSistema.find_by(codigo: @codigo_da_acao)
		end

		def atribui_evento_contabil_padrao
			if evento_contabil_padrao.nil? && self.class.acao_do_sistema.present? && self.send(self.class.atributo_pai).orcamento.present?
				orcamento = self.send(self.class.atributo_pai).orcamento
				evento = self.class.acao_do_sistema.eventos_contabeis.find_by(orcamento_id: orcamento.id, padrao: true)
				if evento.present?
					self.evento_contabil_padrao = evento
				end
			end
		end

		def atribui_eventos_contabeis
			if self.eventos_contabeis.blank? && self.class.acao_do_sistema.present? && self.send(self.class.atributo_pai).orcamento.present?
				self.eventos_contabeis = []
				orcamento = self.send(self.class.atributo_pai).orcamento
				if self.class.acao_do_sistema.codigo == 6
					self.eventos_contabeis << pesquisa_padrao_por_nome_e_classe_sem_dotacao('CONTROLES DISPONIBILIDADE DE CRÉDITO E EMISSÃO DE EMPENHO (ANULAÇÃO DE LIQUIDAÇÃO)', 40)
				end

				if self.class.acao_do_sistema.codigo == 7
					self.eventos_contabeis << pesquisa_padrao_por_nome_e_classe_sem_dotacao('CONTROLES DISPONIBILIDADE DE CRÉDITO E EMISSÃO DE EMPENHO (ANULAÇÃO DE PAGAMENTO)', 40)
				end

				if self.class.acao_do_sistema.codigo == 5 # Anulação de Empenho
					if self.criado_na_contabilidade
						self.eventos_contabeis << pesquisa_padrao_por_nome_e_classe_sem_dotacao('CONTROLES DISPONIBILIDADE DE CRÉDITO E EMISSÃO DE EMPENHO (ANULAÇÃO DE EMPENHO SEM  A FASE DE PRÉ EMPENHO)', 40)
					end
					if self.empenho.pessoa.pessoa_juridica? && (self.empenho.elemento_de_despesa.codigo == "33903500" || self.empenho.elemento_de_despesa.codigo == "33903300")
						evento = self.empenho.class.acao_do_sistema.eventos_contabeis.automatico.pessoa_juridica.where(classe: [40, 51, 52], orcamento_id: self.empenho.orcamento.id, padrao: false).
							joins(:orcamentos_da_despesa_por_evento_contabil).where(contabilidade_orcamentos_da_despesa_por_evento_contabil: { sub_elemento_de_despesa_id: sub_elemento_de_despesa.id }).first
							self.eventos_contabeis << evento if evento.present?
					elsif self.empenho.pessoa.pessoa_fisica? && (self.empenho.elemento_de_despesa.codigo == "33903500" || self.empenho.elemento_de_despesa.codigo == "33903300")
						evento = self.empenho.class.acao_do_sistema.eventos_contabeis.automatico.pessoa_fisica.where(classe: [40, 51, 52], orcamento_id: self.empenho.orcamento.id, padrao: false).
							joins(:orcamentos_da_despesa_por_evento_contabil).where(contabilidade_orcamentos_da_despesa_por_evento_contabil: { sub_elemento_de_despesa_id: sub_elemento_de_despesa.id }).first
						self.eventos_contabeis << evento if evento.present?
					else
						evento = self.class.acao_do_sistema.eventos_contabeis.automatico.where(orcamento_id: orcamento.id, padrao: false).first
						self.eventos_contabeis << evento if evento.present?
					end
				elsif classificacao_orcamentaria_despesa? && !defined?(sub_elemento_de_despesa).nil? && sub_elemento_de_despesa.present?
					evento = self.class.acao_do_sistema.eventos_contabeis.automatico.where(classe: [40,51], orcamento_id: orcamento.id, padrao: false).
						joins(:orcamentos_da_despesa_por_evento_contabil).where(contabilidade_orcamentos_da_despesa_por_evento_contabil: { sub_elemento_de_despesa_id: sub_elemento_de_despesa.id }).first
					self.eventos_contabeis << evento if evento.present?
				elsif classificacao_orcamentaria_receita? && !defined?(receita_stn).nil? && receita_stn.present?
					evento = self.class.acao_do_sistema.eventos_contabeis.automatico.execucao_orcamentaria_da_receita.where(orcamento_id: orcamento.id, padrao: false).
						joins(:orcamentos_da_receita_por_evento_contabil).where(contabilidade_orcamentos_da_receita_por_evento_contabil: { receita_stn_id: receita_stn.id }).first
					self.eventos_contabeis << evento if evento.present?
				end
			end
		end

		def cria_eventos_contabeis_por_gerador
			eventos_contabeis_por_gerador.find_or_create_by(evento_contabil_id: evento_contabil_padrao.id) if !self.evento_contabil_padrao.nil?
			unless self.eventos_contabeis.nil?
				self.eventos_contabeis.each do |evento_contabil|
					eventos_contabeis_por_gerador.find_or_create_by(evento_contabil_id: evento_contabil.id) if !evento_contabil.nil?
				end
			end
		end

		def gera_movimentacao
			valor = self.valor.to_f
			# Movimenta o plano de contas do Evento Contábil
			eventos_contabeis_por_gerador.each do |evento_contabil_por_gerador|
				if evento_contabil_por_gerador.evento_contabil.present?
					contas_por_eventos_contabeis_para_movimentacao(evento_contabil_por_gerador).each { |conta|
						movimentacao_do_plano_de_contas = movimentacoes_do_plano_de_contas.find_by(conta_por_evento_contabil_id: conta.id)
						if valor > 0 && movimentacao_do_plano_de_contas.try(:valor) != valor
							#deleta o movimentação anterior
							movimentacao_do_plano_de_contas.try(:destroy)

							# cria uma movimentação nova/atualizada
							cria_movimentacao(conta, valor)
						end
					}
				end
			end
		end

		# esse método foi criado pois existem alguns eventos que possuem mais de um par de contas e existem algumas regras específicas
		# que o lançamento só deve ser feito em alguns par(res) de conta de um determinado evento
		def contas_por_eventos_contabeis_para_movimentacao evento_contabil_por_gerador
			evento_contabil = evento_contabil_por_gerador.evento_contabil
			gerador = evento_contabil_por_gerador.gerador
			if (evento_contabil_por_gerador.gerador_type == "Contabilidade::EstornoDeLiquidacao" && evento_contabil.apropriacoes_de_vpd?) && evento_contabil.automatico?
				if gerador.empenho.ordinario?
					evento_contabil.contas_por_eventos_contabeis.select{|cpec| cpec.conta.codigo == "213110101" || cpec.conta_par.codigo == "213110101"}
				else
					evento_contabil.contas_por_eventos_contabeis.select{|cpec| cpec.conta.codigo == "213110102" || cpec.conta_par.codigo == "213110102"}
				end
			else
				evento_contabil.contas_por_eventos_contabeis
			end
		end

		def cria_movimentacao conta, valor
			data_de_lancamento = self.send(self.class.atributo_data)
			codigo_movimentacao = self.send(self.class.atributo_codigo_movimentacao)
			unidade_orcamentaria_gerador = defined?(unidade_orcamentaria).nil? ? nil : unidade_orcamentaria

			self.movimentacoes_do_plano_de_contas.create!(
				conta_por_evento_contabil_id: conta.id,
				valor: valor,
				data_de_lancamento: data_de_lancamento,
				tipo_de_lancamento: conta.tipo_de_lancamento,
				codigo_movimentacao: codigo_movimentacao,
				unidade_orcamentaria: unidade_orcamentaria_gerador
			)
		end

		def classificacao_orcamentaria_despesa?
			[5, 6, 7].include? self.class.acao_do_sistema.codigo # Anulações/Estornos de => Empenhar, Liquidar e Pagar
		end

		def classificacao_orcamentaria_receita?
			self.class.acao_do_sistema.codigo.eql?(8) # Anulação => Recolher (Talão de Receita)
		end

		def pesquisa_padrao_por_nome_e_classe_sem_dotacao(nome, classe)
			self.acao_do_sistema.eventos_contabeis.automatico.find_by(classe: classe, orcamento_id: self.orcamento_id, nome: nome)
		end
	end
end
