require 'rails_helper'

RSpec.describe Projecao::ProjecaoDeReceita, type: :model do
	it { is_expected.to belong_to(:planejamento).required }

	it { is_expected.to have_many(:receitas).dependent(:delete_all) }
	it { is_expected.to have_many(:calculo_por_exercicios_base).through(:receitas).source(:calculo_por_exercicios) }
	it { is_expected.to have_many(:calculo_por_exercicios_projecao).through(:calculo_de_projecoes).source(:calculo_por_exercicios) }
	it { is_expected.to have_many(:calculo_de_projecoes).dependent(:destroy) }
	it { is_expected.to have_many(:grupos_de_indice).dependent(:destroy) }
	it { is_expected.to have_many(:indices_de_projecao).dependent(:destroy) }

	[:planejamento_id, :planejamento_type, :descricao, :exercicio_base_inicial, :exercicio_base_final, :exercicio_projecao_inicial, :exercicio_projecao_final].each do |atributo|
		it { is_expected.to validate_presence_of atributo }
	end

	it { is_expected.to validate_length_of(:descricao).is_at_most(200) }

	describe 'validates_uniqueness_of planejamento_id scoped to planejamento_type' do
		context 'salvando 2 projecoes com o mesmo ppa' do
			it 'retorna erro' do
				ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2014) )
				projecao2 = Projecao::ProjecaoDeReceita.find_or_create_by FactoryBot.attributes_for :projecao_de_receita_2020, planejamento_id: ppa.id
				expect(projecao2.errors[:planejamento_id]).to include('já está em uso')
			end
		end
		context 'salvando 2 projecoes,uma no ppa e outra em um orçamento' do
			it 'projeção é criada' do
				Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2014) )
				orcamento = FactoryBot.create :orcamento_2016
				expect(orcamento.projecao_de_receita).to be_persisted
			end
		end
	end

	describe 'validates #immutable' do
		before(:each) do
			ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
			@projecao = ppa.projecao_de_receita
		end

		context 'planejamento_id' do
			it 'ao tentar alterar retorna mensagem de erro no atributo' do
				@projecao.planejamento_id += 1
				@projecao.save

				expect(@projecao.errors[:planejamento_id]).to include 'não pode ser alterado'
			end
		end

		context 'planejamento_type' do
			it 'ao tentar alterar retorna mensagem de erro no atributo' do
				@projecao.planejamento_type = 'Orcamento'
				@projecao.save

				expect(@projecao.errors[:planejamento_type]).to include 'não pode ser alterado'
			end
		end
	end

	describe 'exercicios' do
		before(:each) do
			ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
			@projecao = ppa.projecao_de_receita
		end

		context "quando estão de acordo com as validações" do
			it 'salva' do
				expect(@projecao.save).to be_truthy
			end
		end

		context 'quando o intervalo do base inicial e final é menor que 2 anos' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_base_inicial = 2013
				@projecao.exercicio_base_final = 2014
				@projecao.save

				expect(@projecao.errors[:exercicio_base_final]).to include 'deve ter no mínimo 2 anos de diferença do exercicio inicial da base de cálculo'
			end
		end

		context 'quando base final é menor que o base inicial' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_base_inicial = 2014
				@projecao.exercicio_base_final = 2013
				@projecao.save

				expect(@projecao.errors[:exercicio_base_final]).to include 'deve ser maior que o exercício inicial da base cálculo'
			end
		end

		context 'quando o corrente está presente e é menor ou igual ao base final' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_corrente = 2015
				@projecao.save

				expect(@projecao.errors[:exercicio_corrente]).to include 'deve ser maior que o exercício final da base de cálculo'
			end
		end

		context 'quando o corrente está presente e é maior que o base final mais de 1 ano' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_corrente = 2017
				@projecao.save

				expect(@projecao.errors[:exercicio_corrente]).to include 'deve ter somente 1 ano de diferença do exercicio final da base de cálculo'
			end
		end

		context 'quando o corrente está presente e é menor que o projeção inicial em mais de 1 ano' do
			it 'retorna mensage de erro' do
				@projecao.exercicio_corrente = 2015
				@projecao.save

				expect(@projecao.errors[:exercicio_projecao_inicial]).to include 'deve ter somente 1 ano de diferença do exercicio corrente'
			end
		end

		context 'quando o corrente está presente e é maior ou igual ao projeção inicial' do
			it 'retorna mensage de erro' do
				@projecao.exercicio_corrente = 2017
				@projecao.save

				expect(@projecao.errors[:exercicio_projecao_inicial]).to include 'deve ser maior que o exercício corrente de cálculo'
			end
		end

		context 'quando o projeção inicial é menor ou igual que o base final' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_projecao_inicial = 2015
				@projecao.save

				expect(@projecao.errors[:exercicio_projecao_inicial]).to include 'deve ser maior que o exercício final da base de cálculo'
			end
		end

		context 'quando o projeção final é menor ou igual ao projeção final' do
			it 'retorna mensagem de erro' do
				@projecao.exercicio_projecao_final = 2017
				@projecao.save

				expect(@projecao.errors[:exercicio_projecao_final]).to include 'deve ser maior que o exercício inicial da projeção'
			end
		end
	end

	describe '#copiar_calculos_de_projecoes' do
		it 'duplica o(s) calculo(s) encontrado(s) com o exercício base e destino passado p/ um novo cálculo com a(s) mesma(s) receita(s) e mesmo(s) índice(s), e exercício base + 1 e destino + 2' do
			ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
			projecao = ppa.projecao_de_receita

			projecao.update(receitas_importadas: true)
			calculo_receita_base = FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true)
			calculo = FactoryBot.create(:calculo_de_projecao_para_2017, projecao_de_receita_id: projecao.id, exercicio_base: 2016, exercicio_destino: 2017)
			FactoryBot.create(:igpm_aplicado_para_projecao_2017, calculo_de_projecao_id: calculo.id )
			receita_projetada = FactoryBot.create(:projecao_receita_do_calculo_de_projecao, calculo_de_projecao_id: calculo.id, receita_id: calculo_receita_base.receita.id)
			FactoryBot.create(:calculo_do_exercicio_de_2016, :receita_do_calculo_de_projecao, receita_do_calculo_de_projecao_id: receita_projetada.id)

			projecao.copiar_calculos_de_projecoes( 2017 )
			expect(projecao.calculo_de_projecoes.count).to eq 2
		end
	end

	describe "receita" do
		before do
			ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
			@projecao = ppa.projecao_de_receita
			@projecao.update(receitas_importadas: true)

			FactoryBot.create(:receita_corrente, :criar_niveis_acima)
		end

		describe '#receitas_utilizadas' do
			it "retorna receitas utilizadas nos calculos da projecao" do
				receita = @projecao.receitas.last
				FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true, receita_id: receita.id)
				calculo = FactoryBot.create( :calculo_de_projecao_para_2017, projecao_de_receita_id: @projecao.id, exercicio_base: 2016, exercicio_destino: 2017 )
				FactoryBot.create( :igpm_aplicado_para_projecao_2017, calculo_de_projecao_id: calculo.id )
				receita_projetada = FactoryBot.create( :projecao_receita_do_calculo_de_projecao, calculo_de_projecao_id: calculo.id, receita_id: receita.id )
				FactoryBot.create( :calculo_do_exercicio_de_2016, :receita_do_calculo_de_projecao, receita_do_calculo_de_projecao_id: receita_projetada.id )
				expect(@projecao.receitas_utilizadas(calculo.exercicio_destino)).to include(receita)
			end
		end

		describe '#receitas_disponiveis' do
			it "retorna receitas utilizadas nos calculos da projecao" do
				receita = @projecao.receitas.last
				FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true, receita_id: receita.id)
				calculo = FactoryBot.create( :calculo_de_projecao_para_2017, projecao_de_receita_id: @projecao.id, exercicio_base: 2016, exercicio_destino: 2017 )
				FactoryBot.create( :igpm_aplicado_para_projecao_2017, calculo_de_projecao_id: calculo.id )
				receita_projetada = FactoryBot.create( :projecao_receita_do_calculo_de_projecao, calculo_de_projecao_id: calculo.id, receita_id: receita.id )
				FactoryBot.create( :calculo_do_exercicio_de_2016, :receita_do_calculo_de_projecao, receita_do_calculo_de_projecao_id: receita_projetada.id )
				receitas_disponiveis = @projecao.receitas.to_a.reject {|r| r.id == receita.id || !r.analitica? }
				expect(@projecao.receitas_disponiveis(calculo.exercicio_destino)).to match_array(receitas_disponiveis)
			end
		end

		describe '#total_das_receitas_por_exercicio_e_tipo' do
			context 'caso seja passado um tipo valido' do
				it 'retorna total das receitas de um exercicio e tipo' do
					receita = @projecao.receitas.last
					FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true, receita_id: receita.id)

					expect(@projecao.total_das_receitas_por_exercicio_e_tipo(2016, 'realizado') ).to eq 9.99
				end
			end

			context 'caso seja passado um tipo invalido' do
				it 'retorna zero' do
					receita = @projecao.receitas.last
					FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true, receita_id: receita.id)

					expect(@projecao.total_das_receitas_por_exercicio_e_tipo(2016, 'teste') ).to eq 0
				end
			end

			context 'caso seja passado um exercicio invalido' do
				it 'retorna zero' do
					receita = @projecao.receitas.last
					FactoryBot.create(:calculo_do_exercicio_de_2016, :receita, importado: true, receita_id: receita.id)

					expect(@projecao.total_das_receitas_por_exercicio_e_tipo(2099, 'teste') ).to eq 0
				end
			end
		end
	end

	describe "#total_agregado_projetado_das_origens_do_ppa" do
		context "exibe descrição da receita e total de fontes de financiamento de receitas para 4 anos do ppa atual" do
			it 'retorna nome e quantidade' do
				ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
				receita = FactoryBot.create :receita_corrente
				origem = FactoryBot.create :receita_corrente, codigo: "1200000000"

				receita_do_calculo_de_projecao = FactoryBot.create :projecao_receita_do_calculo_de_projecao, receita_id: receita.id
				FactoryBot.create(:calculo_do_exercicio_de_2016, receita_id: receita.id, tipo: 'realizado', importado: true)
				FactoryBot.create(:calculo_do_exercicio_de_2016, receita_do_calculo_de_projecao_id: receita_do_calculo_de_projecao.id, tipo: 'projetado', exercicio: 2017, total: 20)
				resultado_total_agregado_origens = ppa.projecao_de_receita.total_agregado_projetado_das_origens_do_ppa.last
				resultado = { nome: origem.descricao, quantidade: 20 }
				expect(resultado_total_agregado_origens).to include resultado
			end
		end
	end

	describe "#total_agregado_projecao_por_exercicio" do
		context "exibe exercicio, descrição da receita e total de fontes de financiamento por exercicio" do
			it 'retorna exercício, nome da receita e valor total da receita' do
				FactoryBot.create(:receita, :criar_niveis_acima)
				calculo = FactoryBot.create(:calculo_do_exercicio_de_2016, :receita_do_calculo_de_projecao, total: 9.99, exercicio: 2017)
				projecao_de_receita = calculo.receita_do_calculo_de_projecao.receita.projecao_de_receita

				resultado_projecao_de_receita = projecao_de_receita.total_agregado_projecao_por_exercicio.first[:receitas].last

				resultado = {
					nome: projecao_de_receita.receitas.origens(projecao_de_receita.receitas.first.novo_tipo?).order(:codigo).last.descricao,
					quantidade: 9.99
				}
				expect(resultado_projecao_de_receita).to include resultado
			end
		end
	end

	describe 'pertence_a_orcamento?' do
		context 'se o pai é um ppa' do
			it 'retorna false' do
				ppa = Ppa::Ppa.find_or_create_by!(FactoryBot.attributes_for (:ppa_fortaleza_2020) )
				projecao = ppa.projecao_de_receita
				expect(projecao.pertence_a_orcamento?).to be_falsey
			end
		end
		context 'se o pai é um orçamento' do
			it 'retorna true' do
				orcamento = FactoryBot.create(:orcamento_2016)
				projecao = orcamento.projecao_de_receita
				expect(projecao.pertence_a_orcamento?).to be_truthy
			end
		end
	end

end
