require 'rails_helper'

RSpec.describe Loa::Subacao, type: :model do
	it { is_expected.to belong_to(:acao).required }
	it { is_expected.to belong_to(:unidade_orcamentaria).required }
	it { is_expected.to belong_to(:funcao).class_name('Base::Funcao').required }
	it { is_expected.to belong_to(:subfuncao).class_name('Base::Subfuncao').required }
	it { is_expected.to belong_to(:tipo_de_orcamento).class_name('Base::TipoDeOrcamento').required }

	it { is_expected.to have_many(:metas_fisicas).dependent(:restrict_with_exception) }
	it { is_expected.to have_many(:orcamentos_da_despesa).through(:elementos_de_despesa_por_subacao) }
	it { is_expected.to have_many(:elementos_de_despesa).through(:elementos_de_despesa_por_subacao)}
	it { is_expected.to have_many(:elementos_de_despesa_por_subacao).dependent(:restrict_with_exception) }
	it { is_expected.to have_many(:temas_da_subacao).dependent(:destroy) }

	it { is_expected.to accept_nested_attributes_for(:elementos_de_despesa_por_subacao).allow_destroy(true) }

	[:acao, :unidade_orcamentaria_id, :funcao_id, :subfuncao_id, :tipo_de_orcamento_id, :codigo, :fixacao_da_despesa, :status_do_orcamento ].each do |atributo|
		it { is_expected.to validate_presence_of atributo }
	end

	it { is_expected.to validate_length_of( :codigo ).is_equal_to(4) }

	it { is_expected.to validate_numericality_of(:fixacao_da_despesa).is_greater_than_or_equal_to(0) }

	describe "#preenche_tipo_de_orcamento" do
		it 'preenche tipo de orçamento automaticamente de acordo com a função' do
			subacao = FactoryBot.create :subacao_1005_0001, :orcamento_2016, tipo_de_orcamento_id: nil
			funcao = subacao.funcao
			expect(subacao.funcao.tipo_de_orcamento_id).to eq funcao.tipo_de_orcamento_id
		end
	end

	describe '#acao_deve_ter_apenas_uma_subacao' do
		before(:each) do
		  @acao = Loa::Acao.find_or_create_by!( FactoryBot.attributes_for( :acao_9005, :orcamento_2017 ) )
			@acao.subacoes.create(FactoryBot.attributes_for :subacao_1005_0001, :orcamento_2016, acao_id: @acao.id)
		end

		context 'quando orçamento trabalha com subação' do
			it 'retorna erro caso ação tenha mais de uma subação' do
				subacao = @acao.subacoes.create(FactoryBot.attributes_for :subacao_1005_0002, :orcamento_2016)
				expect(subacao).to be_persisted
			end
		end

		context 'quando orçamento não trabalha com subação' do
			it 'retorna erro caso ação tenha mais de uma subação' do
				orcamento_nao_trabalha_com_subacoes @acao.programa_de_governo.orcamento
				subacao = @acao.subacoes.create(FactoryBot.attributes_for :subacao_1005_0002, :orcamento_2016)
				expect(subacao.errors[:base]).to include "essa ação não pode ter mais de uma subação"
			end
		end
	end

	describe "#valor_total" do
		it "retorna a soma dos orçamentos da despesa" do
			subacao = FactoryBot.create :subacao_1005_0001, :orcamento_2016
			FactoryBot.create :orcamento_da_despesa_saude
			FactoryBot.create :orcamento_da_despesa_educacao

			expect( subacao.valor_total_fixado_da_despesa ).to eq(12365.67)
		end
	end

	describe "#orcamento" do
		it "retorna o orçamento ao qual a subação pertence" do
			subacao = FactoryBot.create :subacao_1005_0001, :orcamento_2016
			expect( subacao.orcamento ).to eq( subacao.acao.programa_de_governo.orcamento )
		end
	end

	describe "#save" do
		it "não permite alteração no código" do
			subacao = FactoryBot.create :subacao_1005_0001, :orcamento_2016
			subacao.codigo = 1000
			subacao.save
			expect( subacao.errors[:codigo] ).to include( 'não pode ser alterado' )
		end

		context "quando a função tem código igual a 8, 9 ou 10" do
			it "atribui 'seguridade social' para o tipo de orçamento" do
				subacao = FactoryBot.create :subacao_1005_0001, :orcamento_2016

				expect(subacao.tipo_de_orcamento.descricao).to eq "Orçamento da Seguridade Social"
			end
		end

		context "quando a função tem código diferente de 8, 9 ou 10" do
			it "atribui 'orçamento fiscal' para o tipo de orçamento" do
				subacao = FactoryBot.create :subacao_1005_0002, :orcamento_2016

				expect(subacao.tipo_de_orcamento.descricao).to eq "Orçamento Fiscal"
			end
		end

		context "quando o tipo de orçamento é passado no hash de atributos" do
			it "permite criação de subação com tipo de orçamento diferente do padrão" do
				subacao = FactoryBot.create :subacao_1005_0001, :com_tipo_de_orcamento_fora_do_padrao, :orcamento_2016

				expect( subacao.tipo_de_orcamento.descricao).to eq "Orçamento da Seguridade Social"
			end
		end
	end

	describe "#update" do
		before do
			@subacao = FactoryBot.create :subacao_1005_0002, :orcamento_2016
			@elemento_de_despesa = FactoryBot.create :elemento_de_despesa_material_de_consumo
			@elemento_de_despesa_diferente = FactoryBot.create :elemento_de_despesa_material_de_consumo, codigo: '33903001', descricao: 'Material de Consumo'
			@fonte_de_recursos = FactoryBot.create :alienacao_de_bens

		end

		context "quando são passados elementos de despesa repetidos" do
			it "retorna mensagem de erro" do
				atributos_de_elementos_de_despesa_por_subacao =  {
					elementos_de_despesa_por_subacao_attributes: {
						"0" => {
							elemento_de_despesa_id: @elemento_de_despesa.id,
						},
						"1" => {
							elemento_de_despesa_id: @elemento_de_despesa.id,
						}
					}
				}
				@subacao.update(atributos_de_elementos_de_despesa_por_subacao)
				expect( @subacao.errors[:elementos_de_despesa_por_subacao] ).to include( 'elemento de despesa deve ser único dentro da subação' )
				expect( @subacao.elementos_de_despesa_por_subacao.first.errors[:elemento_de_despesa_id] ).not_to	include('elemento de despesa deve ser único dentro da subação')
				expect( @subacao.elementos_de_despesa_por_subacao.last.errors[:elemento_de_despesa_id] ).to	include('elemento de despesa deve ser único dentro da subação')
			end
		end

		context "quando não são passados elementos de despesa repetidos" do
			it "atualiza subacao" do
				atributos_de_elementos_de_despesa_por_subacao =  {
					elementos_de_despesa_por_subacao_attributes: {
						"0" => {
							elemento_de_despesa_id: @elemento_de_despesa.id,
						},
						"1" => {
							elemento_de_despesa_id: @elemento_de_despesa_diferente.id,
						}
					}
				}

				@subacao.update(atributos_de_elementos_de_despesa_por_subacao)

				expect( @subacao.errors[:elementos_de_despesa_por_subacao] ).not_to include( 'elemento de despesa deve ser único dentro da subação' )
				expect( @subacao.elementos_de_despesa_por_subacao.first.errors[:elemento_de_despesa_id] ).not_to	include('elemento de despesa deve ser único dentro da subação')
				expect( @subacao.elementos_de_despesa_por_subacao.last.errors[:elemento_de_despesa_id] ).not_to	include('elemento de despesa deve ser único dentro da subação')
			end
		end

		context 'quando o valor da fixação da despesa não corresponde ao total da despesa' do
			it 'retorna mensagem de erro' do
				atributos_de_elementos_de_despesa_por_subacao =  {
					fixacao_da_despesa: 99.00,
					elementos_de_despesa_por_subacao_attributes: {
						"0" => {
							elemento_de_despesa_id: @elemento_de_despesa.id,
							orcamentos_da_despesa_attributes: {
								"0" => {
									valor: 100.00,
									fonte_de_recursos_id: @fonte_de_recursos.id
								}
							}
						}
					}
				}

				@subacao.update(atributos_de_elementos_de_despesa_por_subacao)

				expect( @subacao.errors[:fixacao_da_despesa] ).to include( 'valor da fixação não corresponde ao total da despesa' )
			end
		end

		context 'quando o valor da fixação da despesa corresponde ao total da despesa' do
			it 'não retorna mensagem de erro' do
				atributos_de_elementos_de_despesa_por_subacao =  {
					fixacao_da_despesa: 100.00,
					elementos_de_despesa_por_subacao_attributes: {
						"0" => {
							elemento_de_despesa_id: @elemento_de_despesa.id,
							orcamentos_da_despesa_attributes: {
								"0" => {
									valor: 50.00,
									fonte_de_recursos_id: @fonte_de_recursos.id
								}
							}
						},
						"1" => {
							elemento_de_despesa_id: @elemento_de_despesa_diferente.id,
							orcamentos_da_despesa_attributes: {
								"0" => {
									valor: 50.00,
									fonte_de_recursos_id: @fonte_de_recursos.id
								}
							}
						}
					}
				}

				@subacao.update(atributos_de_elementos_de_despesa_por_subacao)

				expect( @subacao.errors[:fixacao_da_despesa] ).not_to include( 'valor da fixação não corresponde ao total da despesa' )
			end
		end
	end

	describe "#classificacao_funcional_programatica" do
		it "retorna FF.fff.PPPP.AAAA.SSSS, onde FF é o código da função, fff é o código da subfunção, PPPP é o código do programa, AAAA é o código da ação e SSSS é o código da subação" do
			subacao = FactoryBot.create(:subacao_1005_0001, :orcamento_2016)

			expect(subacao.classificacao_funcional_programatica).to eq('80.126.1010.3005.0001')
		end
	end

	describe "#sugestao_de_codigo" do
		context "quando não há subações cadastradas" do
			it "retorna 0001" do
				subacao = FactoryBot.create( :subacao_1005_0002, :orcamento_2016, codigo: '' )

				expect(subacao.codigo).to eq '0001'
			end
		end

		context "quando há subações cadastradas" do
			it "retorna o próximo código" do
				FactoryBot.create( :subacao_1005_0001, :orcamento_2016, codigo: '0001' )
				subacao = FactoryBot.create( :subacao_1005_0003, :orcamento_2016, codigo: '' )

				expect(subacao.codigo).to eq '0002'
			end
		end

		context "quando há lacunas na sequência dos códigos" do
			it "retorna a primeira lacuna" do
				FactoryBot.create( :subacao_1005_0001, :orcamento_2016, codigo: '0001' )
				FactoryBot.create( :subacao_1005_0002, :orcamento_2016, codigo: '0003' )
				subacao = FactoryBot.create( :subacao_1005_0003, :orcamento_2016, codigo: '' )

				expect(subacao.codigo).to eq '0002'
			end
		end
	end

	describe '#subacao_deve_ser_reserva_de_contingecia_se_acao_for_reserva_de_contingencia' do
		it 'retorna mensagem de erro, se a ação da subação for de reserva de contingência e a unidade orçamentaria selecionada para aquela subação não for reserva de contingência' do
			acao = FactoryBot.create( :acao_9009, :orcamento_2016 )
			subacao = FactoryBot.build( :subacao_1005_0002, :orcamento_2016 )
			subacao.acao_id = acao.id
			subacao.save

			expect(subacao.errors[:unidade_orcamentaria_id]).to include( "deve ser selecionada uma Unidade Orçamentária de reserva de contingência, pois a Ação é do tipo reserva de contingência" )
		end
	end

	describe '#subacao_reserva_de_contingencia_deve_ser_acao_reserva_de_contingencia' do
		it 'retorna mensagem de erro, se a ação da subação não for de reserva de contingência e a unidade orçamentaria selecionada para aquela subação for reserva de contingência' do
			tipo_de_unidade_administrativa = Base::TipoDeUnidadeAdministrativa.find_or_create_by!( FactoryBot.attributes_for( :unidade_reserva_de_contingencia, :orcamento_2016).except!(:poder_associado) )
			orgao = FactoryBot.create(:loa_secretaria_de_saude, :orcamento_2016, tipo_de_unidade_administrativa_id: tipo_de_unidade_administrativa.id)

			unidade_reserva_de_contingencia = FactoryBot.build( :loa_seduc, :orcamento_2016 )
			unidade_reserva_de_contingencia.orgao_id = orgao.id
			unidade_reserva_de_contingencia.tipo_de_unidade_administrativa_id = tipo_de_unidade_administrativa.id
			unidade_reserva_de_contingencia.save

			subacao = FactoryBot.build( :subacao_1005_0002, :orcamento_2016 )
			subacao.unidade_orcamentaria_id = unidade_reserva_de_contingencia.id
			subacao.save

			expect(subacao.errors[:unidade_orcamentaria_id]).to include( 'não pode ser selecionada uma Unidade Orçamentária de reserva de contingência, pois a Ação não é do tipo reserva de contingência' )
		end
	end

	describe '#percentual_total_dos_temas' do
		before(:each) do
			@subacao = FactoryBot.create( :subacao_1005_0002, :orcamento_2016 )
			@tema_do_orcamento = FactoryBot.create(:loa_temas_do_orcamento, orcamento_id: @subacao.acao.programa_de_governo.orcamento_id)
		end

		context 'não existe tema associado' do
			it "não retorna erro" do
				@subacao.validate

				expect(@subacao.errors[:temas_da_subacao]).not_to include( 'soma dos percentuais deve ser menor ou igual a 100%' )
			end
		end

		context 'total menor que 100%' do
			it 'não retorna erro' do
				tema = FactoryBot.build(:loa_tema_da_subacao, percentual: 30.0)
				@subacao.temas_da_subacao.new(tema.attributes)
				@subacao.validate

				expect(@subacao.errors[:temas_da_subacao]).not_to include( 'soma dos percentuais deve ser menor ou igual a 100%' )

			end
		end

		context 'total maior que 100%' do
			it 'retorna erro' do
				@subacao.temas_da_subacao.new(FactoryBot.attributes_for(:loa_tema_da_subacao, percentual: 120.0))
				@subacao.validate

				expect(@subacao.errors[:temas_da_subacao]).to include( 'soma dos percentuais deve ser menor ou igual a 100%' )
			end
		end
	end

	describe '#lista_de_temas' do
		before(:each) do
			orcamento = FactoryBot.create(:orcamento_2016)
			@tema = FactoryBot.create(:loa_temas_do_orcamento, orcamento_id: orcamento.id, ativo: true)
			@subacao = FactoryBot.create( :subacao_1005_0002, :orcamento_2016 )
		end
		it 'adiciona tema de orçamento a lista ' do
			expect(@subacao.lista_de_temas.size).to eq 1
		end

		it 'remove tema de orçamento a lista ' do
			@tema.delete
			expect(@subacao.reload.lista_de_temas.size).to eq 0
		end
	end

	describe 'to_sim' do
		before(:each) do
			FactoryBot.create(:configuracao)
		end

		it 'retorna string formatada para envio ao SIM' do
			subacao = FactoryBot.build(:subacao_1005_0001, :com_tipo_de_orcamento, :orcamento_2016)
			expect( subacao.to_sim ).to eq '"203","099","201600","04","00","80","126","1010","3","005","0001","S","AQUISICAO E IMPLANTACAO DE PRODUTOS E SERVICOS DE TI","AQUISICAO E IMPLANTACAO DE PRODUTOS E SERVICOS DE TI",0.00'
		end
	end

	it 'valor_total_fixado_por_fonte_de_recursos' do
		orcamento_da_despesa = Loa::OrcamentoDaDespesa.create( FactoryBot.attributes_for( :orcamento_da_despesa_educacao ) )

		expect(orcamento_da_despesa.elemento_de_despesa_por_subacao.subacao.valor_total_fixado_por_fonte_de_recursos(Base::FonteDeRecursos.first)).to eq 20.00
	end
end
