require 'rails_helper'

RSpec.describe Loa::UnidadeOrcamentaria, type: :model do

	it{ is_expected.to belong_to(:orgao).required }
	it{ is_expected.to belong_to(:unidade_gestora).required }
	it{ is_expected.to belong_to(:tipo_de_administracao).class_name('Base::TipoDeAdministracao').required }
	it{ is_expected.to belong_to(:tipo_de_unidade_administrativa).class_name('Base::TipoDeUnidadeAdministrativa').required(true) }
	it{ is_expected.to belong_to(:ppa_unidade_orcamentaria).class_name('Ppa::UnidadeOrcamentaria').with_foreign_key(:ppa_unidade_orcamentaria_id) }

	it{ is_expected.to have_many(:unidades_orcamentarias_por_usuario).class_name('Loa::UnidadeOrcamentariaPorUsuario') }
	it{ is_expected.to have_many(:usuarios).through(:unidades_orcamentarias_por_usuario).class_name('Usuario') }

	it{ is_expected.to have_many(:agente_publico).class_name('Base::AgentePublicoMunicipal') }
	it{ is_expected.to have_many(:unidades_orcamentaria_vinculada).class_name('Loa::UnidadeOrcamentariaVinculada') }

	it{ is_expected.to have_many(:subacoes).dependent(:restrict_with_exception) }
	it{ is_expected.to have_many(:acoes).through(:subacoes) }
	it{ is_expected.to have_many(:programas_de_governo).through(:acoes) }
	it{ is_expected.to have_many(:elementos_de_despesa_por_subacao).through(:subacoes) }
	it{ is_expected.to have_many(:elementos_de_despesa).through(:elementos_de_despesa_por_subacao) }
	it{ is_expected.to have_many(:orcamentos_da_despesa).through(:elementos_de_despesa_por_subacao) }
	it{ is_expected.to have_many(:empenhos).through(:orcamentos_da_despesa).class_name('Contabilidade::Empenho') }
	it{ is_expected.to have_many(:ordens_de_compra).through(:empenhos).class_name('Licitacao::OrdemDeCompra') }

	it{ is_expected.to have_many(:ordenadores_de_despesa) }
	it{ is_expected.to have_many(:subfuncoes).through(:subacoes) }
	it{ is_expected.to have_many(:funcoes).through(:subfuncoes) }
	it{ is_expected.to have_many(:fontes_de_recursos).through(:orcamentos_da_despesa) }
	it{ is_expected.to have_many(:modalidades_de_aplicacao).through(:elementos_de_despesa) }
	it{ is_expected.to have_many(:grupos_de_natureza_da_despesa).through(:modalidades_de_aplicacao) }

	it{ is_expected.to have_many(:unidades_orcamentarias_por_natureza_da_receita).dependent(:restrict_with_exception) }
	it{ is_expected.to have_many(:naturezas_da_receita).through(:unidades_orcamentarias_por_natureza_da_receita) }
	it{ is_expected.to have_many(:orcamentos_da_receita).through(:unidades_orcamentarias_por_natureza_da_receita) }
	it{ is_expected.to have_many(:fontes_de_recursos_da_receita).through(:orcamentos_da_receita).source(:fonte_de_recursos).class_name('Base::FonteDeRecursos') }
	it{ is_expected.to have_many(:transferencias_de_recursos_origem).class_name('Loa::TransferenciaDeRecurso').with_foreign_key("unidade_orcamentaria_origem_id") }
	it{ is_expected.to have_many(:transferencias_de_recursos_destino).class_name('Loa::TransferenciaDeRecurso').with_foreign_key("unidade_orcamentaria_destino_id") }

	it{ is_expected.to have_many(:contas_bancarias_por_unidade_orcamentaria).dependent(:destroy) }
	it{ is_expected.to have_many(:contas_bancarias).through(:contas_bancarias_por_unidade_orcamentaria) }

	it{ is_expected.to have_many(:unidades_orcamentarias_por_pedido).class_name('Licitacao::UnidadeOrcamentariaPorPedido') }
	it{ is_expected.to have_many(:pedidos).through(:unidades_orcamentarias_por_pedido).class_name('Licitacao::Pedido') }
	it{ is_expected.to have_many(:projetos).through(:pedidos).class_name('Licitacao::Projeto') }
	it{ is_expected.to have_many(:processos).through(:pedidos).class_name('Licitacao::Processo') }
	it{ is_expected.to have_many(:contratos).dependent(:restrict_with_exception).class_name('Licitacao::Contrato') }

	it{ is_expected.to have_many(:contas_por_unidades_orcamentarias ).dependent(:destroy).class_name('Contabilidade::ContaPorUnidadeOrcamentaria') }
	it{ is_expected.to have_many(:contas).through(:contas_por_unidades_orcamentarias).class_name('Contabilidade::Conta') }
	it{ is_expected.to have_many(:convenios).class_name('Contabilidade::Convenio') }
	it{ is_expected.to have_many(:diarias).class_name('Contabilidade::Diaria') }

	it { is_expected.to have_one(:orcamento).through(:orgao) }

	[:unidade_gestora_id, :orgao_id, :nome, :sigla, :codigo, :tipo_de_administracao_id, :tipo_de_unidade_administrativa_id, :status_do_orcamento].each do |atributo|
		it{ is_expected.to validate_presence_of atributo}
	end

	[:nome, :sigla, :codigo].each do |atributo|
		it{ is_expected.to validate_uniqueness_of(atributo).scoped_to([:unidade_gestora_id, :orgao_id]).case_insensitive }
	end

	context "quando unidade orcamentaria possui rpps" do
		subject {
			FactoryBot.build(:loa_pref, :orcamento_2016, rpps: true)
		}
		it{ is_expected.to validate_presence_of :situacao_orcamentaria }
	end

	it { is_expected.to validate_length_of( :codigo ).is_at_most(4) }
	it { is_expected.to validate_length_of( :nome ).is_at_most(80) }
	it { is_expected.to validate_length_of( :sigla ).is_at_most(10) }

	describe '#codigo_e_nome' do
		it 'retorna "XXXX - NOME", onde XXXX é o código e NOME é o nome da unidade orcamentaria' do
			unidade_orcamentaria = Loa::UnidadeOrcamentaria.new(codigo: '8794', nome: 'Intersol')
			expect(unidade_orcamentaria.codigo_e_nome).to eq('8794 - Intersol')
		end
	end

	describe '#codigo_completo_e_nome' do
		it 'retorna "YYXXXX - NOME", onde YY é o código do orgão, XXXX é o código da unidade orçamentária e NOME é o nome da unidade orcamentaria' do
			unidade_orcamentaria = Loa::UnidadeOrcamentaria.new(codigo: '8794', nome: 'Intersol', unidade_gestora: Loa::UnidadeGestora.new(codigo:'01'), orgao: Loa::Orgao.new(codigo: '99') )
			expect(unidade_orcamentaria.codigo_completo_e_nome).to eq('998794 - Intersol')
		end
	end

	describe "#prefeitura_deve_ser_salva_no_orgao_correto" do
		context "ao cadastrar prefeitura em orgão prefeitura" do
			it "retorna mensagem de erro" do
				unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
				unidade_orcamentaria.save
				expect( unidade_orcamentaria).to be_persisted
			end
		end
		context "ao cadastrar prefeitura em orgão diferente de prefeitura" do
			it "retorna mensagem de erro" do
				unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
				unidade_orcamentaria.orgao = FactoryBot.build(:loa_secretaria_da_educacao, :orcamento_2016)
				unidade_orcamentaria.save
				expect( unidade_orcamentaria.errors[:tipo_de_unidade_administrativa_id] ).to include "Não é possível cadastrar uma prefeitura para esse órgão"
			end
		end

	end

	describe "#codigo_de_prefeitura_deve_ser_AA" do
		context "ao cadastrar prefeitura com código diferente de AA" do
			it "retorna mensagem de erro" do
				unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
				unidade_orcamentaria.codigo = "999"
				unidade_orcamentaria.save
				expect( unidade_orcamentaria.errors[:codigo] ).to include "Esse código não corresponde ao de uma prefeitura"
			end
		end
	end

	describe "#upcase_sigla" do
		it "salva sigla com letras maiusculas" do
			unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
			unidade_orcamentaria.sigla = 'aaaa'
			unidade_orcamentaria.save

			expect(Loa::UnidadeOrcamentaria.last.sigla).to eq('AAAA')
		end
	end

	describe "#sugestao_de_codigo" do
		context "quando não há unidades orçamentárias cadastradas" do
			it "retorna 001" do
				unidade_orcamentaria = Loa::UnidadeOrcamentaria.new( FactoryBot.attributes_for( :loa_seduc, :orcamento_2016, codigo: '' ) )

				expect(unidade_orcamentaria.codigo).to eq '001'
			end
		end

		context "quando há unidades orçamentárias cadastradas" do
			it "retorna o próximo código" do
				FactoryBot.create( :loa_seduc, :orcamento_2016, codigo: '001' )
				unidade = Loa::UnidadeOrcamentaria.new( FactoryBot.attributes_for( :loa_seduc, :orcamento_2016, codigo: '' ) )

				expect(unidade.codigo).to eq '002'
			end
		end

		context "quando há lacunas na sequência dos códigos" do
			it "retorna a primeira lacuna" do
				Loa::UnidadeOrcamentaria.find_or_create_by!(FactoryBot.attributes_for( :loa_seduc, :orcamento_2016, codigo: '001' ))
				Loa::UnidadeOrcamentaria.find_or_create_by!(FactoryBot.attributes_for( :loa_hgf, :orcamento_2016, codigo: '003' ))
				Loa::UnidadeOrcamentaria.find_or_create_by!(FactoryBot.attributes_for( :loa_fes, :orcamento_2016, codigo: '005' ))

				unidade = Loa::UnidadeOrcamentaria.new( FactoryBot.attributes_for( :loa_seduc, :orcamento_2016, codigo: '' ) )

				expect(unidade.codigo).to eq '002'
			end
		end
	end

	describe '#uniqueness_unidade_reserva_de_contingencia' do
		it 'retorna mensagem de erro no cadastro, caso já haja uma unidade do tipo 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

			unidade_reserva_de_contingencia_com_erro = FactoryBot.build( :loa_fes, :orcamento_2016 )
			unidade_reserva_de_contingencia_com_erro.orgao_id = orgao.id
			unidade_reserva_de_contingencia_com_erro.tipo_de_unidade_administrativa_id = tipo_de_unidade_administrativa.id
			unidade_reserva_de_contingencia_com_erro.save

			expect(unidade_reserva_de_contingencia_com_erro.errors[:tipo_de_unidade_administrativa_id]).to include( 'já existe uma unidade orçamentária do tipo reserva de contingência' )
		end
	end


	describe '#deve_ter_o_mesmo_tipo_de_poder_do_orgao' do
		context 'quando a unidade possui o mesmo poder do orgao' do
			it 'salva a unidade orcamentaria' do
				_orgao_executivo = FactoryBot.create( :loa_secretaria_de_saude, :orcamento_2016 )
				seduc_unidade_gestora = FactoryBot.create(:loa_gestora_seduc, :orcamento_2016)

				_orgao_executivo.unidades_orcamentarias.create({
					codigo: '20',
					nome: 'Administração',
					sigla: 'ADM',
					status_do_orcamento: 0,
					unidade_gestora_id: seduc_unidade_gestora.id,
					tipo_de_administracao_id: Base::TipoDeAdministracao.find_or_create_by!( FactoryBot.attributes_for(:administracao_direta, :orcamento_2016)).id,
					tipo_de_unidade_administrativa_id: Base::TipoDeUnidadeAdministrativa.find_by(codigo: 90).id
				})

				expect(Loa::UnidadeOrcamentaria.last).to be_persisted
			end
			context 'quando a unidade possui um poder diferente do visto no orgao' do
				it 'retorna mensagem de erro' do
					_orgao_executivo = FactoryBot.create( :loa_secretaria_de_saude, :orcamento_2016 )
					seduc_unidade_gestora = FactoryBot.create(:loa_gestora_seduc, :orcamento_2016)

					nova_unidade_orcamentaria = _orgao_executivo.unidades_orcamentarias.create({
						codigo: '20',
						nome: 'Administração',
						sigla: 'ADM',
						status_do_orcamento: 0,
						unidade_gestora_id: seduc_unidade_gestora.id,
						tipo_de_administracao_id: Base::TipoDeAdministracao.find_or_create_by!( FactoryBot.attributes_for(:administracao_direta, :orcamento_2016)).id,
						tipo_de_unidade_administrativa_id: Base::TipoDeUnidadeAdministrativa.find_by(descricao: 'Câmara Municipal').id
					})

					expect(nova_unidade_orcamentaria.errors[:tipo_de_unidade_administrativa_id]).to include('Não é possivel escolher um tipo de unidade administrativa com poder diferente ao escolhido no orgão')
				end
			end
		end
	end

	describe 'to_sim' do
		before(:each) do
			FactoryBot.create(:configuracao)
		end
		# testar sempre validando se o ultimo campo foi preenchido
		it 'retorna string formatada para envio ao SIM' do
			unidade_orcamentaria = FactoryBot.build(:loa_hgf, :orcamento_2016)
			expect( unidade_orcamentaria.to_sim ).to eq '"104","099","201600","04","0002","91","HOSPITAL GERAL DE FORTALEZA","D"'
		end
	end

	describe '#programas_de_governo' do
		it 'retorna apenas programas da unidade gestora enviada' do
			unidade = FactoryBot.create(:loa_hgf, :orcamento_2016)
			programa = FactoryBot.create(:programa_bolsa_familia, :orcamento_2016)
			acao = FactoryBot.create(:acao_9005, programa_de_governo_id:programa.id)
			FactoryBot.create(:subacao_1005_0001, :orcamento_2016, acao_id: acao.id, unidade_orcamentaria_id:unidade.id)
			expect(unidade.programas_de_governo).to include programa
		end
	end

	it 'total_da_receita_por_fonte_de_recurso' do
		orcamento_da_receita = FactoryBot.create(:orcamento_da_receita_saude)

		expect(orcamento_da_receita.unidade_orcamentaria_por_natureza_da_receita.unidade_orcamentaria.total_da_receita_por_fonte_de_recurso(Base::FonteDeRecursos.first)).to eq 10.00
	end

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

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

	it 'diferenca_entre_receita_e_despesa_por_fonte_de_recurso' do
		orcamento_da_receita = FactoryBot.create(:orcamento_da_receita_educacao, valor: 40)
		Loa::OrcamentoDaDespesa.create( FactoryBot.attributes_for( :orcamento_da_despesa_educacao ) )

		expect(orcamento_da_receita.unidade_orcamentaria_por_natureza_da_receita.unidade_orcamentaria.diferenca_entre_receita_e_despesa_por_fonte_de_recurso(Base::FonteDeRecursos.first)).to eq 20.00
	end

	describe '#saae_deve_ter_sempre_tipo_de_administracao_como_autarquia' do
		context "quando é saae e possui tipo de administracao como autarquia" do
			it "cria unidade orcamentaria" do
				FactoryBot.create(:orcamento_2016)
				tipo_autarquia = Base::TipoDeUnidadeAdministrativa.find_by(codigo: '05')
				unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016, saae: true, tipo_de_unidade_administrativa_id: tipo_autarquia.id)
				expect(unidade_orcamentaria.validate).to be true
			end
		end

		context "quando é saae e não possui tipo de administracao como autaria" do
			it "retorna mensagem de erro" do
				FactoryBot.create(:orcamento_2016)
				tipo_unidade_prefeitura = Base::TipoDeUnidadeAdministrativa.find_by(codigo: '00')
				unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016, saae: true, tipo_de_unidade_administrativa_id: tipo_unidade_prefeitura.id)
				unidade_orcamentaria.validate
				expect(unidade_orcamentaria.errors[:tipo_de_unidade_administrativa_id]).to include "SAAE deve ter o tipo de unidade administrativa Autarquia"
			end
		end
	end

	it 'valor_total_fixado_da_despesa' do
		orcamento_da_despesa = FactoryBot.create( :orcamento_da_despesa_saude)
		unidade_orcamentaria = orcamento_da_despesa.elemento_de_despesa_por_subacao.subacao.unidade_orcamentaria
		expect(unidade_orcamentaria.valor_total_fixado_da_despesa).to eq 12345.67
	end

	context "Resgata ordenador de despesa do ano anterior" do
		it "Quando ordenador não tem fim de gestão" do
			unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
			unidade_orcamentaria.save(validate: false)
			ordenador = FactoryBot.create(:ordenador_de_despesa, unidade_orcamentaria_id: unidade_orcamentaria.id)
			unidade_orcamentaria_2017 = FactoryBot.build(:loa_pref, :orcamento_2017)
			unidade_orcamentaria_2017.save(validate: false)
			unidade_orcamentaria_2017.resgata_ordenador_antigo
			expect(unidade_orcamentaria_2017.reload.ordenadores_de_despesa.last.agente_publico_id).to eq ordenador.agente_publico_id
		end
		it "Quando ordenador tem fim de gestão" do
			unidade_orcamentaria = FactoryBot.build(:loa_pref, :orcamento_2016)
			unidade_orcamentaria.save(validate: false)
			ordenador = FactoryBot.create(:ordenador_de_despesa, unidade_orcamentaria_id: unidade_orcamentaria.id, data_fim_de_gestao: Date.today)
			unidade_orcamentaria_2017 = FactoryBot.build(:loa_pref, :orcamento_2017)
			unidade_orcamentaria_2017.save(validate: false)
			unidade_orcamentaria_2017.resgata_ordenador_antigo
			expect(unidade_orcamentaria_2017.reload.ordenadores_de_despesa).to be_empty
		end
	end
end
