module Base
	class ElementoDeDespesa < ApplicationRecord
		include ClassificacaoDaDespesaConcern
		include CalculaValoresDeElementos

		has_paper_trail

		attr_default :exibir_elemento_de_despesa, false
		attr_default :exibir_elemento_de_despesa, 0
		attr_default :preferencialmente_pessoa_fisica, 0
		attr_default :preferencialmente_pessoa_juridica, 0
		attr_default :folha_de_pagamento, 0
		attr_default :despesa_financeira, 0
		attr_default :compoe_obras, 0
		attr_default :despesa_pessoal, 0
		attr_default :deducao_despesa_pessoal, 0
		attr_default :transferencia_intraorcamentaria, 0
		attr_default :deducao_pasep, 0
		attr_default :contabil_obrigatorio, 0
		attr_default :padrao_rpps, 0

		belongs_to :elemento_de_gasto, class_name: 'NaturezaDaDespesa::ElementoDeDespesa'
		belongs_to :modalidade_de_aplicacao
		has_one :percentuais_mensais, class_name: 'Base::Rcl', as: :modulo, dependent: :destroy

		accepts_nested_attributes_for :percentuais_mensais, allow_destroy: true

		validates_presence_of :codigo, :descricao

		validates_uniqueness_of :codigo, :descricao, scope: :modalidade_de_aplicacao_id, conditions: -> { where.not(modalidade_de_aplicacao_id: '') }
		# validate :codigo_deve_estar_contido_nos_dados_base, unless: :reserva_de_contingencia?
		# validate :descricao_deve_estar_contida_nos_dados_base, unless: :reserva_de_contingencia?
		validate :valida_valores_de_configuracao

		has_many :sub_elementos_de_despesa, class_name: 'Contabilidade::SubElementoDeDespesa', dependent: :destroy
		has_many :sub_elementos_de_despesa_da_matriz, class_name: 'Contabilidade::SubElementoDeDespesaDaMatriz', dependent: :destroy
		has_many :sub_elementos_de_despesa_com_matriz_vinculada, class_name: 'Contabilidade::SubElementoDeDespesaComMatrizVinculada' , dependent: :destroy

		has_many :empenhos, through: :sub_elementos_de_despesa, class_name: 'Contabilidade::Empenho'
		has_many :liquidacoes, through: :empenhos, class_name: "Contabilidade::Liquidacao"
		has_many :pagamentos, through: :liquidacoes, class_name: "Contabilidade::Pagamento"

		has_many :elementos_de_despesa_por_subacao, class_name: 'Loa::ElementoDeDespesaPorSubacao', dependent: :destroy
		has_many :orcamentos_da_despesa, through: :elementos_de_despesa_por_subacao
		has_many :lancamentos_do_orcamento_da_despesa, through: :orcamentos_da_despesa
		has_many :elementos_por_subacao_da_solicitacao, class_name: 'Contabilidade::ElementoPorSubacaoDaSolicitacao'

		accepts_nested_attributes_for :sub_elementos_de_despesa_com_matriz_vinculada, allow_destroy: true, reject_if: :all_blank
		after_create :seta_elemento_de_gasto, if: Proc.new { elemento_de_gasto.nil? }

		scope :ativos, ->{where ("base_elementos_de_despesa.inativo is not TRUE")}
		scope :correntes_intra, -> { where("base_elementos_de_despesa.codigo SIMILAR TO ?", '3\d91\d{4}') }
		scope :capital_intra, -> { where("base_elementos_de_despesa.codigo SIMILAR TO ?", '4\d91\d{4}') }
		scope :intra_orcamentarios, -> { where("base_elementos_de_despesa.codigo SIMILAR TO ?", '\d\d91\d{4}') }
		scope :exceto_intra_orcamentarios, -> { where("substring(base_elementos_de_despesa.codigo from 3 for 2) != '91'") }

		def codigo_e_descricao
			"#{codigo_formatado} - #{descricao}"
		end

		def descricao_e_codigo
			"#{descricao} - #{codigo_formatado}"
		end

		def reserva_de_contingencia?
			descricao == 'Reserva de Contingência' && codigo == '99999900'
		end

		def contribuicoes_patronais?
			descricao == 'Contribuições Patronais' && codigo == '31911300'
		end

		def obra?
			categoria == "51" #Obras e instalações
		end

		def reconhecimento_de_divida?
			categoria == "92"
		end

		def material_de_consumo?
			categoria == "30"
		end

		def material_de_consumo_com_distribuicao_gratuita?
			categoria == "32"
		end

		def material_permanente?
			categoria == "52"
		end

		def tipo_de_orcamento_pela_despesa elemento_de_despesa
			elemento_de_despesa.orcamentos_da_despesa.each do |orcamento_da_despesa|
				return orcamento_da_despesa.tipo_de_orcamento
			end
		end

		def valor_total_fixado_da_despesa tipo_de_orcamento=nil
			tipo_de_orcamento = :itself unless ["fiscal", "social"].include? tipo_de_orcamento.to_s
			self.orcamentos_da_despesa.send(tipo_de_orcamento).sum(:valor)
		end

		def valor_total_realizado_da_despesa
			self.orcamentos_da_despesa.sum(:valor_empenhado)
		end

		def tem_folha_de_pagamento?
			pode_ter_folha_de_pagamento?
		end

		def pode_ter_folha_de_pagamento?
			['sim', 'geralmente_sim'].include?(self.configuracao_do_atributo(:folha_de_pagamento))
		end

		def configuracao_do_atributo atributo
			case self[atributo].to_i
				when 1
					'sim'
				when 2
					'geralmente_nao'
				when 3
					'geralmente_sim'
				else
					'nao'
			end
		end

		def reserva_de_contingencia?
			descricao == 'Reserva de Contingência' && codigo == '99999900'
		end

		def valor_previsto_por_unidade_orcamentaria unidade_orcamentaria_id
			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}).distinct.sum(:valor)
		end

		def valor_previsto_por_unidades_orcamentarias unidades_orcamentarias
			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {unidade_orcamentaria_id: unidades_orcamentarias.ids}).distinct.sum(:valor)
		end
		
		def orcamentos_da_despesa_por_unidade_orcamentaria unidade_orcamentaria_id
			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {unidade_orcamentaria_id: unidade_orcamentaria_id}).distinct.sum(:valor)
		end
		
		def valor_previsto_por_unidades_orcamentarias_e_fontes_de_recurso codigos_das_fontes,unidades_orcamentarias
			self.orcamentos_da_despesa.joins(:fonte_de_recursos,elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {unidade_orcamentaria_id: unidades_orcamentarias.ids}, base_fontes_de_recursos:{codigo: codigos_das_fontes}).distinct.sum(:valor)
		end
		
		def total_previsto_por_unidades_orcamentarias unidades_orcamentarias
			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: { subacao: :unidade_orcamentaria }).where('loa_unidades_orcamentarias.id in (?)', unidades_orcamentarias.ids ).sum(:valor)
		end
		
		def total_previsto_por_unidades_orcamentarias_e_fontes_de_recurso(codigos_das_fontes, unidades_orcamentarias)
			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: { subacao: :unidade_orcamentaria }, fonte_de_recursos: {}).where(loa_unidades_orcamentarias: { id: unidades_orcamentarias.ids }, base_fontes_de_recursos: { codigo: codigos_das_fontes }).distinct.sum(:valor)
		end

		def valor_previsto_por_mes mes, unidade_orcamentaria_id=0
			valor = unidade_orcamentaria_id.to_i > 0 ? valor_previsto_por_unidade_orcamentaria(unidade_orcamentaria_id) : valor_total_fixado_da_despesa
			if self.percentuais_mensais.nil? || percentuais_mensais.percentual_janeiro.nil?
				valor.to_f / 12
			else
				mes.to_i > 0 ? (valor * percentuais_mensais.percentual(mes)) / 100 : 0
			end
		end

		def build_sub_elementos_de_despesa_com_matriz_vinculada
			sub_elementos_de_despesa_com_matriz_vinculada.build if sub_elementos_de_despesa_com_matriz_vinculada.empty?
		end

		private
		def seta_elemento_de_gasto
			if !self.codigo.nil?
				elemento_de_gasto = NaturezaDaDespesa::ElementoDeDespesa.find_by_codigo(self.codigo[4..5])
				self.update_column(:elemento_de_gasto_id, elemento_de_gasto.id) if elemento_de_gasto.present?
			end
		end

		def codigo_deve_estar_contido_nos_dados_base
			if codigo.present?
				unless NaturezaDaDespesa::ElementoDeDespesa.find_by( codigo: codigo[4..5] )
					errors.add(:codigo, 'não existe elemento de despesa com esse código')
				end
			end
		end

		def descricao_deve_estar_contida_nos_dados_base
			if descricao.present? && !contribuicoes_patronais?
				unless NaturezaDaDespesa::ElementoDeDespesa.find_by( descricao: descricao )
					errors.add(:descricao, 'não existe elemento de despesa com essa descrição')
				end
			end
		end

		def valida_valores_de_configuracao
			[:padrao_rpps, :deducao_pasep, :preferencialmente_pessoa_fisica, :despesa_financeira, :compoe_obras,
				:despesa_pessoal, :deducao_despesa_pessoal, :folha_de_pagamento, :transferencia_intraorcamentaria,
				:contabil_obrigatorio, :preferencialmente_pessoa_juridica].each {|atributo|
				errors.add(atributo, "valor inválido") unless CONFIGURACAO_ELEMENTO_DA_DESPESA.values.include?(self[atributo])
			}
		end
	end
end
