class Administrativo::ItemDaRequisicaoDeMaterial < ApplicationRecord
has_paper_trail

	attr_accessor :quantidade_disponivel_attribute
	attr_accessor :unidade_de_medida_texto
	attr_accessor :enviando_ao_almoxarifado_pelo_patrimonio

	belongs_to :estoque ,class_name: "GestaoDeEstoque::Estoque"
	belongs_to :item, class_name: "Base::Item"
	belongs_to :unidade_de_medida, class_name: "UnidadeDeMedida"
  belongs_to :requisicao_de_material, class_name: "Administrativo::RequisicaoDeMaterial"
	belongs_to :detalhamento_da_requisicao_de_material, class_name: "Administrativo::DetalhamentoDaRequisicaoDeMaterial"
	belongs_to :centro_de_custo_da_requisicao, class_name: "Controladoria::CentroDeCustoDaRequisicao"

	has_many :movimentacoes_do_estoque, as: :origem
	has_many :demandas_programadas, class_name: 'Administrativo::DemandaProgramada', dependent: :destroy

	accepts_nested_attributes_for :demandas_programadas, reject_if: :all_blank, allow_destroy: true

	delegate :descricao_codigo_na_prefeitura_e_unidade_destacada, :codigo_e_descricao, :codigo_e_descricao_do_item, to: :item
	delegate :recebimento_de_material, to: :requisicao_de_material

	validates_associated :demandas_programadas
	validates :demandas_programadas, uniq_nested_attributes: { atributo: :data_do_atendimento, mensagem: "Já existe uma demanda para essa data" }
	validates_numericality_of :quantidade_atendida, less_than_or_equal_to: -> (item) {item.saldo_atual_do_recebimento_de_material}, if: Proc.new { |item| item.utiliza_quantidade_regular? }
	validates_numericality_of :quantidade_requisitada, less_than_or_equal_to: -> (item) {item.saldo_atual_do_recebimento_de_material}, if: Proc.new { |item| item.utiliza_quantidade_regular? }

	validates_numericality_of :quantidade_atendida, less_than_or_equal_to: -> (item) {item.quantidade_disponivel_patrimonio}, if: Proc.new { |item| item.utiliza_quantidade_patrimonial? }
	validates_numericality_of :quantidade_requisitada, less_than_or_equal_to: -> (item) {item.quantidade_disponivel_patrimonio}, if: Proc.new { |item| item.utiliza_quantidade_patrimonial? }

	validates_numericality_of :quantidade_atendida, less_than_or_equal_to: -> (item) {item.quantidade_requisitada}, if: Proc.new { |item| item.quantidade_requisitada.present? && item.quantidade_atendida.present?}

	validate :quantidade_requisitada_deve_ser_maior_que_zero, unless: :eh_avulsa? && :possui_classificacao?

	#after_save :adiciona_movimentacao_no_estoque, if: Proc.new { self.quantidade_atendida.to_i > 0 && (self.requisicao_de_material.atendido? || self.requisicao_de_material.atendido_parcialmente?) }
	before_save :preenche_item_id
	validates_uniqueness_of :item_id, scope: :requisicao_de_material_id, message: 'Deve ser único'
	before_save :atualiza_quantidade_atendida_conforme_a_demanda_programada, if: :possui_demanda_programada?

	def preenche_item_id
		if self.estoque_id.present?
			self.item_id = self.estoque.item_id
			self.unidade_de_medida_id = self.estoque.unidade_de_medida_id
			self.valor_unitario = self.estoque.valor_unitario_medio
			if self.quantidade_atendida.to_i > 0 && self.valor_unitario.to_f > 0
				self.valor_total = (self.valor_unitario * self.quantidade_atendida).to_f
			end
		end
	end

	def adiciona_ou_atualiza_saldo_no_estoque
		begin
			if self.estoque_id.present?
				self.estoque.update_columns(
					saldo_da_ordem_de_compra: saldo_do_estoque
				)
				self.adiciona_movimentacao_no_estoque
			else
				estoque = GestaoDeEstoque::Estoque.new(
					item_id: self.item_id,
					unidade_de_medida_id: retorna_unidade_de_medida_ou_conversao.id,
					saldo_da_ordem_de_compra: saldo_do_estoque,
					unidade_orcamentaria_id: self.requisicao_de_material.unidade_orcamentaria_id,
					almoxarifado_id: self.requisicao_de_material.almoxarifado_id,
					tipo_de_material: self.requisicao_de_material.tipo_de_material,
					sub_elemento_de_despesa_id: self.requisicao_de_material.sub_elemento_de_despesa_id
				)
				self.estoque_id = estoque.id if estoque.save
				self.adiciona_movimentacao_no_estoque
			end

			estoque_destino = GestaoDeEstoque::Estoque.where(item_id: self.item_id, unidade_de_medida_id: self.try(:item).try(:unidade_de_medida).id, almoxarifado_id: self.requisicao_de_material.almoxarifado_destino_id, unidade_orcamentaria_id: self.requisicao_de_material.unidade_orcamentaria).first rescue nil
      if estoque_destino.nil?
        estoque = GestaoDeEstoque::Estoque.new(
          item_id: self.item_id,
          unidade_de_medida_id: self.try(:item).try(:unidade_de_medida).id,
          saldo_da_ordem_de_compra: saldo_do_estoque,
          unidade_orcamentaria_id: self.requisicao_de_material.unidade_orcamentaria_id,
          almoxarifado_id: self.requisicao_de_material.almoxarifado_destino_id,
          valor_unitario_medio: self.estoque.valor_unitario_medio,
          tipo_de_material: self.estoque.tipo_de_material,
          sub_elemento_de_despesa_id: self.estoque.sub_elemento_de_despesa_id
        )
        estoque.save
        self.adiciona_movimentacao_no_estoque_de_destino(estoque)
      else
        self.adiciona_movimentacao_no_estoque_de_destino(estoque_destino)
      end
		rescue Exception => e
			raise e
		end
	end

	def adiciona_movimentacao_no_estoque
		begin

			if self.estoque.present? && self.estoque.persisted?
				GestaoDeEstoque::MovimentacaoDoEstoque.create!(
					estoque_id: self.estoque.id,
					quantidade_saida: self.requisicao_de_material.avulsa? ? nil : self.quantidade_atendida.to_f,
					origem_id: self.requisicao_de_material.id,
					origem_type: self.requisicao_de_material.class.name,
					orcamento_id: self.requisicao_de_material.orcamento_id,
					unidade_orcamentaria_id: requisicao_de_material.unidade_orcamentaria_id,
					almoxarifado_id: requisicao_de_material.almoxarifado_id,
					valor_unitario: self.valor_unitario,
					valor_total: self.valor_total
				)
				if eh_avulsa?
					(self.requisicao_de_material.existe_item_atendido? && self.requisicao_de_material.atendimento_parcial?) ? self.requisicao_de_material.atendido_parcialmente! : self.requisicao_de_material.atendido!
				end
				self.desconta_saldo_do_sub_estoque
			end
		rescue Exception => e
			raise e
		end
	end

	def adiciona_movimentacao_no_estoque_de_destino(estoque)
    begin

      if self.estoque.present? && self.estoque.persisted?
        GestaoDeEstoque::MovimentacaoDoEstoque.create!(
          estoque_id: estoque.id,
          quantidade_entrada: self.quantidade_atendida.to_f,
          origem_id: self.requisicao_de_material.id,
          origem_type: self.requisicao_de_material.class.name,
          orcamento_id: self.requisicao_de_material.orcamento_id,
          unidade_orcamentaria_id: requisicao_de_material.unidade_orcamentaria_id,
          almoxarifado_id: requisicao_de_material.almoxarifado_destino_id,
          valor_unitario: self.valor_unitario,
          valor_total: self.valor_total
        )
      end
    rescue Exception => e
      raise e
    end
  end

	def quantidade_requisitada_demanda_programada
		self.demandas_programadas.inject(0){ |total, demanda|
			total + demanda.quantidade_atendida.to_f
		}
	end

	def quantidade_atendida_demanda_programada
		self.demandas_programadas.inject(0){ |total, demanda|
			total + demanda.quantidade_atendida.to_f
		}
	end

	def atualiza_quantidade_atendida_conforme_a_demanda_programada
		self.quantidade_atendida = quantidade_atendida_demanda_programada
	end

	def possui_demanda_programada?
		self.demandas_programadas.present?
	end

	def possui_demanda_programada_para_hoje?
		self.demandas_programadas.select { |demanda| demanda.possui_demanda_programada_para_hoje? }.any?
	end

	def quantidade_total_requisitada_da_demanda_programada
		self.demandas_programadas.sum(&:quantidade_requisitada).to_f
	end

	def possui_conversao_de_unidade_de_medida?
		self.requisicao_de_material.recebimento_de_material.itens_do_recebimento_de_materiais.where(item_id: self.item_id).where.not(unidade_de_medida_de_conversao_id: nil).present? rescue false
	end

	def possui_recebimento_de_material?
		self.requisicao_de_material.recebimento_de_material.present?
	end

	def possui_classificacao?
		self.requisicao_de_material.classificacao.present? rescue false
	end

	def eh_avulsa?
		self.requisicao_de_material.avulsa? rescue false
	end

	def unidade_de_medida_do_recebimento_de_material
		self.requisicao_de_material.recebimento_de_material.itens_do_recebimento_de_materiais.find_by(item_id: self.item_id).try(:unidade_de_medida_de_conversao)
	end

	def quantidade_total_disponivel_da_ordem_de_compra
		self.requisicao_de_material.recebimento_de_material.ordem_de_compra.itens_da_ordem_de_compra.find_by(item_id: self.item_id).quantidade.to_f rescue 0.0
	end

	def marca_item_da_requisicao
		self.requisicao_de_material.recebimento_de_material.itens_do_recebimento_de_materiais.find_by(item_id: self.item_id).try(:marca)
	end

	def debita_saldo_atual_da_ordem_de_compra
		saldo_atual_da_ordem_de_compra - self.quantidade_requisitada.to_f
	end

	def saldo_atual_da_ordem_de_compra
		saldo_ordem_de_compra = estoque.saldo_da_ordem_de_compra.to_f rescue 0.0
		if saldo_ordem_de_compra.to_f <= 0
			quantidade_total_disponivel_da_ordem_de_compra.to_f
		else
			saldo_ordem_de_compra.to_f
		end
	end

	def debita_saldo_atual_da_ordem_de_compra_avulso
		saldo_atual_da_ordem_de_compra_avulso - self.quantidade_requisitada.to_f
	end

	def saldo_atual_do_recebimento_de_material
		estoque.quantidade_total_saldo.to_f rescue 0.0
	end

	def debita_saldo_ao_recebimento_de_material
		saldo_atual_do_recebimento_de_material.to_f - quantidade_atendida.to_f
	end

	def quantidade_requisitada_deve_ser_maior_que_zero
		errors.add(:quantidade_requisitada, "A quantidade requisitada é obrigatória e não pode ser menor ou igual a zero") if (!eh_avulsa? && (self.quantidade_requisitada.nil? || self.quantidade_requisitada.to_f <= 0))
	end

	def quantidade_disponivel_para_devolucao
		quantidade_devolvida = GestaoDeEstoque::ItemDaDevolucaoDeMaterial.joins(:devolucao_de_material).where('gestao_de_estoque_devolucoes_de_materiais.status not in (3, 5) and gestao_de_estoque_devolucoes_de_materiais.origem_id = ? and gestao_de_estoque_devolucoes_de_materiais.origem_type = ? and item_id = ?', self.requisicao_de_material_id, self.requisicao_de_material.class.name, self.item_id).sum(:quantidade_devolvida)
		quantidade_atendida.to_f - quantidade_devolvida.to_f
	end

	def saldo_do_estoque
		total = estoque.quantidade_total_saldo.to_f rescue 0.0
			(total - retorna_quantidade_ou_quantidade_de_conversao.to_f).to_f
		end

	def retorna_unidade_de_medida_ou_conversao
			self.possui_conversao_de_unidade_de_medida? ? self.try(:unidade_de_medida_de_conversao) : self.try(:item).try(:unidade_de_medida)
		end

	def retorna_quantidade_ou_quantidade_de_conversao
			self.quantidade_atendida
		end

	def unidade_de_medida_descricao
		self.estoque.unidade_de_medida_descricao rescue ""
	end

	def quantidade_mais_proxima_a_vencer
		estoque.quantidade_mais_proxima_a_vencer rescue 0.0
	end

	def desconta_saldo_do_sub_estoque
		quantidade = self.quantidade_atendida

		while(quantidade > 0)
			sub_estoque_menor_validade = self.estoque.sub_estoques_por_validade.where("quantidade > 0 ").order("validade ASC").first rescue nil
			break if sub_estoque_menor_validade.nil?
			if quantidade >= sub_estoque_menor_validade.quantidade
				quantidade = quantidade - sub_estoque_menor_validade.quantidade
				sub_estoque_menor_validade.update_attribute(:quantidade, 0)
			else
				sub_estoque_menor_validade.update_attribute(:quantidade, sub_estoque_menor_validade.quantidade - quantidade)
				break
			end
		end
	end

	def quantidade_disponivel_patrimonio
		estoque.quantidade_total_saldo_nao_disponivel.to_d rescue 0
	end

	def utiliza_quantidade_regular?
		if recebimento_de_material.present?
			estoque.present? && quantidade_atendida.present? && (recebimento_de_material.patrimonial? == false || self.enviando_ao_almoxarifado_pelo_patrimonio.nil?)
		else
			false
		end
	end

	def utiliza_quantidade_patrimonial?
		if recebimento_de_material.present?
			estoque.present? && quantidade_requisitada.present? && recebimento_de_material.patrimonial? && self.enviando_ao_almoxarifado_pelo_patrimonio == true
		else
			false
		end
	end
end
