require 'rails_helper'
require 'licitacao_helper'

RSpec.configure do |c|
	c.include LicitacaoHelper
end

RSpec.describe Licitacao::Rodada, type: :model do
	it { is_expected.to belong_to(:lote) }
	it { is_expected.to have_many(:lances).dependent(:destroy) }

	it { is_expected.to validate_presence_of :lote_id }

	before(:each) do
		allow_any_instance_of(Licitacao::Rodada).to receive(:todos_os_itens_do_lote_devem_ter_cotacoes).and_return(true)
		projeto = FactoryBot.create(:licitacao_projeto, :por_lote, modalidade_de_licitacao: :pregao_eletronico )
		@lote = projeto.lotes.first
		@lote.update_column(:lances_abertos, true)

		@lote.processo.pessoas_do_projeto.first.itens_do_projeto_por_pessoa.create(
			item_do_lote_id: @lote.itens_do_lote.first.id,
			preco: 12,
			marca: 'teste'
		)
	end

	describe 'validates_uniqueness_of :desempate' do
		before(:each) do
			allow_any_instance_of(Licitacao::Rodada).to receive(:concluida?).and_return(true)
		end
		context 'quando desempate = true' do
			subject { Licitacao::Rodada.new(desempate: true) }
			it { is_expected.to validate_uniqueness_of(:desempate).scoped_to(:lote_id) }
		end

		context 'quando desempate = false' do
			subject { Licitacao::Rodada.new(desempate: false) }
			it { is_expected.not_to validate_uniqueness_of :desempate }
		end
	end

	describe '#atribui_numero' do
		context 'quando é a primeira rodada criada' do
			it 'salva numero 1' do
				rodada = @lote.rodadas.create

				expect(rodada.numero).to eq 1
			end
		end
		context 'quando é a segunda rodada criada' do
			it 'salva numero 2' do
				@lote.rodadas.create
				rodada = @lote.rodadas.create

				expect(rodada.numero).to eq 2
			end
		end
	end

	describe '#sinaliza_caso_ultimo_lance' do
		context 'quando há apenas um lance válido' do
			it 'atribui lance_final = true ao lance' do
				rodada = FactoryBot.create :licitacao_rodada, lote_id: @lote.id

				atributos_rodada =  {
					lances_attributes: {
						'0': {
							id: rodada.lances.first.id,
							valor: nil,
							desistir: true
						},
						'1': {
							id: rodada.lances.second.id,
							valor: 1
						}
					}
				}

				rodada.update(atributos_rodada)

				expect(rodada.lances.second.lance_final).to be_truthy
			end
		end
		context 'quando há mais de um lance válido' do
			it 'nenhum lance possui lance_final = true' do
				rodada = FactoryBot.create :licitacao_rodada, lote_id: @lote.id

				atributos_rodada =  {
					lances_attributes: {
						'0': {
							id: rodada.lances.first.id,
							valor: 1
						},
						'1': {
							id: rodada.lances.second.id,
							valor: 1
						}
					}
				}

				rodada.update(atributos_rodada)

				expect(rodada.lances).to all(satisfy { |lance| lance.lance_final.nil? })
			end
		end
	end

	describe '#rodada_anterior' do
		context 'quando é a primeira rodada criada' do
			it 'retorna nil' do
				rodada = @lote.rodadas.create
				expect(rodada.rodada_anterior).to eq nil
			end
		end

		context 'quando é a segunda rodada criada' do
			it 'retorna a primeira rodada' do
				primeira_rodada = @lote.rodadas.create
				segunda_rodada = @lote.rodadas.create

				expect(segunda_rodada.rodada_anterior).to eq primeira_rodada
			end
		end
	end

	describe '#concluida?' do
		context 'quando todos os lances tem valor' do
			it 'retorna true' do
				rodada = @lote.rodadas.create
				rodada.lances.update_all(valor: 10)
				expect(rodada.concluida?).to be_truthy
			end
		end

		context 'quando há lances sem valor' do
			it 'retorna false' do
				rodada = @lote.rodadas.create
				expect(rodada.concluida?).to be_falsy
			end
		end
	end

	describe '#menor_lance' do
		it 'retorna o menor lance da rodada' do
			rodada = @lote.rodadas.create
			primeiro_lance = rodada.lances.first
			primeiro_lance.update(valor: 10)
			rodada.lances.create(valor: 15, pessoa_do_projeto_id: primeiro_lance.pessoa_do_projeto_id)
			rodada.lances.create(valor: 1, pessoa_do_projeto_id: primeiro_lance.pessoa_do_projeto_id)
			expect(rodada.menor_lance).to eq 1
		end
	end

	describe '#rodada_anterior_deve_estar_concluida' do
		context 'quando a é a primeira rodada' do
			it 'salva rodada pois não existe rodada anterior' do
				rodada = @lote.rodadas.create

				expect(rodada).to be_persisted
			end
		end

		context 'quando é a segunda rodada' do
			context 'com a primeira rodada concluída' do
				it 'salva a rodada' do
					primeira_rodada = @lote.rodadas.create
					primeira_rodada.lances.update_all(valor: 10)

					segunda_rodada = @lote.rodadas.create
					expect(segunda_rodada).to be_persisted
				end
			end

			context 'com a primeira rodada não concluída' do
				it 'retorna erro' do
					_primeira_rodada = @lote.rodadas.create
					segunda_rodada = @lote.rodadas.create

					expect(segunda_rodada.errors[:lote_id]).to include "A rodada anterior não foi concluída"
				end
			end
		end
	end

	describe '#gera_lista_de_lances' do
		let(:lote) { @lote }
		let(:processo) { @lote.processo }
		it 'salva lances' do
			rodada = @lote.rodadas.create
			expect(rodada.lances.count).to be > 0
		end

		it 'gera lances apenas para pessoas do projeto elegíveis' do
			cria_pessoas_do_projeto_e_propostas(quantidade: 5, intervalo: 3) # 100 -> 103 -> 106 -> 109 -> 112,  tornando os 4 primeiros elegíveis
			rodada = @lote.rodadas.create
			expect(rodada.lances.map(&:pessoa_do_projeto)).to match_array(@lote.pessoas_do_projeto_elegiveis_para_lances_verbais)
		end

		context 'quando há desistência em uma rodada anterior' do
			it 'retira a empresa que desistiu da lista de lances' do
				primeira_rodada = @lote.rodadas.create
				primeira_rodada.lances.update_all(valor: 10)
				pessoas_do_projeto_ids = primeira_rodada.lances.pluck(:pessoa_do_projeto_id)

				lance_desistencia = primeira_rodada.lances.first
				lance_desistencia.update!(desistir: true, valor: nil)
				pessoa_do_projeto_removida_id = lance_desistencia.pessoa_do_projeto.id

				segunda_rodada = @lote.rodadas.create
				expect(segunda_rodada.lances.pluck(:pessoa_do_projeto_id)).to eq (pessoas_do_projeto_ids - [pessoa_do_projeto_removida_id])
			end
		end
	end

	describe '#gera_nova_rodada' do
		context 'depois do update' do
			context 'quando rodada não foi concluída' do
				it 'não cria nova rodada' do
					rodada = FactoryBot.create :licitacao_rodada
					expect { rodada.save }.not_to change(Licitacao::Rodada, :count)
				end
			end
			context 'quando rodada foi concluída' do
				context 'sobraram empresas' do
					it 'cria nova rodada com desempate = false' do
						rodada = FactoryBot.create :licitacao_rodada, lote_id: @lote.id

						atributos_rodada =  {
							lances_attributes: {
									'0': {
										id: rodada.lances.first.id,
										valor: 1,
									},
									'1': {
										id: rodada.lances.second.id,
										valor: 1,
									}
							}
						}

						expect {
							rodada.assign_attributes(atributos_rodada)
							rodada.save(validate: false)
						}.to change(Licitacao::Rodada, :count).by(1)
						expect(Licitacao::Rodada.last.desempate).to be_falsey
					end
				end
				context 'sobrou apenas uma empresa' do
					it 'cria nova rodada com desempate = true' do
						rodada = FactoryBot.create :licitacao_rodada, lote_id: @lote.id
						atributos_rodada =  {
							lances_attributes: {
									'0': {
										id: rodada.lances.first.id,
										valor: 1,
									},
									'1': {
										id: rodada.lances.second.id,
										desistir: true,
									}
							}
						}

						expect {
							rodada.assign_attributes(atributos_rodada)
							rodada.save(validate: false)
						}.to change(Licitacao::Rodada, :count).by(1)
						expect(Licitacao::Rodada.last.desempate).to be_truthy
					end
				end
			end
		end
	end

	describe '#nao_deve_estar_concluida' do
		context 'retorna erro de rodada concluída' do
			it 'retorna erro' do
				primeira_rodada = @lote.rodadas.create
				segunda_rodada = @lote.rodadas.create
				allow(segunda_rodada).to receive(:concluida?).and_return(true)
				segunda_rodada.validate
				expect(segunda_rodada.errors[:base]).to include "Não é possível atualizar pois a rodada já está concluída"
			end
		end

		context 'quando a rodada não está concluida e há um update' do
			it 'não retorna erro de rodada concluída' do
				rodada = FactoryBot.create :licitacao_rodada, lote_id: @lote.id
				atributos_rodada =  {
					lances_attributes: {
							'0': {
								id: rodada.lances.first.id,
								valor: 1,
							}
					}
				}
				rodada.update(atributos_rodada)
				expect(rodada.errors[:base]).not_to include "Não é possível atualizar pois a rodada já está concluída"
			end
		end
	end

	describe 'lote_deve_estar_aberto_a_lances' do
		context 'quando o lote está aberto a lances' do
			it 'salva rodada' do
				lote = FactoryBot.create :licitacao_lote, lances_abertos: true
				rodada = FactoryBot.build :licitacao_rodada, lote_id: lote.id
				rodada.validate
				expect(rodada).to be_valid
			end
		end
		context 'quando o lote está fechado para lances' do
			it 'retorna erro' do
				lote = FactoryBot.create :licitacao_lote, lances_abertos: false
				rodada = FactoryBot.build :licitacao_rodada, lote_id: lote.id
				rodada.validate
				expect(rodada.errors[:base]).to include("O lote está fechado para lances")
			end
		end
	end

	describe '#lances_obedecem_ordem_decrescente' do
		before(:each) do
			projeto = FactoryBot.create :licitacao_projeto, :por_lote
			@lote = projeto.lotes.first
			@lote.update_column(:lances_abertos, true)
			processo = @lote.processo
			processo.pessoas_do_projeto.create!(pessoa_id: FactoryBot.create(:pessoa_juridica).id)

			@rodada = @lote.rodadas.create
			@lance1 = @rodada.lances.first
			@lance2 = @rodada.lances.second

			allow(@lance1).to receive(:valor_anterior).and_return(120)
			allow(@lance1).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@lance2).to receive(:valor_anterior).and_return(110)
			allow(@lance2).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@rodada).to receive(:concluida?).and_return(false)
		end
		context 'caso os lances estejam em ordem decrescente' do
			it 'salva' do
				@lance1.update(valor: 100)
				@lance2.update(valor: 90)
				@rodada.validate
				expect(@rodada).to be_valid
			end
		end
		context 'caso os lances não estejam em ordem decrescente' do
			it 'não salva' do

				@lance1.update(valor: 90)
				@lance2.update(valor: 100)
				@rodada.validate
				expect(@rodada.errors[:base]).to include("Não é possível atualizar pois um valor posterior é maior ou igual a um anterior")
			end
		end
	end

	describe 'valores_dos_lances_maior_que_zero' do
		before(:each) do
			projeto = FactoryBot.create :licitacao_projeto, :por_lote
			@lote = projeto.lotes.first
			@lote.update_column(:lances_abertos, true)
			processo = @lote.processo
			processo.pessoas_do_projeto.create!(pessoa_id: FactoryBot.create(:pessoa_juridica).id)

			@rodada = @lote.rodadas.create
			@lance1 = @rodada.lances.first
			@lance2 = @rodada.lances.second

			allow(@lance1).to receive(:valor_anterior).and_return(120)
			allow(@lance1).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@lance2).to receive(:valor_anterior).and_return(110)
			allow(@lance2).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@rodada).to receive(:concluida?).and_return(false)
		end
		context 'Sem lance' do
			it 'salva rodada' do
				@lance1.update(valor: "", desistir: true)
				@lance2.update(valor: 90)
				@rodada.validate
				expect(@rodada).to be_valid
			end
		end
		context 'valor invalido' do
			it 'retorna erro' do
				@lance1.update(valor: "")
				@lance2.update(valor: 90)
				@rodada.validate
				expect(@rodada.errors[:base]).to include("Não é possível atualizar pois um ou mais valores estão vazios ou zerado")
			end
		end
	end

	describe 'lance_deve_ser_menor_da_rodada' do
		before(:each) do
			projeto = FactoryBot.create :licitacao_projeto, :por_lote
			@lote = projeto.lotes.first
			@lote.update_column(:lances_abertos, true)
			processo = @lote.processo
			processo.pessoas_do_projeto.create!(pessoa_id: FactoryBot.create(:pessoa_juridica).id)

			@rodada = @lote.rodadas.create
			@lance1 = @rodada.lances.first
			@lance2 = @rodada.lances.second

			allow(@lance1).to receive(:valor_anterior).and_return(120)
			allow(@lance1).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@lance2).to receive(:valor_anterior).and_return(110)
			allow(@lance2).to receive(:menor_valor_da_rodada_anterior).and_return(110)

			allow(@rodada).to receive(:concluida?).and_return(false)
		end
		context 'Sem lance' do
			it 'salva rodada' do
				@lance1.update(valor: "", desistir: true)
				@lance2.update(valor: 90)
				@rodada.validate
				expect(@rodada).to be_valid
			end
		end
		context 'valor invalido' do
			it 'retorna erro' do
				atributos_rodada =  {
					lances_attributes: {
							'0': {
								id: @rodada.lances.first.id,
								valor: 210,
							},
							'1': {
								id: @rodada.lances.second.id,
								desistir: true,
							}
					}
				}
				@rodada.assign_attributes(atributos_rodada)
				@rodada.validate
				expect(@rodada.errors[:base]).to include("Não é possível atualizar pois um ou mais valores não é o menor lance da rodada anterior")
			end
		end
	end
end
