class Projecao::ProjecaoDeReceita < ApplicationRecord
	include SeedIndices
	include SeedProjecaoReceitas

	belongs_to :planejamento, required: true, polymorphic: true

	has_many :calculo_de_projecoes, dependent: :destroy
	has_many :receitas, dependent: :delete_all
	has_many :calculo_por_exercicios_base, through: :receitas, source: :calculo_por_exercicios
	has_many :calculo_por_exercicios_projecao, through: :calculo_de_projecoes, source: :calculo_por_exercicios
	has_many :grupos_de_indice, dependent: :destroy
	has_many :indices_de_projecao, dependent: :destroy

	validates_uniqueness_of :planejamento_id, scope: :planejamento_type

	validates_presence_of :planejamento_id, :planejamento_type, :descricao, :exercicio_base_inicial, :exercicio_base_final, :exercicio_projecao_inicial, :exercicio_projecao_final

	validates_numericality_of :exercicio_base_final,        greater_than: Proc.new {|projecao| projecao.exercicio_base_inicial.to_f }, message: 'deve ser maior que o exercício inicial da base cálculo'
	validates_numericality_of :exercicio_corrente,          greater_than: Proc.new {|projecao| projecao.exercicio_base_final.to_f }, message: 'deve ser maior que o exercício final da base de cálculo', allow_blank: true
	validates_numericality_of :exercicio_projecao_inicial,  greater_than: Proc.new {|projecao| projecao.exercicio_corrente.to_f }, message: 'deve ser maior que o exercício corrente de cálculo', if: Proc.new {|projecao| projecao.exercicio_corrente.present? }
	validates_numericality_of :exercicio_projecao_inicial,  greater_than: Proc.new {|projecao| projecao.exercicio_base_final.to_f }, message: 'deve ser maior que o exercício final da base de cálculo'
	validates_numericality_of :exercicio_projecao_final,    greater_than: Proc.new {|projecao| projecao.exercicio_projecao_inicial.to_f }, message: 'deve ser maior que o exercício inicial da projeção'

	validates_length_of :descricao, maximum: 200

	validate :intervalo_minimo_de_dois_anos_para_os_exercicios_base_inicial_e_final
	validate :exercicio_corrente_deve_ser_um_ano_acima_do_exercicio_base_final, if: Proc.new {|projecao| projecao.exercicio_corrente.present? }
	validate :exercicio_projecao_inicial_deve_ser_um_ano_acima_do_exercicio_corrente, if: Proc.new {|projecao| projecao.exercicio_corrente.present? }

	validates :planejamento_id, immutable: true
	validates :planejamento_type, immutable: true

	after_create :criar_indices

	def busca_projecoes_de_receita_por_orcamento codigo, ano
		resultado = self.receitas.find_by(codigo: codigo).try(:calculo_por_exercicios).find_by(exercicio: ano) if self.receitas.present?
		resultado.try(:tipo) == "previsto" ? resultado.update(tipo: "convenio") : resultado
		resultado
	end

	def total_agregado_projetado_das_origens_do_ppa
		self.receitas.origens(planejamento.utiliza_novo_tipo_para_receita?).where.not(categoria_economica: '9').order(:codigo).map { |receita|
			{
				nome: receita.descricao,
				quantidade: receita.total_agregado_projecao_consolidado
			}
		}
	end

	def total_agregado_projecao_consolidado_nos_3_ultimos_anos
		origens = self.receitas.order(:codigo).origens
		resultado = [{
				exercicio: exercicio_projecao_inicial,
				receitas: origens.map do |receita|
					{
						nome: receita.descricao,
						quantidade: receita.total_agregado_projecao(exercicio_projecao_inicial)
					}
				end
		}, {
				exercicio: (exercicio_projecao_inicial+1).to_s + ' - ' + exercicio_projecao_final.to_s,
				receitas: origens.map do |receita|
					{
						nome: receita.descricao,
						quantidade: receita.total_agregado_projecao_dos_3_ultimos_anos
					}
				end
		}]

		resultado.each do |dado|
			dado[:receitas].delete_if {|k| k[:quantidade] == 0}
		end

		return resultado
	end

	def total_agregado_projecao_por_exercicio
		if self.receitas.any?
			origens = self.receitas.order(:codigo).origens(self.receitas.first.novo_tipo?)

			resultado = self.exercicios_destino.map do |exercicio|
				{
					exercicio: exercicio,
					receitas: origens.map do |receita|
						{
							nome: receita.descricao,
							quantidade: receita.total_agregado_projecao(exercicio)
						}
					end
				}
			end

			resultado.each do |dado|
				dado[:receitas].delete_if {|k| k[:quantidade] == 0}
			end
		end

		return resultado
	end

	def exercicios_base
		return exercicio_base_inicial..exercicio_base_final
	end

	def exercicios_base_e_corrente
		return exercicio_corrente.present? ? exercicio_base_inicial..exercicio_corrente : exercicios_base
	end

	def exercicios_destino
		return exercicio_projecao_inicial..exercicio_projecao_final
	end

	def exercicios
		return exercicio_base_inicial..exercicio_projecao_final
	end

	def exercicios_com_tipo
		quadro = []
		exercicios_base.each do |exercicio|
			quadro.push({exercicio: exercicio, tipo: 'realizado'})
		end

		quadro.push({exercicio: exercicio_corrente, tipo: 'orcado'})
		quadro.push({exercicio: exercicio_corrente, tipo: 'revisado'})

		exercicios_destino.each do |exercicio|
			quadro.push({exercicio: exercicio, tipo: 'projetado'})
		end

		return quadro
	end

	def pertence_a_orcamento?
		planejamento_type == "Orcamento"
	end

	def copiar_calculos_de_projecoes exercicio_base_de_copia
		calculos_copiados = []
		calculos = self.calculo_de_projecoes.where( exercicio_destino: exercicio_base_de_copia )
		calculos.each { |calculo|
			receitas_projetadas_ids = Projecao::CalculoDeProjecao.receitas_projetadas( self, calculo.exercicio_destino, calculo.exercicio_destino + 1 ) # Retorna a lista com as receitas que já foram calculadas para o ano destino da cópia
			calculos_copiados << Projecao::CalculoDeProjecao.copiar_para_anos_seguintes( calculo, receitas_projetadas_ids )
		} if calculos.present?

		return calculos_copiados
	end

	def total_das_receitas_por_exercicio_e_tipo exercicio, tipo=nil
		self.receitas.categorias_economicas.inject(0){|total, receita|
			(total + receita.total_agregado(exercicio, tipo).to_f) if receita.present?
		}
	end

	def receitas_disponiveis exercicio
		receitas.analiticas - receitas_utilizadas(exercicio)
	end

	def receitas_utilizadas exercicio
		calculos_de_projecao = self.calculo_de_projecoes.where( exercicio_destino: exercicio ).order( :exercicio_base )
		receitas_utilizadas_em_convenio = self.planejamento.class.name.demodulize == "Ppa" || self.planejamento.class.name.demodulize == "Orcamento" ? self.planejamento.convenios.map(&:receitas).flatten : nil
		calculos_de_projecao.includes(:receitas).map(&:receitas).flatten.concat(receitas_utilizadas_em_convenio).uniq
	end

	def descricao_do_planejamento
		if self.planejamento.is_a? Ppa::Ppa
			"Projeção do PPA #{planejamento.exercicio_inicial}-#{planejamento.exercicio_final}"
		else
			"Projeção do Orçamento #{planejamento.exercicio}"
		end
	end

	def pai_mais_proximo_de_um_codigo(codigo, utiliza_novo_tipo_para_receita)
		receita = nil

		while receita.blank?
			classificacao = Projecao::Receita.classificacao_com_niveis_utilizados_por_codigo(codigo, utiliza_novo_tipo_para_receita)
			receita = receitas.where(classificacao)
			unless receita.present?
				receita = receita.nive
			end
		end
	end

	private
	def intervalo_minimo_de_dois_anos_para_os_exercicios_base_inicial_e_final
		errors.add(:exercicio_base_final, 'deve ter no mínimo 2 anos de diferença do exercicio inicial da base de cálculo') if (exercicio_base_final.to_f - exercicio_base_inicial.to_f) < 2
	end

	def exercicio_corrente_deve_ser_um_ano_acima_do_exercicio_base_final
		errors.add(:exercicio_corrente, 'deve ter somente 1 ano de diferença do exercicio final da base de cálculo') if (exercicio_corrente.to_f - exercicio_base_final.to_f) != 1
	end

	def exercicio_projecao_inicial_deve_ser_um_ano_acima_do_exercicio_corrente
		errors.add(:exercicio_projecao_inicial, 'deve ter somente 1 ano de diferença do exercicio corrente') if (exercicio_projecao_inicial.to_f - exercicio_corrente.to_f) != 1
	end
end
