class Projecao::CalculoDeProjecao < ApplicationRecord
	has_paper_trail

	belongs_to :projecao_de_receita

	attr_default :recalcular_valor, false

	has_many :indices_do_calculo_de_projecao, dependent: :destroy, inverse_of: :calculo_de_projecao
	accepts_nested_attributes_for :indices_do_calculo_de_projecao, allow_destroy: true, reject_if: :all_blank

	has_many :indices_de_projecao, through: :indices_do_calculo_de_projecao

	has_many :receitas_dos_calculos_de_projecao, dependent: :restrict_with_exception
	has_many :receitas, class_name: 'Projecao::Receita', through: :receitas_dos_calculos_de_projecao
	has_many :calculo_por_exercicios, through: :receitas_dos_calculos_de_projecao

	validates :indices_do_calculo_de_projecao, uniq_nested_attributes: {atributo: :indice_de_projecao_id, mensagem: "não é possível selecionar índices iguais"}
	validates_each :indices_do_calculo_de_projecao do |record, attribute, collection|
		indices_do_mesmo_grupo = collection.to_a.reject(&:marked_for_destruction?).select { |indice_do_calculo|
			collection.to_a.reject(&:marked_for_destruction?).count{ |prox_indice_do_calculo|
				indice_do_calculo.grupo_id_do_indice_de_projecao.eql?(prox_indice_do_calculo.grupo_id_do_indice_de_projecao)
			} > 1
		}

		indices_do_mesmo_grupo.drop(1).each { |indice_do_calculo| indice_do_calculo.errors.add(:indice_de_projecao_id, "você já utilizou um #{indice_do_calculo.indice_de_projecao.grupo_de_indice.nome}") }
		record.errors.add(attribute, "Não são permitidos indíces do mesmo tipo no mesmo cálculo") if indices_do_mesmo_grupo.any?
	end

	validates_presence_of :projecao_de_receita_id
	validates_presence_of :exercicio_base, message: 'o exercicio base não pode ficar em branco'
	validates_presence_of :exercicio_destino, message: 'o exercicio destino não pode ficar em branco'
	validates_presence_of :memoria_de_calculo, message: 'Memória de cálculo não pode ficar em branco'

	validates :projecao_de_receita_id, immutable: true

	validates_numericality_of :exercicio_base, less_than: 9999, message: 'o exercicio base não é um ano'
	validates_numericality_of :exercicio_destino, less_than: 9999, message: 'o exercicio destino não é um ano'

	validate :exercicios, if: Proc.new{|calculo| calculo.projecao_de_receita.present? }

	validate :valor_exercicio_base
	validate :projecao_tem_receita_importada

	def percentual_dos_indices
		return indices_do_calculo_de_projecao.sum(:percentual).to_f
	end

	def recalcula_valor_das_receitas
		Projecao::CalculoDeProjecao.transaction do
			begin
				self.receitas_dos_calculos_de_projecao.each { |receita_do_calculo| receita_do_calculo.atualiza_total_do_calculo_por_exercicio }
				return true
			rescue ActiveRecord::RecordInvalid
				raise ActiveRecord::Rollback
				return false
			end
		end
	end

	def indices
		return indices_do_calculo_de_projecao
	end

	def self.receitas_projetadas projecao_de_receita, exercicio_base, exercicio_destino
		receita_ids = []
		calculos_projetados = Projecao::CalculoDeProjecao.where(projecao_de_receita_id: projecao_de_receita.id, exercicio_base: exercicio_base, exercicio_destino: exercicio_destino)
		calculos_projetados.each { |calculo_projetado| receita_ids << calculo_projetado.receitas_dos_calculos_de_projecao.pluck(:receita_id) } if calculos_projetados.present?
		return receita_ids.flatten
	end

	def self.copiar_para_anos_seguintes calculo, lista_receitas
		if calculo.receitas_dos_calculos_de_projecao.present?
			Projecao::CalculoDeProjecao.transaction do
				begin
					novo_calculo = calculo.dup
					novo_calculo.exercicio_base = calculo.exercicio_destino
					novo_calculo.exercicio_destino = calculo.exercicio_destino + 1
					if novo_calculo.save
						indices_utilizados_no_calculo = calculo.indices_de_projecao
						indices = indices_utilizados_no_calculo.map { |indice_de_projecao|
							percentual = indice_de_projecao.projecoes_do_indice.find_by(exercicio: calculo.exercicio_destino + 1).percentual
							{indice_de_projecao_id: indice_de_projecao.id, percentual: percentual} if percentual
						}.compact
						return {nao_ha_indice_projetado: true} unless indices.any?
						receitas_dos_calculos = calculo.receitas_dos_calculos_de_projecao.reject { |rc| lista_receitas.include?(rc.receita_id) }.map{ |rc| {receita_id: rc.receita_id} }

						novo_calculo.indices_do_calculo_de_projecao.create!( indices )
						novo_calculo.receitas_dos_calculos_de_projecao.create( receitas_dos_calculos )
						return novo_calculo
					else
						return {exercicio_destino_fora_do_limite: true}
					end
				rescue ActiveRecord::RecordInvalid => invalid
					raise ActiveRecord::Rollback
					return invalid.message
				end
			end
		else
			return { sem_receitas: true }
		end
	end

	private
	def valor_exercicio_base
		if exercicio_base != nil && exercicio_destino != nil
			if exercicio_base >= exercicio_destino
				errors.add(:exercicio_base, "o exercicio base deve ser menor que o exercício destino")
			end
		end
	end

	def projecao_tem_receita_importada
		if self.projecao_de_receita && !self.projecao_de_receita.receitas_importadas
			errors.add(:exercicio_base, 'para criar calculo, a receita deve estar importada na projeção.')
		end
	end

	def exercicios
		unless (self.projecao_de_receita.exercicios).include?(self.exercicio_base)
			errors.add(:exercicio_base, 'o exercício base selecionado para o cálculo de projeção deve estar entre o exercício inicial e final da base de cálculo da projeção de receita')
		end

		unless (self.projecao_de_receita.exercicios_destino).include?(self.exercicio_destino)
			errors.add(:exercicio_destino, 'o exercício destino selecionado para o cálculo de projeção deve estar entre o exercício inicial e final de projeção da projeção de receita')
		end
	end
end
