module Base
	class FonteDeRecursos < ApplicationRecord
		include TradutorConcern
		has_paper_trail

		attr_default :de_convenio, false

		belongs_to :modulo, polymorphic: true, required: true
		belongs_to :grupo_da_fonte_de_recursos
		belongs_to :fonte_stn, class_name: 'Base::FontesDeRecursos::FonteStn'

		has_one :percentuais_mensais, class_name: 'Base::Rcl', as: :modulo, dependent: :destroy
		has_many :orcamento_da_receita, class_name: 'Loa::OrcamentoDaReceita', dependent: :destroy
		has_many :orcamentos_da_despesa, class_name: 'Loa::OrcamentoDaDespesa', dependent: :restrict_with_exception
		has_many :elementos_de_despesa_por_subacao, through: :orcamentos_da_despesa, class_name: 'Loa::ElementoDeDespesaPorSubacao'
		has_many :subacoes, through: :elementos_de_despesa_por_subacao, class_name: 'Loa::Subacao'
		has_many :transferencias_de_recursos, class_name: 'Loa::TransferenciaDeRecurso', dependent: :restrict_with_exception
		has_many :contigenciamentos, class_name: 'Contabilidade::Contigenciamento'
		has_many :solicitacoes_de_alteracao_orcamentaria, class_name: 'Contabilidade::SolicitacaoDeAlteracaoOrcamentaria'
		has_many :fontes_de_recursos_da_conta_bancaria, class_name: 'Base::FonteDeRecursoDaContaBancaria', foreign_key: :fonte_de_recurso_id
		has_many :contas_bancarias, through: :fontes_de_recursos_da_conta_bancaria, source: :conta_bancaria, class_name: 'Base::ContaBancaria'
		has_many :movimentacoes_por_fonte_de_recursos, class_name: "Contabilidade::MovimentacaoPorFonteDeRecursos", dependent: :destroy
		has_many :fontes_de_recursos_de_restos_a_pagar, class_name: "Contabilidade::FonteDeRecursosDeRestosAPagar", dependent: :destroy

		accepts_nested_attributes_for :percentuais_mensais, allow_destroy: true

		validates_presence_of :codigo, :descricao, :modulo_id, :modulo_type

		validates :modulo_id, immutable: true
		validates :modulo_type, immutable: true

		validates_length_of :codigo, is: 2
		validates_length_of :descricao, maximum: 250

		validates_numericality_of :percentual_minimo_de_destinacao, greater_than_or_equal_to: 0, less_than: 100, allow_nil: true

		validates :modulo_id, immutable: true
		validates :modulo_type, immutable: true

		enum tipo_de_arrecadacao: {
			ordinario: 0,
			vinculado: 1
		}

		enum tipo_de_vinculo: {
			educacao: 0,
			saude: 1,
			previdencia_social_rpps: 2,
			previdencia_social_rgps: 3,
			assistencia_social: 4,
			outras_destinacoes: 5
		}

		before_validation :destroi_percentual_vazio

		ransacker :codigo_completo do
			Arel.sql("base_fontes_stn.codigo_tipo_exercicio::text || base_fontes_stn.codigo_principal::text || base_fontes_stn.detalhamento_sintetico::text || base_fontes_de_recursos.codigo::text")
		end

		def codigo_completo
			"#{grupo_da_fonte_de_recursos.codigo.to_i}""#{codigo}"
		end

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

		def codigo_completo_e_descricao
			"#{codigo_completo} - #{descricao}"
		end

		def codigo_completo_e_descricao_e_exercicio
			"#{codigo_completo_e_descricao} - #{modulo.exercicio}"
		end

		def descricao_e_exercicio
			"#{descricao} - #{modulo.exercicio}"
		end

		def destroi_percentual_vazio
				meses = [:janeiro, :fevereiro, :marco, :abril, :maio, :junho, :julho, :agosto, :setembro, :outubro, :novembro, :dezembro]

				if percentuais_mensais.present?
					if meses.all? { |mes| percentuais_mensais.send("percentual_#{mes}").blank? }
						percentuais_mensais.mark_for_destruction
					end
				end
		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 total_da_receita
			self.orcamento_da_receita.sum(:valor).to_d
		end

		def total_da_despesa tipo_de_orcamento_id = nil
			if tipo_de_orcamento_id
				self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: :subacao).where(loa_subacoes: {tipo_de_orcamento_id: tipo_de_orcamento_id}).sum(:valor).to_f
			else
				self.orcamentos_da_despesa.sum(:valor).to_f
			end
		end

		def total_da_despesa_por_grupo_de_natureza_da_despesa_tipo_de_orcamento_e_uo(grupo_de_natureza_da_despesa_id, tipo_de_orcamento_id, uo_id)
			statement_grupo = grupo_de_natureza_da_despesa_id.present? ? { base_grupos_de_natureza_da_despesa: {id: grupo_de_natureza_da_despesa_id} } : nil
			statement_tipo_de_orcamento = tipo_de_orcamento_id.present? ? { loa_subacoes: {tipo_de_orcamento_id: tipo_de_orcamento_id} } : nil
			statement_unidade_orcamentaria = uo_id.present? ? {loa_subacoes: {unidade_orcamentaria_id: uo_id}} : nil

			self.orcamentos_da_despesa.joins(elemento_de_despesa_por_subacao: {
				subacao: {}, elemento_de_despesa: {modalidade_de_aplicacao: :grupo_de_natureza_da_despesa}
			}).where(statement_grupo).where(statement_tipo_de_orcamento).where(statement_unidade_orcamentaria).sum(:valor).to_f
		end

		def diferenca_entre_receita_e_despesa
			(self.total_da_receita - self.total_da_despesa).to_f
		end

		def entradas_por_taloes(conta_bancaria)
			movimentacoes_da_conta = conta_bancaria.movimentacoes_da_conta_bancaria
			taloes = movimentacoes_da_conta.por_talao.map(&:modulo)

			return 0 if taloes.empty?

			orcamentos_ids = []
			entradas = 0
			taloes.select{|talao| talao.try(:orcamentario?) }.each do |talao|
				orcamento_da_receita = talao.orcamentos_da_receita.find_by(fonte_de_recursos_id: self.id)
				if orcamento_da_receita.present?
					valor_na_fonte = orcamento_da_receita.try(:valor_arrecadado).try(:to_f)

					entradas += (!orcamentos_ids.include?(orcamento_da_receita.id) ? valor_na_fonte.to_f : 0)
					orcamentos_ids.push(orcamento_da_receita.id)
				end
			end
			entradas
		end

		def entradas_por_transferencias(conta_bancaria)
			movimentacoes_da_conta = conta_bancaria.movimentacoes_da_conta_bancaria
			transferencias = movimentacoes_da_conta.por_transferencia.map(&:modulo)

			return 0 if transferencias.empty?

			transferencias.inject(0) do |entradas, transferencia|
				mesma_fonte = transferencia.fonte_de_recurso_destino_id == self.id
				mesma_conta = transferencia.conta_bancaria_destino.conta_bancaria_id == conta_bancaria.id

				entradas + (mesma_fonte && mesma_conta ? transferencia.valor.to_f : 0)
			end
		end

		def total_de_entradas_por_conta(conta_bancaria)
			entradas_por_taloes(conta_bancaria) + entradas_por_transferencias(conta_bancaria)
		end

		def total_de_saldo_inicial_por_conta(conta_bancaria)
			@total_de_saldo_inicial_por_conta ||= fontes_de_recursos_da_conta_bancaria.where(conta_bancaria_id: conta_bancaria.try(:id)).sum{ |fr| fr.saldo_inicial }
		end

		def saidas_por_pagamentos(conta_bancaria)
			movimentacoes_da_conta = conta_bancaria.movimentacoes_da_conta_bancaria
			pagamentos = movimentacoes_da_conta.por_pagamento.map(&:modulo)

			return 0 if pagamentos.empty?

			pagamentos.inject(0) do |saidas, pagamento|
				if pagamento.present?
					mesma_fonte = pagamento.empenho.fonte_de_recursos.id == self.id

					valor_na_fonte = pagamento.contas_bancarias_por_pagamento.find_by(conta_bancaria_id: conta_bancaria.id).valor_pago.to_f
					saidas + (mesma_fonte ? valor_na_fonte : 0)
				else
					0
				end
			end
		end

		def saidas_por_transferencias(conta_bancaria)
			movimentacoes_da_conta = conta_bancaria.movimentacoes_da_conta_bancaria
			transferencias = movimentacoes_da_conta.por_transferencia.map(&:modulo)

			return 0 if transferencias.empty?

			transferencias.inject(0) do |saidas, transferencia|
				mesma_fonte = transferencia.fonte_de_recurso_origem_id == self.id
				mesma_conta = transferencia.conta_bancaria_origem.conta_bancaria_id == conta_bancaria.id

				saidas + (mesma_fonte && mesma_conta ? transferencia.valor.to_f : 0)
			end
		end

		def total_de_saidas_por_conta(conta_bancaria)
			saidas_por_pagamentos(conta_bancaria) + saidas_por_transferencias(conta_bancaria)
		end

		def saldo_por_conta(conta_bancaria)
			total_de_saldo_inicial_por_conta(conta_bancaria) + total_de_entradas_por_conta(conta_bancaria) - total_de_saidas_por_conta(conta_bancaria)
		end

		def self.recursos_ordinarios # correspondente a fonte 101
			find_by(codigo: '01')
		end

		def self.recursos_de_educacao # correspondente a fonte 102
			find_by(codigo: '02')
		end
		def self.recursos_de_saude # correspondente a fonte 103
			find_by(codigo: '03')
		end

		def codigo_sim
			if self.class.name.eql?("Base::FontesDeRecursos::Fonte2018")
				self.codigo.to_s.sim_preenche(2)
			elsif self.class.name.eql?("Base::FontesDeRecursos::FonteTCE")
				self.codigo_completo[1..9].to_s.sim_preenche(9)
			else
				self.codigo.to_s.sim_preenche(2)
			end
		end

		def codigo_tcm
			if self.class.name.eql?("Base::FontesDeRecursos::Fonte2018")
				self.codigo.to_s
			elsif self.class.name.eql?("Base::FontesDeRecursos::FonteTCE")
				self.codigo_completo[1..9].to_s
			else
				self.codigo.to_s
			end
		end
		

		def retorna_complementos_por_fonte(complementos, unidade_orcamentaria)
			if ["1124000000", "1125000001", "1125000000", "1190000000", "1214000000", "1215000000", "1220000001", "1290000000",
				"1311000000", "1312000000", "1312000001", "1390000000", "1510000000", "1550000000", "1940000000", "1940000001"].include?(self.try(:codigo_completo))
				complementos = complementos.select {|complemento| ["transferencia_da_uniao_de_emendas_parlam_individuais", "transferencia_da_uniao_de_emendas_parlam_de_bancada", "sem_complemento"].include?(complemento)} #0000 #3110 #3120
			elsif ["1410000001", "1410000002", "1420000001", "1420000002"].include?(self.try(:codigo_completo))
				if unidade_orcamentaria.try(:tipo_de_unidade_administrativa).try(:legislativo?)
					complementos = complementos.select {|complemento| ["beneficios_prev_poder_legislativo_plano_prev", "beneficios_prev_poder_legislativo_plano_financeiro"].include?(complemento)} #1121 #2121
				elsif unidade_orcamentaria.try(:tipo_de_unidade_administrativa).try(:executivo?)
					complementos = complementos.select {|complemento| ["beneficios_prev_poder_executivo_plano_prev", "beneficios_prev_poder_executivo_plano_financeiro"].include?(complemento)} #1111 #2111
				end
			elsif ["1706000000", "2706000000"].include?(self.try(:codigo_completo))
				complementos = complementos.select {|complemento| ["transferencia_da_uniao_de_emendas_parlam_individuais", "transferencia_da_uniao_de_emendas_parlam_de_bancada"].include?(complemento)} #3110 #3120
			elsif ["1710000000", "2710000000"].include?(self.try(:codigo_completo))
				complementos = complementos.select {|complemento| ["transferencia_dos_estados_de_emendas_parlam_individuais", "transferencia_dos_estados_de_emendas_parlam_de_bancada"].include?(complemento)} #3210 #3220
			else
				complementos = complementos.select {|complemento| "sem_complemento".eql?(complemento)} #0000
			end
			return complementos
		end
	end

	def fonte_de_recurso_do_orcamento?(orcamento)
		modulo_type == orcamento.class.name && modulo_id == orcamento.id
	end
end
