class Licitacao::Rodada < ApplicationRecord
	has_paper_trail

  attr_accessor :arquivo_bll

	attr_default :desempate, false

	belongs_to :lote
	has_many :lances, dependent: :destroy

	before_validation :processo_desempate, if: :desempate

	accepts_nested_attributes_for :lances

	before_validation :atribui_numero, if: Proc.new { new_record? && !numero && lote  }
	before_validation :sinaliza_caso_ultimo_lance

	after_create :gera_lista_de_lances, unless: Proc.new { desempate || numero == 0 }
	after_create :gera_lista_de_lances_desempate, if: Proc.new { desempate && lote && lote.primeiro_colocado && arquivo_bll.nil? }
	after_update :gera_nova_rodada, if: Proc.new { concluida? && !desempate }

	validates_presence_of :lote_id
	validates_uniqueness_of :desempate, scope: [:lote_id], if: :desempate

	validate :nao_deve_estar_concluida, unless: Proc.new { !self.rodada_anterior.present? || self.rodada_anterior.numero == 0}
	validate :lances_obedecem_ordem_decrescente
	validate :todos_os_itens_do_lote_devem_ter_cotacoes
	validate :rodada_anterior_deve_estar_concluida, unless: Proc.new { !self.rodada_anterior.present? || self.rodada_anterior.numero == 0}
	validate :lote_deve_estar_aberto_a_lances
	validate :valores_dos_lances_maior_que_zero
	validate :lance_deve_ser_o_menor_da_rodada, unless: Proc.new { self.desempate? || lances.count <= 1}
	validate :lance_deve_ser_menor_ou_igual_da_rodada_final, if: Proc.new { self.desempate? || lances.count == 1 }

	validates_associated :lances

	scope :concluidas, -> { joins(:lances).where("(valor is not null and valor > 0) or desistir is true").distinct.order(:numero) }


	def rodada_anterior
		lote.rodadas.find_by(numero: self.numero - 1) if self.numero
	end

	def concluida?
		lances.count > 0 && lances.nao_concluidos.count == 0
	end

	def menor_lance
		lances.minimum(:valor)
	end

	def maior_lance
		lances.maximum(:valor)
	end

	private

		def gera_nova_rodada
			so_uma_empresa_deu_valor = lances.to_a.count{|lance| lance.valor } == 1
			lote.rodadas.create(desempate: so_uma_empresa_deu_valor)
		end

		def atribui_numero
			self.numero = if lote.rodadas.any? && self.lote.rodadas.order(:numero).first.numero == 0
				self.lote.rodadas.count
			else
				lote.rodadas.count + 1
			end
		end

		def nao_deve_estar_concluida
			errors.add(:base, "Não é possível atualizar pois a rodada já está concluída") if concluida?
		end

		def todos_os_itens_do_lote_devem_ter_cotacoes
			existe_cotacao_para_todos_os_itens_do_lote = false
			lote.processo.pessoas_do_projeto.classificados.each do |pessoa_do_projeto|
				existe_cotacao_para_todos_os_itens_do_lote = lote.itens_do_lote.ativos.all? do |item_do_lote|
					pessoa_do_projeto.itens_do_projeto_por_pessoa.where(item_do_lote_id: item_do_lote.id).size > 0
				end
				break if existe_cotacao_para_todos_os_itens_do_lote == true
			end
			if lote.processo.global?
				errors.add(:base, "Todos os itens devem ter propostas preenchidas") unless existe_cotacao_para_todos_os_itens_do_lote
			elsif lote.processo.por_item?
				errors.add(:base, "Nenhuma empresa preencheu proposta para este item") unless existe_cotacao_para_todos_os_itens_do_lote
			elsif lote.processo.por_lote?
				errors.add(:base, "Nem todos os itens do lote tem proposta preenchida") unless existe_cotacao_para_todos_os_itens_do_lote
			end
		end

		def rodada_anterior_deve_estar_concluida
			if self.lote.present?
				if rodada_anterior.present?
					errors.add(:lote_id, "A rodada anterior não foi concluída") unless rodada_anterior.concluida?
				end
			end
		end

		def lote_deve_estar_aberto_a_lances
			errors.add(:base, "O lote está fechado para lances") unless lote && lote.lances_abertos?
		end

		def lances_obedecem_ordem_decrescente
			unless lances.sort_by(&:valor_anterior).reverse.map(&:valor).compact.each_cons(2).all? { |a, b| a.present? && b.present? && ((a <=> b) > 0) }
				errors.add(:base, "Não é possível atualizar pois um valor posterior é maior ou igual a um anterior")
			end
		end

		def valores_dos_lances_maior_que_zero
			lances.each do |lance|
				unless lance.desistir?
					return errors.add(:base, "Não é possível atualizar pois um ou mais valores estão vazios ou zerado") if (lance.valor.nil?|| lance.valor <= 0)
				end
			end
		end

		def lance_deve_ser_o_menor_da_rodada
			menor_lance_anterior = lances.sort_by(&:valor_anterior).map(&:valor_anterior).first
			menor_lance_anterior_id = lances.sort_by(&:valor_anterior).map(&:id).first

			lances.each do |lance|
				unless lance.desistir? || lance.valor.nil? || lance.rodada.lote.lote_por_desconto?
					return errors.add(:base, "Não é possível atualizar pois um ou mais valores não é o menor lance da rodada anterior") unless lance.valor.to_f.round(2) < menor_lance_anterior.to_f.round(2) || lance.id == menor_lance_anterior_id
				end
			end
		end

		def lance_deve_ser_menor_ou_igual_da_rodada_final
			menor_lance_anterior = lances.sort_by(&:valor_anterior).map(&:valor_anterior).first

			if lances.count == 1
				if !lances.first.desistir? && !lances.first.valor.nil? && !self.desempate? && !lote.lote_por_desconto?
					return errors.add(:base, "Não é possível atualizar pois o valor deve ser menor ou igual ao lance da rodada anterior") if lances.first.valor.to_f.round(2) > menor_lance_anterior.to_f.round(2)
				end
			end
		end

		def gera_lista_de_lances
			elegiveis = lote.pessoas_do_projeto_elegiveis_para_lances_verbais
			if self.lote.processo.present? && elegiveis.any?
				if self.numero == 1
					elegiveis.each do |pessoa_do_projeto|
						lance = self.lances.new(pessoa_do_projeto: pessoa_do_projeto)
						lance.save(validate: false)
					end
				else
					elegiveis.each do |pessoa_do_projeto|
						lance_anterior = self.rodada_anterior.lances.find_by(pessoa_do_projeto: pessoa_do_projeto)
						desistiu = lance_anterior.nil? || lance_anterior.desistir?
						unless desistiu
							lance = self.lances.new(pessoa_do_projeto: pessoa_do_projeto)
							lance.save(validate: false)
						end
					end
				end
			end
		end

		def gera_lista_de_lances_desempate
			lance_ganhador = lances.new(pessoa_do_projeto: lote.primeiro_colocado[:pessoa_do_projeto])
			lance_ganhador.save(validate: false)
			if lote.lote_por_preco?
				unless lote.primeiro_colocado[:pessoa_do_projeto].pessoa.me_ou_epp_ou_mei?
					lote.empresas_empatadas.each { |candidato|
						lance = lances.new(pessoa_do_projeto: candidato[:pessoa_do_projeto])
						lance.save(validate: false)
					}
				end
			end
		end

		def sinaliza_caso_ultimo_lance
			lances_ativos = lances.reject {|lance| lance.desistir? }
			if lances_ativos.size == 1
				lances_ativos[0].lance_final = true
			end
		end

		def processo_desempate
			lances_dados = lances.reject{ |lance| lance.valor == nil}
			lances_empresas_grande_porte = lances.reject { |lance| lance.pessoa_do_projeto.pessoa.porte != 'grande_porte'}
			if !lances_dados.blank?
				if lances_dados.count > 1
					errors.add(:base, "Na rodada de desempate só deve haver um lance")
				else
					if lances_empresas_grande_porte.count > 1
						lances_empresas_grande_porte.each do |lance|
							lance.desistir = true
						end
					end
				end
			end
		end
end
