require 'rails_helper'

RSpec.describe Contabilidade::MovimentacaoDoPlanoDeContas, type: :model do
	cria_configuracao_default

	it { is_expected.to belong_to :gerador }
	it { is_expected.to belong_to :conta_por_evento_contabil }

	[
		:gerador_id,
		:gerador_type,
		:conta_por_evento_contabil_id,
		:valor,
		:data_de_lancamento,
		:tipo_de_lancamento
	].each { |atributo| it { is_expected.to validate_presence_of atributo } }

	describe '#immutable' do
		context 'gerador_id' do
			it 'não permite alteração' do
				movimentacao_empenho_padrao = FactoryBot.create :movimentacao_empenho_padrao
				movimentacao_empenho_padrao.gerador_id += 1
				movimentacao_empenho_padrao.save

				expect(movimentacao_empenho_padrao.errors[:gerador_id]).to include 'não pode ser alterado'
			end
		end

		context 'gerador_type' do
			it 'não permite alteração' do
				movimentacao_empenho_padrao = FactoryBot.create :movimentacao_empenho_padrao
				movimentacao_empenho_padrao.gerador_type = 'Novo::Gerador'
				movimentacao_empenho_padrao.save

				expect(movimentacao_empenho_padrao.errors[:gerador_type]).to include 'não pode ser alterado'
			end
		end
	end

	describe '#after_create' do
		it 'atualiza o saldo das contas de acordo com o valor da(s) movimentação(ões) e o movimento que está sendo lançado' do
			evento_contabil = FactoryBot.create :evento_contabil, :empenhar

			conta_debito = FactoryBot.create :conta_patrimonial, :sem_validacao
			conta_credito = FactoryBot.create :conta_patrimonial_dois, :sem_validacao

			conta_debito_do_evento_contabil = FactoryBot.create :conta_patrimonial_evento_padrao, :debito, evento_contabil: evento_contabil, conta_id: conta_debito.id
			conta_credito_do_evento_contabil = FactoryBot.create :conta_patrimonial_evento_padrao, :credito, evento_contabil: evento_contabil, conta_id: conta_credito.id

			empenho = FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [evento_contabil]

			conta_debito.reload
			conta_credito.reload

			expect(conta_debito.saldo.to_f).to eq empenho.valor.to_f * -1
			expect(conta_credito.saldo.to_f).to eq empenho.valor.to_f
		end
	end

	describe '#atualiza_saldo_diario_da_conta' do
		before(:each) do
			@evento_contabil = FactoryBot.create :evento_contabil, :empenhar

			@conta_debito = FactoryBot.create :conta_patrimonial, :sem_validacao
			@conta_credito = FactoryBot.create :conta_patrimonial_dois, :sem_validacao

			@conta_debito_do_evento_contabil = FactoryBot.create :conta_patrimonial_evento_padrao, :debito, evento_contabil: @evento_contabil, conta_id: @conta_debito.id
			@conta_credito_do_evento_contabil = FactoryBot.create :conta_patrimonial_evento_padrao, :credito, evento_contabil: @evento_contabil, conta_id: @conta_credito.id
		end

		context 'quando não há nenhum registro de saldo diário da conta no determinado dia e o saldo é atualizado' do
			it 'cria o registro do saldo diário p/ conta débito' do
				expect{
					FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
				}.to change(@conta_debito.saldos_diarios_da_conta, :count).by(1)
			end

			it 'cria o registro do saldo diário p/ conta crédito' do
				expect{
					FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
				}.to change(@conta_credito.saldos_diarios_da_conta, :count).by(1)
			end

			it 'o saldo diário é igual ao saldo da conta de débito e crédito no determinado dia' do
				empenho = FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]

				@conta_debito.reload
				@conta_credito.reload

				saldo_diario_debito = @conta_debito.saldos_diarios_da_conta.find_by(data_do_saldo: empenho.data_de_solicitacao).saldo.to_f
				saldo_diario_credito = @conta_credito.saldos_diarios_da_conta.find_by(data_do_saldo: empenho.data_de_solicitacao).saldo.to_f

				expect(@conta_debito.saldo.to_f).to eq saldo_diario_debito
				expect(@conta_credito.saldo.to_f).to eq saldo_diario_credito
			end
		end

		context 'quando há um registro de saldo diário da conta no determinado dia e o saldo é atualizado' do
			before(:each) do
				@empenho = FactoryBot.create :empenho_22090001, valor: 1000.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
			end
			it 'não cria um novo registro de saldo diário no dia p/ conta débito' do

				expect{
					FactoryBot.create :liquidacao_001, data_da_liquidacao: @empenho.data_do_empenho, eventos_contabeis: [@evento_contabil]
				}.to_not change(@conta_debito.saldos_diarios_da_conta, :count)
			end

			it 'não cria um novo registro de saldo diário no dia p/ conta crédito' do
				expect{
					FactoryBot.create :liquidacao_001, data_da_liquidacao: @empenho.data_do_empenho, eventos_contabeis: [@evento_contabil]
				}.to_not change(@conta_credito.saldos_diarios_da_conta, :count)
			end

			it 'atualiza o registro do saldo diário debito' do
				saldo_diario_debito = @conta_debito.saldos_diarios_da_conta.find_by(data_do_saldo: @empenho.data_de_solicitacao)

				expect {
					@empenho = FactoryBot.create :empenho_22090001, valor: 50.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
					saldo_diario_debito.reload
				}.to change( saldo_diario_debito, :saldo ).by(-50.0)
			end

			it 'atualiza o registro do saldo diário credito' do
				saldo_diario_credito = @conta_credito.saldos_diarios_da_conta.find_by(data_do_saldo: @empenho.data_de_solicitacao)

				expect {
					@empenho = FactoryBot.create :empenho_22090001, valor: 50.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
					saldo_diario_credito.reload
				}.to change( saldo_diario_credito, :saldo ).by(50.0)
			end
		end

		context 'quando há um registro de saldo diário da conta no determinado dia e o gerador é removido' do
			it 'estorna o valor do gerador do saldo diário da conta' do
				empenho = FactoryBot.create :empenho_22090001, valor: 50.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
				liquidacao = FactoryBot.create :liquidacao_001, data_da_liquidacao: empenho.data_do_empenho, eventos_contabeis: [@evento_contabil]

				saldo_diario_debito = @conta_debito.saldos_diarios_da_conta.find_by(data_do_saldo: empenho.data_de_solicitacao)
				saldo_diario_credito = @conta_credito.saldos_diarios_da_conta.find_by(data_do_saldo: empenho.data_de_solicitacao)

				saldo_diario_debito_antes_da_exclusao = saldo_diario_debito.saldo.to_f
				saldo_diario_credito_antes_da_exclusao = saldo_diario_credito.saldo.to_f

				empenho.destroy

				expect(saldo_diario_debito.saldo.to_f).to eq (saldo_diario_debito_antes_da_exclusao)
				expect(saldo_diario_credito.saldo.to_f).to eq (saldo_diario_credito_antes_da_exclusao)
			end
		end

		context 'quando há um registro de saldo diário da conta no determinado dia e o gerador é removido e o saldo diário fica igual a zero' do
			it 'apaga os registros do saldo diário das contas movimentadas' do
				empenho = FactoryBot.create :empenho_22090001, valor: 50.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]

				expect {
					empenho.destroy
				}.to change(Contabilidade::SaldoDiarioDaConta, :count).by(-2)
			end
		end
	end

	describe '#descricao_da_movimentacao' do
		context 'quando o gerador é um(a) Contabilidade::Empenho' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :empenho_22090001
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_do_empenho

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pelo Empenho #{movimentacao.codigo_movimentacao}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::Liquidacao' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :liquidacao_001
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pela Liquidação #{movimentacao.codigo_movimentacao} do Empenho #{gerador.empenho.numero_do_empenho}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::Pagamento' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :pagamento_de_pessoal
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pelo Pagamento #{movimentacao.codigo_movimentacao}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::TalaoDeReceita' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :contabilidade_talao_de_receita
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_do_talao

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pelo Talão #{movimentacao.codigo_movimentacao}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::EstornoDeLiquidacao' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :contabilidade_estorno_de_liquidacao
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_da_liquidacao

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pelo Estorno da Liquidação #{movimentacao.codigo_movimentacao} do Empenho #{gerador.empenho.numero_do_empenho}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::EstornoDePagamento' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :estorno_de_pagamento
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_do_pagamento

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pelo Estorno do Pagamento #{movimentacao.codigo_movimentacao}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::AnulacaoDoEmpenho' do
			it 'retorna mensagem descrevendo a movimentação' do
				empenho = FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
				gerador = FactoryBot.create :anulacao_do_empenho_total, empenho: empenho
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_do_empenho

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pela anulação #{gerador.tipo_de_anulacao} do Empenho #{movimentacao.codigo_movimentacao}."
			end
		end

		context 'quando o gerador é um(a) Contabilidade::AnulacaoDoTalaoDeReceita' do
			it 'retorna mensagem descrevendo a movimentação' do
				gerador = FactoryBot.create :anulacao_do_talao_de_receita
				movimentacao = FactoryBot.create :movimentacao_empenho_padrao, gerador_id: gerador.id, gerador_type: gerador.class.name, codigo_movimentacao: gerador.numero_do_talao

				expect(movimentacao.descricao_da_movimentacao).to eq "Esse movimento foi gerado pela Anulação #{gerador.tipo_de_anulacao} do Talão #{movimentacao.codigo_movimentacao}."
			end
		end
	end

	describe '#desfaz_movimentacao_no_saldo_da_conta' do
		context 'quando gerador for apagado' do
			it 'conta deve voltar ao valor anterior da conta' do
				evento = FactoryBot.create :evento_contabil, :empenhar
				evento.contas_por_eventos_contabeis.find_or_create_by!(FactoryBot.attributes_for(:conta_patrimonial_evento_padrao, :conta_devedor, :debito))
				evento.contas_por_eventos_contabeis.find_or_create_by!(FactoryBot.attributes_for(:conta_patrimonial_evento_padrao, :conta_credor, :credito))

				conta = evento.contas_por_eventos_contabeis.last.conta
				saldo_anterior = conta.saldo
				empenho = FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [evento]
				conta.reload
				expect(conta.saldo).to eq saldo_anterior + empenho.valor
				empenho.destroy
				conta.reload
				expect(conta.saldo).to eq saldo_anterior
			end
		end
	end

	describe '#desfaz_alteracao_no_saldo_diario_da_conta' do
		context 'quando gerador for apagado' do
			it 'conta deve voltar ao valor anterior do saldo diário da conta' do
				@evento_contabil = FactoryBot.create :evento_contabil, :empenhar
				@evento_contabil.contas_por_eventos_contabeis.find_or_create_by!(FactoryBot.attributes_for(:conta_patrimonial_evento_padrao, :conta_devedor, :debito))
				@evento_contabil.contas_por_eventos_contabeis.find_or_create_by!(FactoryBot.attributes_for(:conta_patrimonial_evento_padrao, :conta_credor, :credito))
				empenho = FactoryBot.create :empenho_22090001, valor: 100.to_f, descriminacao_obrigatoria_de_itens: false, eventos_contabeis: [@evento_contabil]
				quantidade_de_contas = empenho.eventos_contabeis.last.contas_por_eventos_contabeis.count
				saldo_da_conta = Contabilidade::SaldoDiarioDaConta.find_by(conta_id: empenho.eventos_contabeis.last.contas_por_eventos_contabeis.last.conta, data_do_saldo: empenho.data_de_solicitacao)
				expect(saldo_da_conta.saldo).to eq empenho.valor
				expect { empenho.destroy }.to change(Contabilidade::SaldoDiarioDaConta, :count).by(-1 * quantidade_de_contas)
			end
		end
	end
end
