class GestaoDeEstoque::Consumo < ApplicationRecord
  has_paper_trail

  include TradutorConcern
  include IncrementadorDeCodigoConcern
  include AASM

  attr_accessor :interno, :outros_procedimentos
  attr_default :usa_escola, false 

  belongs_to :almoxarifado, class_name: "GestaoDeEstoque::Almoxarifado"
  belongs_to :orcamento, class_name: "::Orcamento"
  belongs_to :agente_publico, class_name: "Base::AgentePublicoMunicipal", foreign_key: :responsavel_id
  belongs_to :unidade_orcamentaria, class_name: "Loa::UnidadeOrcamentaria"
  belongs_to :escola, class_name: "GestaoDeEstoque::Escola"

  has_many :programas_por_consumo, class_name: "GestaoDeEstoque::ProgramaPorConsumo", dependent: :destroy
  has_many :itens_do_consumo, class_name: "GestaoDeEstoque::ItemDoConsumo", dependent: :destroy
  accepts_nested_attributes_for :programas_por_consumo, allow_destroy: true, reject_if: :all_blank
  accepts_nested_attributes_for :itens_do_consumo, allow_destroy: true

  validate :nao_pode_criar_consumo_com_mes_encerrado, on: :create
  validate :valida_dados_do_consumo_por_api, on: :create, if: Proc.new{ self.gerado_por_api?}
  
  validates_presence_of :data_de_consumo, :almoxarifado_id
  
  validate :data_de_consumo_nao_pode_ser_de_orcamento_diferente
  validates :data_de_consumo, sabado_ou_domingo_ou_feriado: { flexivel: false }
  validates :itens_do_consumo, uniq_nested_attributes: { atributo: :estoque_id, mensagem: "Estoque deve ser único." }, unless: proc { self.usa_escola? }

  
  before_validation :atribui_numero_disponivel
  
  before_save :check_for_duplicate
  after_destroy :remove_dependentes

  enum status: {
    aberto: 1,
    em_atendimento: 2,
    consumido: 3,
    enviado_ao_almoxarifado: 4,
		recebido_pelo_almoxarifado: 5,
    recusado: 6,
    solicitado: 7,
    cancelado: 8
  }

  enum classificacao: {
    doacao: 1,
    devolucao_estorno: 2,
    ajuste_de_perdas: 3
  }
  
  enum tipo_de_material: {
    consumo: 1,
    permanente: 2,
    consumo_distribuicao_gratuita: 3
  }

  aasm column: :status, enum: true, whiny_transitions: false do
    state :aberto, :initial => true
    state :em_atendimento
    state :consumido
    state :enviado_ao_almoxarifado
    state :recebido_pelo_almoxarifado
    state :recusado

    event :confirmar_consumo do
      transitions from: :aberto, to: :consumido do
        guard do
          self.aberto? && self.itens_do_consumo.size > 0
        end
        after do
          self.itens_do_consumo.each do |item_do_consumo|
            item_do_consumo.adiciona_ou_atualiza_saldo_no_estoque
          end
        end
      end

      transitions from: :recebido_pelo_almoxarifado, to: :consumido do
        guard do
          self.recebido_pelo_almoxarifado?
        end
        after do
          self.itens_do_consumo.each do |item_do_consumo|
            item_do_consumo.adiciona_ou_atualiza_saldo_no_estoque
          end
        end
      end
    end

    event :retornar_para_aberto do
      transitions from: [:consumido, :recusado], to: :aberto do
        guard do 
          (self.consumido? || self.recusado?)
        end
        after do
					self.remove_movimentacao_do_estoque
				end
      end
    end

    event :enviar_ao_almoxarifado do
			transitions from: :aberto, to: :enviado_ao_almoxarifado do
				guard do
					self.aberto?
				end
			end
		end

    event :recusar do
			transitions from: :enviado_ao_almoxarifado, to: :recusado
		end

		event :receber_no_almoxarifado do
			transitions from: :enviado_ao_almoxarifado, to: :recebido_pelo_almoxarifado
		end

  end

  def existe_encerramento?
		encerramento = GestaoDeEstoque::ControleDoAlmoxarifado.all.select{ |control| control.data_de_encerramento.month == self.data_de_consumo.month && control.data_de_encerramento.year == self.data_de_consumo.year }.sort_by(&:data_de_encerramento).last rescue nil if GestaoDeEstoque::ControleDoAlmoxarifado.any? && self.data_de_consumo.present?

    if encerramento.present? && self.data_de_consumo.present?
			false
		else
			true
		end
	end

  def pode_editar?
    (self.aberto? || self.recebido_pelo_almoxarifado?)
  end

  def tem_item_com_saldo_inferior?
    self.itens_do_consumo.select{ |item| item.estoque.quantidade_total_saldo < item.quantidade_consumida }.any?
  end

  def remove_movimentacao_do_estoque
    estoques_id = []
		GestaoDeEstoque::MovimentacaoDoEstoque.where(origem_id: self.id, origem_type: self.class.name).each do |movimentacao_do_estoque|
      estoques_id << movimentacao_do_estoque.estoque_id
      movimentacao_do_estoque.destroy
    end

    GestaoDeEstoque::Estoque.where(id: estoques_id).each do |estoque|
			if GestaoDeEstoque::MovimentacaoDoEstoque.where(estoque_id: estoque.id).blank?
				estoque.destroy
			else
				estoque.calcular_valor_unitario_medio
			end
		end
	end

  def remove_dependentes
    ActiveRecord::Base.connection.execute("DELETE FROM gestao_de_estoque_movimentacoes_do_estoque WHERE origem_id = #{self.id} and origem_type = '#{GestaoDeEstoque::Consumo}'")
  end

  def atribui_numero_disponivel
		if self.data_de_consumo.present? && self.codigo.blank?
			gerar_codigo(self.data_de_consumo, :codigo, :data_de_consumo, :orcamento_id, self.orcamento_id)
		end
	end

  def data_de_consumo_nao_pode_ser_de_orcamento_diferente
    if self.data_de_consumo.present? && self.orcamento_id.present? && self.data_de_consumo.year != self.orcamento.exercicio
      errors.add(:data_de_consumo, "O exercício do consumo não pode ser diferente do exercício do orçamento")
    end
  end

  def nao_pode_criar_consumo_com_mes_encerrado
    if !self.existe_encerramento?
			errors.add(:base, "Não é possível criar um consumo, pois o almoxarifado já foi encerrado no mês de #{Date::MONTHNAMES[self.data_de_consumo.month]}")
		end
  end

  def retorna_todos_sub_elementos
		if tipo_de_material.present?
			Contabilidade::SubElementoDeDespesa.ativos.joins(elemento_de_despesa: [modalidade_de_aplicacao: [grupo_de_natureza_da_despesa: :categoria_economica]])
				.where(
					"base_elementos_de_despesa.codigo = ?
					and base_categorias_economicas.modulo_type = ?", codigo_elemento_de_despesa, 'Orcamento')
				.order(codigo: :asc)
		else
			Array.new
		end
	end

	def codigo_elemento_de_despesa
		if permanente?
			"44905200"
		elsif consumo?
			"33903000"
		else
			"33903200"
		end
	end

  def valida_dados_do_consumo_por_api
    if self.unidade_orcamentaria.present?
      errors.add(:unidade_orcamentaria, "A unidade orçamentária informada não é referente a Educação") unless self.unidade_orcamentaria.codigo_completo == "0810"
    else
      errors.add(:unidade_orcamentaria, "A unidade orçamentária infomrada não foi localizada")
    end

    if self.almoxarifado.present?
      errors.add(:almoxarifado_id, "O almoxarifado informado não pertence a unidade da Educação") unless self.almoxarifado.unidades_orcamentarias.select{|uo| uo if uo.codigo_completo == "0810"}.present?
    else
      errors.add(:almoxarifado_id, "O almoxarifado informado não foi localizado")
    end
  end

  def check_for_duplicate
    programas = []
    self.programas_por_consumo.each do |programa_por_consumo|
      if programas.pluck(:id).include?(programa_por_consumo.programa_por_escola.programa_id)
        programa_por_consumo.delete
      else
        programas << programa_por_consumo.programa_por_escola.programa
      end
    end

  end

end
