require 'active_support/concern'

module CalculaValoresDeElementos
	extend ActiveSupport::Concern

	def codigo_formatado
		codigo.gsub(/(\d{1})(\d{1})(\d{2})(\d{2})(\d{2})/, '\1.\2.\3.\4.\5')
	end

	def valor_total_fixado_dos_elementos tipo_de_orcamento=nil
		self.elementos_de_despesa.to_a.sum (tipo = tipo_de_orcamento) {|elemento| elemento.valor_total_fixado_da_despesa(tipo)}
	end

	def valor_total_fixado_dos_elementos_por_unidade_orcamentaria unidade_orcamentaria_id
		if unidade_orcamentaria_id.to_i > 0
			self.elementos_de_despesa.to_a.sum {|elemento| elemento.valor_previsto_por_unidade_orcamentaria(unidade_orcamentaria_id)}
		else
			self.valor_total_fixado_dos_elementos
		end
	end

	def valor_total_realizado_dos_elementos
		self.elementos_de_despesa.to_a.sum(&:valor_total_realizado_da_despesa)
	end

	def valor_total_fixado_dos_elementos_intra_orcamentarios unidade_orcamentaria_id=nil
		calcula_valor_total_dos_elementos_por_filtro :fixado, :intra_orcamentarios, unidade_orcamentaria_id
	end

	def valor_total_realizado_dos_elementos_intra_orcamentarios unidade_orcamentaria_id=nil
		calcula_valor_total_dos_elementos_por_filtro :realizado, :intra_orcamentarios, unidade_orcamentaria_id
	end

	def valor_total_fixado_dos_elementos_exceto_intra_orcamentarios unidade_orcamentaria_id=nil
		calcula_valor_total_dos_elementos_por_filtro :fixado, :exceto_intra_orcamentarios, unidade_orcamentaria_id
	end

	def valor_total_realizado_dos_elementos_exceto_intra_orcamentarios unidade_orcamentaria_id=nil
		calcula_valor_total_dos_elementos_por_filtro :realizado, :exceto_intra_orcamentarios, unidade_orcamentaria_id
	end

	def calcula_valor_total_dos_elementos_por_filtro fonte_dos_valores, categorias_economicas, unidade_orcamentaria_id
		if fonte_dos_valores == :fixado
			metodo_de_calculo = :valor_total_fixado_da_despesa
			metodo_valor = :valor
		elsif fonte_dos_valores == :realizado
			metodo_de_calculo = :valor_total_realizado_da_despesa
			metodo_valor = :valor_empenhado
		else
			return nil
		end

		if unidade_orcamentaria_id.present? && unidade_orcamentaria_id != ""
			elementos_de_despesa = self.elementos_de_despesa.send(categorias_economicas)
			Loa::OrcamentoDaDespesa.joins(elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}, loa_elementos_de_despesa_por_subacao: {elemento_de_despesa_id: elementos_de_despesa } ).distinct.sum(metodo_valor)
		else
			self.elementos_de_despesa.send(categorias_economicas).to_a.sum(&metodo_de_calculo)
		end
	end

	def valor_previsto_por_mes mes, unidade_orcamentaria_id=0
		self.elementos_de_despesa.inject(0){ |total, elemento_de_despesa| total + elemento_de_despesa.valor_previsto_por_mes(mes, unidade_orcamentaria_id)}
	end

	def valor_empenhado_da_despesa(unidade_orcamentaria_id, data_final, elemento_de_despesa)
		if unidade_orcamentaria_id == "CONSOLIDADO"
			calcula_valor_total_da_despesa(:empenhado, unidade_orcamentaria_id, data_final, elemento_de_despesa)
		else
			calcula_valor_total_da_despesa(:empenhado, unidade_orcamentaria_id, data_final, elemento_de_despesa)
		end
	end

	def valor_anulado_da_despesa(unidade_orcamentaria_id, data_final, elemento_de_despesa)
		if unidade_orcamentaria_id == "CONSOLIDADO"
			calcula_valor_total_da_despesa(:anulado, unidade_orcamentaria_id, data_final, elemento_de_despesa)
		else
			calcula_valor_total_da_despesa(:anulado, unidade_orcamentaria_id, data_final, elemento_de_despesa)
		end
	end

	def saldo_total_da_despesa(unidade_orcamentaria_id=nil, data_final=nil, elemento_de_despesa=nil)
		valor_empenhado_da_despesa(unidade_orcamentaria_id, data_final, elemento_de_despesa) + valor_anulado_da_despesa(unidade_orcamentaria_id, data_final, elemento_de_despesa)
	end

	def calcula_valor_total_da_despesa(tipo_de_lancamento, unidade_orcamentaria_id, data_final, elemento_de_despesa)
		if tipo_de_lancamento == :empenhado
			modulo_type = "Contabilidade::Empenho"
		elsif tipo_de_lancamento == :anulado
			modulo_type = "Contabilidade::AnulacaoDoEmpenho"
		else
			return nil
		end

		if unidade_orcamentaria_id != "CONSOLIDADO" && elemento_de_despesa.nil?
			Contabilidade::LancamentoDoOrcamentoDaDespesa.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao])
				.where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}, loa_elementos_de_despesa_por_subacao: {elemento_de_despesa_id: elementos_de_despesa })
				.where('contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento <= ? AND contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ?', data_final, modulo_type)
				.select('DISTINCT ON (contabilidade_lancamentos_do_orcamento_da_despesa.valor) contabilidade_lancamentos_do_orcamento_da_despesa.valor')
				.sum('contabilidade_lancamentos_do_orcamento_da_despesa.valor')
		elsif unidade_orcamentaria_id != "CONSOLIDADO" && elemento_de_despesa.present?
			Contabilidade::LancamentoDoOrcamentoDaDespesa.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao])
				.where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}, loa_elementos_de_despesa_por_subacao: {elemento_de_despesa_id: id })
				.where('contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento <= ? AND contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ?', data_final, modulo_type)
				.select('DISTINCT ON (contabilidade_lancamentos_do_orcamento_da_despesa.valor) contabilidade_lancamentos_do_orcamento_da_despesa.valor')
				.sum('contabilidade_lancamentos_do_orcamento_da_despesa.valor')
		elsif unidade_orcamentaria_id == "CONSOLIDADO" && elemento_de_despesa.nil?
			Contabilidade::LancamentoDoOrcamentoDaDespesa.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao])
				.where(loa_elementos_de_despesa_por_subacao: {elemento_de_despesa_id: elementos_de_despesa })
				.where('contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento <= ? AND contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ?', data_final, modulo_type)
				.select('DISTINCT ON (contabilidade_lancamentos_do_orcamento_da_despesa.valor) contabilidade_lancamentos_do_orcamento_da_despesa.valor')
				.sum('contabilidade_lancamentos_do_orcamento_da_despesa.valor')
		elsif unidade_orcamentaria_id == "CONSOLIDADO" && elemento_de_despesa.present?
			Contabilidade::LancamentoDoOrcamentoDaDespesa.joins(orcamento_da_despesa: [elemento_de_despesa_por_subacao: :subacao])
				.where(loa_elementos_de_despesa_por_subacao: {elemento_de_despesa_id: id })
				.where('contabilidade_lancamentos_do_orcamento_da_despesa.data_do_lancamento <= ? AND contabilidade_lancamentos_do_orcamento_da_despesa.modulo_type = ?', data_final, modulo_type)
				.select('DISTINCT ON (contabilidade_lancamentos_do_orcamento_da_despesa.valor) contabilidade_lancamentos_do_orcamento_da_despesa.valor')
				.sum('contabilidade_lancamentos_do_orcamento_da_despesa.valor')
		end
	end
end
