class Licitacao::ItemDoLote < ApplicationRecord
	has_paper_trail
	include IncrementadorDeCodigoConcern

	attr_default :anulado, false
	attr_default :sequencia, 0

	belongs_to :lote
	belongs_to :item_do_pedido

	delegate :quantidade_total_requisitada, to: :item_do_pedido, allow_nil: true
	delegate :quantidade_total_requisitada_sem_periodicidade, to: :item_do_pedido, allow_nil: true
	delegate :quantidade_por_unidade_orcamentaria_sem_periodicidade, to: :item_do_pedido, allow_nil: true
	delegate :descricao, to: :item_do_pedido, allow_nil: true
	delegate :item, to: :item_do_pedido, allow_nil: true
	delegate :codigo_e_descricao_do_item, to: :item
	delegate :codigo_descricao_unidade_codigo_da_prefeitura, to: :item
	delegate :projeto, :processo, to: :lote

	has_many :itens_do_projeto_por_pessoa
	has_many :itens_do_contrato
	has_many :itens_de_ordem_de_compra_diretas, -> {
		joins(ordem_de_compra: :empenho).where(
			contabilidade_empenhos: { contrato_id: nil }
		)
	}, class_name: "Licitacao::ItemDaOrdemDeCompra"

	validates_uniqueness_of :item_do_pedido_id, scope: [:lote_id]

	validates_presence_of :motivo_da_anulacao, :if => :anulado?
	validates_absence_of :motivo_da_anulacao, :unless => :anulado?

	after_update :anula_lote, if: Proc.new { lote && lote.itens_do_lote.ativos.empty? }
	before_destroy :reordenar_lote

	scope :ativos, -> { where(anulado: false) }
	scope :anulados, -> { where(anulado: true) }
	scope :produtos, -> { joins(:lote).merge(Licitacao::Lote.produto) }
	scope :itens_dos_lotes_ativos, -> { ativos.joins(:lote).merge(Licitacao::Lote.ativo) }
	scope :validos, -> { where("licitacao_itens_do_lote.anulado is false or licitacao_itens_do_lote.anulado is null") }
	scope :por_preco, -> { joins(:item_do_pedido).merge(Licitacao::ItemDoPedido.por_preco) }
	scope :por_desconto, -> { joins(:item_do_pedido).merge(Licitacao::ItemDoPedido.por_desconto) }

	before_create :gerar_identificador
	before_create :define_ordem, unless: Proc.new { Configuracao.last.ordencacao_de_itens_pela_sequencia? }

	# BOOLEANS
	def possui_saldo?
		saldo > 0
	end

	def pode_ser_anulado?
		processo.aberto? && processo.pedido.projeto_simplificado.blank?
	end

	def pode_ter_lances?
		processo.pregao? && processo.em_sessao? && processo.por_item? && (!lote.melhor_tecnica? && !lote.melhor_tecnica_ou_conteudo_artistico? && !lote.tecnica_e_preco?) && !lote.fracassado? && !lote.deserto?
	end

	# VALORES
	def preco_unitario
		if projeto.try(:pedido).try(:projeto_simplificado).present? && !projeto.bid_obra_ou_servico?
			item_do_pedido.preco_estimativo
		elsif lote.lote_por_valor_previsto?
			item_do_pedido.valor_total_previsto_por_desconto
		else
			itens_do_projeto_por_pessoa.find_by(final: true).try(:preco).to_f
		end
	end

	def preco_unitario_por_unidade_orcamentaria unidade
		item_do_pedido.valor_previsto_por_unidade_orcamentaria(unidade)
	end

	def preco_final
		
		preco_unitario * quantidade_total_requisitada.to_d
	end

	def valor_estimativo
		if projeto.pedido.projeto_simplificado?
			item_do_pedido.preco_estimativo
		end
	end

	def valor_previsto_desconto
		if lote.lote_por_desconto?
			item_do_pedido.maior_desconto
		else
			valor_unitario
		end
	end

	def valor_unitario
		if !projeto.pedido.projeto_simplificado?
			if projeto.menor_valor?
				projeto.global? ? valor_unitario_global : item_do_pedido.menor_preco
			elsif projeto.valor_medio?
				item_do_pedido.preco_medio
			elsif projeto.por_mediana?
				item_do_pedido.mediana
			end
		else
			valor_estimativo
		end
	end

	def valor_unitario_global
		fornecedores_com_todos_os_itens_cotados = projeto.pedido.fornecedores_com_todos_os_itens_cotados
		if !fornecedores_com_todos_os_itens_cotados.nil? && !fornecedores_com_todos_os_itens_cotados.empty?
			pessoa_do_pedido_menor_cotacao = fornecedores_com_todos_os_itens_cotados.sort_by { |fornecedor| fornecedor.total_da_cotacao }.first
			item_do_pedido_por_pessoa = Licitacao::ItemDoPedidoPorPessoa.find_by(item_do_pedido_id: item_do_pedido.id, pessoa_do_pedido_id: pessoa_do_pedido_menor_cotacao.id)
			return item_do_pedido_por_pessoa.preco_de_cotacao
		else
			# projeto global precisa de todos os itens cotados por pelo menos um fornecedor, caso contrario o valor global é zero
			0
		end
	end

	def valor_unitario_global_por_pessoa pessoa_do_pedido
		if pessoa_do_pedido.present?
			Licitacao::ItemDoPedidoPorPessoa.find_by(item_do_pedido_id: item_do_pedido.id, pessoa_do_pedido_id: pessoa_do_pedido.id).preco_de_cotacao.to_f
		end
	end

	def valor_total_global_por_unidade unidade_orcamentaria_id
		valor_unitario_global.to_f * item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id)
	end

	def valor_total_global
		valor_unitario_global.to_f * item_do_pedido.quantidade_total_requisitada
	end

	def valor_total
		if !projeto.pedido.projeto_simplificado?
			if projeto.menor_valor?
				item_do_pedido.menor_total
			elsif projeto.valor_medio?
				item_do_pedido.total_medio
			end
		end
	end

	# def valor_total_por_item_da_ordem_de_compra(item_da_ordem_de_compra_id)
	# 	preco_unitario * Licitacao::ItemDaOrdemDeCompra.find(item_da_ordem_de_compra_id).quantidade
	# end

	def valor_total_por_item
		preco_unitario * quantidade_contratada
	end

	def valor_total_por_unidade(unidade_orcamentaria_id)
		if item_do_pedido.por_valor_previsto?
			item_do_pedido.valor_previsto_por_unidade_orcamentaria(unidade_orcamentaria_id)
		else
			(valor_unitario * item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id))
		end		
	end

	def preco_total_por_unidade(unidade_orcamentaria_id)
		(preco_unitario * item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id)).round(2)
	end

	def itens_por_unidade unidade_orcamentaria_id
		unidade_orcamentaria_atual = Loa::UnidadeOrcamentaria.find(unidade_orcamentaria_id)
		unidades_vinculadas_ids = unidade_orcamentaria_atual.unidades_orcamentaria_vinculada
			.pluck(:unidade_orcamentaria_vinculada_id)

		unidades_pelo_codigo_ids = Loa::UnidadeOrcamentaria.joins(:orgao)
			.where(codigo: unidade_orcamentaria_atual.codigo, loa_orgaos: { codigo: unidade_orcamentaria_atual.orgao.codigo }).pluck(:id)

		unidades_ids = unidades_vinculadas_ids + unidades_pelo_codigo_ids

		unidade_orcamentaria_do_pedido = item_do_pedido.pedido.unidades_orcamentarias_por_pedido.where(unidade_orcamentaria_id: unidades_ids).first
		item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_do_pedido.unidade_orcamentaria.id)
	end

	def itens_por_unidade_sem_periodicidade unidade_orcamentaria_id
		item_do_pedido.quantidade_por_unidade_orcamentaria_sem_periodicidade(unidade_orcamentaria_id)
	end

	def itens_por_unidade_valor_previsto unidade_orcamentaria_id
		item_do_pedido.valor_previsto_por_unidade_orcamentaria(unidade_orcamentaria_id)
	end

	def saldo_por_item
		saldo * preco_unitario
	end

	def retorna_item_por_unidade_orcamentaria_do_pedido(unidade_orcamentaria_por_pedido_id)
		self.item_do_pedido.pedido.itens_do_pedido_por_unidade_orcamentaria.find_by(unidade_orcamentaria_por_pedido_id: unidade_orcamentaria_por_pedido_id, item_do_pedido_id: self.item_do_pedido_id)
	end

	def saldo
		quantidade_total_requisitada - quantidade_contratada - quantidade_empenhada_diretamente
	end

	def saldo_em_porcentagem
		(saldo * 100)/quantidade_total_requisitada
	end

	def quantidade_contratada
		itens_do_contrato.sum(:quantidade)
	end

	def quantidade_empenhada_diretamente
		Contabilidade::ItemDoEmpenho.joins(:empenho).where(contabilidade_empenhos: { projeto_id: self.projeto.id, contrato_id: nil , descrimina_itens_processo_ou_contrato: true }, item: item).where('contabilidade_empenhos.status <> 5').sum(:quantidade)
	end

	def quantidade_utilizada
		quantidade_contratada.to_i + quantidade_empenhada_diretamente.to_i
	end

	def porcentagem_do_valor_total_a_contratar
		total = preco_unitario * quantidade_total_requisitada
		return 0 if total <= 0

		(saldo_por_item / total) * 100
	end

	def saldo_a_contratar_por_unidade(unidade_orcamentaria_id, ata_de_registro_de_preco)
		if self.item_do_pedido.por_valor_previsto == true
			(preco_unitario_por_unidade_orcamentaria(unidade_orcamentaria_id) + ata_de_registro_de_preco.itens_do_aditivo_da_ata.find_by(item_do_lote: self.id).try(:valor_total).to_f ) - saldo_quantidade_contratada(unidade_orcamentaria_id)
		else
			item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id) - total_contratado_por_unidade(unidade_orcamentaria_id) - total_empenhado_diretamente(unidade_orcamentaria_id)
		end
	end

	def saldo_por_unidade(unidade_orcamentaria_id)
		item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id) - total_contratado_por_unidade(unidade_orcamentaria_id) - total_empenhado_diretamente(unidade_orcamentaria_id)
	end

	def saldo_por_unidade_em_porcentagem(unidade_orcamentaria_id)
		total_por_unidade = item_do_pedido.quantidade_por_unidade_orcamentaria(unidade_orcamentaria_id)
		return 0 if total_por_unidade <= 0

		(saldo_por_unidade(unidade_orcamentaria_id)/total_por_unidade) * 100
	end

	def valor_a_contratar unidade_orcamentaria_id
		saldo_por_unidade(unidade_orcamentaria_id) * preco_unitario
	end

	def valor_contratado unidade_orcamentaria_id
		total_contratado_por_unidade(unidade_orcamentaria_id) * itens_do_contrato.por_unidade_do_pedido(unidade_orcamentaria_id).sum(:valor)
	end

	def total_contratado_por_unidade(unidade_orcamentaria_id)
		itens_do_contrato.por_unidade_do_pedido(unidade_orcamentaria_id).sum(:quantidade)
	end

	def total_aditivado_por_unidade(unidade_orcamentaria_id)
		itens_do_contrato.por_unidade_do_pedido(unidade_orcamentaria_id).sum(&:quantidade_aditivada)
	end

	def total_comprado_diretamente_por_unidade unidade_orcamentaria_id
		itens_de_ordem_de_compra_diretas.sum(:quantidade)
	end

	def total_empenhado_diretamente unidade_orcamentaria_id
		projeto.itens_dos_empenhos_diretos.joins(empenho: [orcamento_da_despesa: [elemento_de_despesa_por_subacao: [subacao: :unidade_orcamentaria]]]).where(item: item).where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}).sum(:quantidade)
	end

	def marca_do_item
		return item_do_pedido.try(:marca) if projeto.simplificado?

		itens_do_projeto_por_pessoa.find_by(final: true).try(:marca)
	end

	def saldo_quantidade_contratada unidade_orcamentaria_id
		if (preco_unitario_por_unidade_orcamentaria(unidade_orcamentaria_id) * total_contratado_por_unidade(unidade_orcamentaria_id)) != 0
			quantidade = quantidade_por_unidade_orcamentaria_sem_periodicidade(unidade_orcamentaria_id) > 0 ? quantidade_por_unidade_orcamentaria_sem_periodicidade(unidade_orcamentaria_id) : 1
			return (preco_unitario_por_unidade_orcamentaria(unidade_orcamentaria_id) * total_contratado_por_unidade(unidade_orcamentaria_id)) / quantidade
		else
			return 0
		end
	end

	def valor_previsto_de_itens_por_desconto
		self.itens_do_pedido_por_unidade_orcamentaria.joins(:item_do_pedido).where(licitacao_itens_do_pedido: {tipo: Licitacao::ItemDoPedido.tipos["por_desconto"]}).sum(:valor_previsto_desconto)
	end

	# AÇÕES
	def define_ordem
		self.ordem = self.projeto.itens_do_lote.count + 1
	end

	def reordenar_lote
		ordem = self.ordem
		self.projeto.itens_do_lote.where("ordem > ?", self.ordem).each do |item_do_lote|
			item_do_lote.update_column(:ordem, ordem)
			ordem += 1
		end
	end

	def anula_lote
		lote.update_columns(anulado: true, numero: nil)
		projeto.reatribuir_numeracao_lotes
	end

	# TCM
	def to_sim
		begin
			licitante_ganhador = lote.ganhador.pessoa
			valida_descricao_do_item
	
			texto = ""
			texto << "506".to_s.sim_preenche(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","
			texto << projeto.data_de_autuacao.sim_data + ","
			texto << projeto.numero_do_processo.sim_limite(15) + ","
			texto << self.ordem.to_s + ","
			texto << item.descricao.gsub(/["\r\n]/, "").sim_limite(255) + ","
			texto << item.unidade_de_medida.descricao.sim_limite(10) + ","
			texto << format("%.2f", item_do_pedido.quantidade_total_requisitada.round(2)) + ","
			texto << format("%.6f", preco_unitario) + ","
			texto << format("%.2f", preco_final) + ","
			texto << (licitante_ganhador.pessoa_juridica? ? "1" : "2").sim_limite(1) + ","
			texto << (licitante_ganhador.pessoa_juridica? ? licitante_ganhador.cnpj : licitante_ganhador.cpf).sim_limite(25) + ","
			return texto
		rescue => e
			if e.class.to_s == "NoMethodError"
				atributo_falho = e.message.split(" ")[2]
				coluna = CSV.parse(texto, :headers => false).last.count
				raise e.mensagem_traduzida(self, "", atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def valida_descricao_do_item
		if item.descricao.gsub(/["\r\n]/, "").size < 5
			raise "Item do Processo Nº: #{projeto.numero_do_processo.to_s}, deve ter descrição maior ou igual a 5 caracteres: #{item.codigo_e_descricao_do_item}"
		end
	end

	def saldo_preenchido_na_unidade(unidade_orcamentaria_id)
		self.item_do_pedido.itens_do_pedido_por_unidade_orcamentaria.joins(:unidade_orcamentaria_por_pedido).where(licitacao_unidades_orcamentarias_por_pedido: {unidade_orcamentaria_id: unidade_orcamentaria_id}).sum(&:valor_previsto_desconto)
	end

	private
	def gerar_identificador
		self.identificador = rand(10000000..99999999)
	end
end
