class Contabilidade::Conta < ApplicationRecord
	include TradutorConcern

	has_paper_trail

	attr_accessor :nivel_de_consolidacao, :evento_contabil_id, :primeiro_registro, :exibir_somente_com_saldo

	attr_default :nivel_de_consolidacao, false
	attr_default :saldo, 0.00
	attr_default :importada_do_pcasp, false
	attr_default :registro_de_evento_contabil_obrigatorio, false
	attr_default :status, "ativa"
	attr_default :analitica, true

	belongs_to :orcamento
	belongs_to :grupo_de_conta
	belongs_to :conta_pai, class_name: "Contabilidade::Conta"
	has_many :contas_extra_orcamentaria, foreign_key: :subconta_id, class_name: 'Contabilidade::ContaExtraOrcamentaria'

	has_many :saldos_diarios_da_conta
	has_many :contas_por_eventos_contabeis, dependent: :restrict_with_exception
	has_many :movimentacoes_do_plano_de_contas, through: :contas_por_eventos_contabeis
  has_many :movimentacoes_por_fonte_de_recursos, through: :movimentacoes_do_plano_de_contas
	has_many :movimentacoes_das_contas_do_balancete, class_name: "Contabilidade::MovimentacaoDaContaDoBalancete", dependent: :destroy
	has_many :eventos_contabeis_manuais, class_name: "Contabilidade::EventoContabilManual", foreign_key: "conta_pcasp_id"
	has_many :contas_extra_orcamentaria, class_name: "Contabilidade::ContaExtraOrcamentaria"
	has_many :contas_filhas, class_name: "Contabilidade::Conta", dependent: :destroy, foreign_key: 'conta_pai_id'
	has_many :saldo_mensal_da_conta_por_informacoes, class_name: "Contabilidade::SaldoMensalDaContaPorInformacao"
	has_many :contas_pcasp_da_conta_bancaria_por_orcamento, class_name: 'Contabilidade::ContaPcaspDaContaBancariaPorOrcamento'

	has_many :contas_por_unidades_orcamentarias, dependent: :destroy
	has_many :unidades_orcamentarias, through: :contas_por_unidades_orcamentarias, class_name: 'Loa::UnidadeOrcamentaria'

	before_validation :completar_codigo, if: Proc.new{ conta_pai_id.present? && detalhamento_extra.nil? }
	before_validation :completar_codigo_detalhamento_extra, if: Proc.new{ detalhamento_extra.present? && conta_pai_id.present? }
	before_validation :atribui_natureza_do_saldo_da_conta_pai_para_novo_detalhamento, if: :conta_pai_id
	before_validation :atribui_classificacao_da_conta, if: Proc.new{ conta_pai_id.nil?}
	before_validation :determina_grupo_de_conta

	before_update  :deve_impedir_alteracao_para_contas_importadas_do_pcasp, if: Proc.new{|conta| conta.importada_do_pcasp? && !conta.saldo_changed?}

	before_destroy :deve_impedir_delecao_para_contas_importadas_do_pcasp, if: Proc.new{|conta| conta.importada_do_pcasp?}
	before_destroy :deve_impedir_delecao_se_tiver_filhos

	after_create :muda_nivel_detalhado_da_conta_pai
	after_create :cria_detalhamento_extra_da_conta_par, if: Proc.new { evento_contabil_id.present?}

	validates_presence_of :orcamento_id, :grupo_de_conta_id, :nome, :codigo,
		:classe, :grupo, :subgrupo, :titulo, :subtitulo, :item,
		:status, :natureza_do_saldo, :saldo

	validates_presence_of :subitem, if: Proc.new{ evento_contabil_id.nil? }

	validates_presence_of :detalhamento_extra, if: Proc.new{ evento_contabil_id.present? || detalhamento_extra_was.present? }

	validates_uniqueness_of :codigo,		scope: :orcamento_id, case_sensitive: false
	validates_uniqueness_of :classe,		scope: [:grupo, :subgrupo, :titulo, :subtitulo, :item, :subitem, :orcamento_id, :detalhamento_extra],		case_sensitive: false
	validates_uniqueness_of :grupo, 		scope: [:classe, :subgrupo, :titulo, :subtitulo, :item, :subitem, :orcamento_id, :detalhamento_extra],		case_sensitive: false
	validates_uniqueness_of :subgrupo,	scope: [:classe, :grupo, :titulo, :subtitulo, :item, :subitem, :orcamento_id, :detalhamento_extra],			case_sensitive: false
	validates_uniqueness_of :titulo,		scope: [:classe, :grupo, :subgrupo, :subtitulo, :item, :subitem, :orcamento_id, :detalhamento_extra],		case_sensitive: false
	validates_uniqueness_of :subtitulo,	scope: [:classe, :grupo, :subgrupo, :titulo, :item, :subitem, :orcamento_id, :detalhamento_extra],				case_sensitive: false
	validates_uniqueness_of :item,			scope: [:classe, :grupo, :subgrupo, :titulo, :subtitulo, :subitem, :orcamento_id, :detalhamento_extra],	case_sensitive: false
	validates_uniqueness_of :subitem,		scope: [:classe, :grupo, :subgrupo, :titulo, :subtitulo, :item, :orcamento_id, :detalhamento_extra],			case_sensitive: false

	validates_length_of :nome, maximum: 255
	validates_length_of :codigo, is: 9, if: Proc.new { detalhamento_extra.nil? && conta_pai_id.nil?}
	validates_length_of :classe, :grupo, :subgrupo, :titulo, :subtitulo, is: 1
	validates_length_of :item, is: 2
	validates_length_of :subitem, is: 2, if: Proc.new { detalhamento_extra.nil? }

	validates :orcamento_id, immutable: true
	validate :saldo_nao_pode_virar, if: Proc.new{ |conta| conta.saldo_changed?}
	# COMENTADO POR CONTAS TER VÁRIOS FILHOS, NAO DESOCBRIMOS A RAZAO DA VALIDAÇÃO
	# validate :nao_deve_ter_irmao_importado, if: Proc.new{ |conta| conta.codigo.present? && conta.new_record? && conta.detalhamento_extra.nil? }
	validate :deve_ter_pai_cadastrado, if: Proc.new{ |conta| conta.codigo.present? }
	validate :deve_ter_pai_ativo, if: Proc.new{ |conta| conta.codigo.present? }
	validate :pai_nao_pode_ter_movimentacao, if: Proc.new{ |conta| conta.codigo.present? && detalhamento_extra.nil? }

	scope :analiticas, -> { where(analitica: true) }
	scope :superiores, -> { where(analitica: false) }
	scope :possui_saldos_mensais, -> { joins(:saldo_mensal_da_conta_por_informacoes).distinct }

	def ultimo_nivel?
		Contabilidade::Conta.where(classificacao_com_niveis_preenchidos.merge(orcamento_id: self.orcamento_id)).where.not(codigo: self.codigo).empty?
	end

	def self.buscar_analiticas
		Contabilidade::Conta.select(&:ultimo_nivel?)
	end

	enum status: {
		ativa: 0,
		inativa: 1
	}

	enum natureza_do_saldo: {
		credor: 0,
		devedor: 1,
		mista: 2
	}

	enum atributo_de_conta: {
		financeira: 0,
		permanente: 1,
		ambos: 2
	}

	enum ente_que_utiliza: {
		uniao: 0,
		estado: 1,
		municipio: 2,
		todos_os_entes: 3
	}

	# SIGLAS DAS INFORMAÇÕES COMPLEMENTARES DO DETALHAMENTO DE CONTA
	# PO	Poder ou Órgão
	# FP	Atributo do Superávit Financeiro (Financeiro/Permanente)
	# DC	Dívida Consolidada
	# FR	Fonte ou Destinação de Recursos
	# CF	Complemento da Fonte de Recursos ou Destinação de Recursos
	# NR	Natureza da Receita
	# ND	Natureza da Despesa
	# FS	Classificação Funcional (Função e Subfunção)
	# AI	Ano de Inscrição de Restos a Pagar
	# ES	Despesas com MDE e ASPS

	enum informacao_complementar: {
		nao_se_aplica: 0,
		po: 1,
		po_fp: 2,
		po_fp_fr: 3,
		po_fp_dc_fr: 4,
		po_fr: 5,
		po_fr_cf_nr: 6,
		po_fs_fr_cf_nd_es: 7,
		po_fs_fr_cf_nd_es_ai: 8,
		po_fp_dc: 9,
		po_fp_fr_co: 10,
		po_fr_co: 11,
		po_fr_co_nr: 12,
		po_fs_fr_co_nd: 13,
		po_fs_fr_co_nd_ai: 14
	}

	enum classe_pcasp: {
		classe_ativa: 1,
		classe_passiva: 2,
		classe_vpd: 3,
		classe_vpa: 4,
		classe_controle_aprovacao: 5,
		classe_controle_execucao: 6,
		classe_controle_devedor: 7,
		classe_controle_credor: 8
	}

	enum nivel_detalhado: {
		ultimo: 1,
		superior: 2
	}

	def descricao_para_detalhe_do_pagamento
		"#{nome}"
	end

	def descricao_para_detalhe_do_pagamento_na_liquidacao
		"#{nome} (#{self.codigo_formatado})"
	end

	def codigo_formatado
		detalhamento_extra.present? ? "#{classificacao_detalhamento_extra.values.join('.')}" : "#{classificacao.values.join('.')}"
	end

	def codigo_formatado_e_nome
		"#{self.codigo_formatado} - #{self.nome}"
	end

	def codigo_formatado_nome_e_descricao
		"#{self.codigo_formatado} - #{self.nome} - #{self.try(:descricao).try(:downcase).try(:capitalize)}"
	end

	def codigo_formatado_nome_e_descricao_e_atributo_de_conta
		if !self.atributo_de_conta.nil?
			codigo_formatado_nome_e_descricao + " - #{self.atributo_de_conta.try(:upcase)}"
		else
			codigo_formatado_nome_e_descricao
		end
	end

	# def saldo_agregado
	# 	attrs_busca = self.classificacao_com_niveis_preenchidos.merge( {orcamento_id: self.orcamento_id} )
	# 	return saldo_inicial.to_d + Contabilidade::Conta.where( attrs_busca ).sum(&:saldo_total_atual).to_d
	# end

	def saldo_agregado
		if self.saldo > 0 
			return saldo_total.to_d
		else
			attrs_busca = self.classificacao_com_niveis_preenchidos.merge( {orcamento_id: self.orcamento_id} )
			return saldo_inicial.to_d + Contabilidade::Conta.where( attrs_busca ).sum(&:saldo).to_d
		end
	end

	# saldo inicial é o saldo final da conta no exercicio anterior.
	def saldo_inicial
		#orcamento_anterior = Orcamento.find_by(exercicio: (orcamento.exercicio - 1))
		#if orcamento_anterior.present?
			#Contabilidade::Conta.find_by(orcamento_id: orcamento_anterior.id, codigo: self.codigo).try(:saldo).to_d
		#else
			0
		#end
	end

	def retificadora?
		self.nome.to_s.include?("(-)")
	end

	def saldo_total
		saldo_inicial.to_f + self.saldo.to_f
	end

	def valor_total_debitos
		movimentacoes_do_plano_de_contas.debito.sum(:valor)
	end

	def valor_total_creditos
		movimentacoes_do_plano_de_contas.credito.sum(:valor)
	end

	def saldo_total_na_data (data)
		valor_total_debitos_na_data(data) - valor_total_creditos_na_data(data)
	end

	def saldo_total_atual
		data_atual = Date.today
		valor_total_debitos_na_data(data_atual) - valor_total_creditos_na_data(data_atual)
	end

	def valor_total_debitos_na_data (data)
		movimentacoes_do_plano_de_contas.where('data_de_lancamento <= ?', data.to_date).debito.sum(:valor)
	end

	def valor_total_creditos_na_data (data)
		movimentacoes_do_plano_de_contas.where('data_de_lancamento <= ?', data.to_date).credito.sum(:valor)
	end

	def saldo_total_por_mes_debitos data_referencia
		movimentacoes = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento >= ? AND contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento < ?', data_referencia, data_referencia + 1.months).debito.sum(:valor).to_d
		movimentacoes_de_abertura = 0
		if data_referencia.month == 12
			movimentacoes_de_abertura = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento <= ?', data_referencia + 2.months).where("contabilidade_movimentacoes_do_plano_de_contas.lancamento_manual = 999").debito.sum(:valor).to_d
		end
		return movimentacoes + movimentacoes_de_abertura
	end

	def saldo_total_por_mes_creditos data_referencia
		movimentacoes = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento >= ? AND contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento < ?', data_referencia, data_referencia + 1.months).credito.sum(:valor).to_d
		movimentacoes_de_abertura = 0
		if data_referencia.month == 12
			movimentacoes_de_abertura = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento <= ?', data_referencia + 2.months).where("contabilidade_movimentacoes_do_plano_de_contas.lancamento_manual = 999").credito.sum(:valor).to_d
		end
		return movimentacoes + movimentacoes_de_abertura
	end

	def saldo_total_por_mes data_referencia
		movimentacoes = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento >= ? AND contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento < ?', data_referencia, data_referencia + 1.months).sum(:valor).to_d
		movimentacoes_de_abertura = 0
		if data_referencia.month == 12
			movimentacoes_de_abertura = movimentacoes_do_plano_de_contas.where('contabilidade_movimentacoes_do_plano_de_contas.data_de_lancamento <= ?', data_referencia + 2.months).where("contabilidade_movimentacoes_do_plano_de_contas.lancamento_manual = 999").sum(:valor).to_d
		end
		return movimentacoes + movimentacoes_de_abertura
	end

	def classificacao
		if self.codigo
			{
				classe: self.codigo[0],
				grupo: self.codigo[1],
				subgrupo: self.codigo[2],
				titulo: self.codigo[3],
				subtitulo: self.codigo[4],
				item: self.codigo[5..6],
				subitem: self.codigo[7..8]
			}
		end
	end

	def classificacao_detalhamento_extra
		{
			classe: classe,
			grupo: grupo,
			subgrupo: subgrupo,
			titulo: titulo,
			subtitulo: subtitulo,
			item: item,
			detalhamento_extra: detalhamento_extra
		}
	end

	def unidades_orcamentaria_utilizadas
		ids = self.movimentacoes_do_plano_de_contas_com_contas_filhas.pluck(:unidade_orcamentaria_id).uniq
		Loa::UnidadeOrcamentaria.where(id: ids)
	end

	def pode_detalhar_classificacao?
		if self.ativa? && self.movimentacoes_do_plano_de_contas.empty?
			conta_filho = contas_filho.first
			return conta_filho.present? ? !conta_filho.importada_do_pcasp? : self.subitem.to_i.eql?(0)
		else
			return false
		end
	end

	def classificacao_com_niveis_preenchidos
		return Hash[ Hash[self.classificacao.to_a.reverse].drop_while{|key, value| value.to_i.eql?(0) }.reverse ]
	end

	def classificacao_niveis_nao_preenchidos
		return Hash[self.classificacao.to_a - self.classificacao_com_niveis_preenchidos.to_a]
	end

	def contas_filho
		Contabilidade::Conta.where( classificacao_com_niveis_preenchidos.merge(orcamento_id: self.orcamento_id) ).where.not(codigo: self.codigo)
	end

	def completar_codigo
		codigo_nivel = self.codigo

		unless self.codigo.length == 9
			if (codigo_nivel.present?)
				codigo_parcial_pai = conta_pai.classificacao_com_niveis_preenchidos.values.join
				if codigo_parcial_pai.length > 4
					self.codigo = (codigo_parcial_pai + codigo_nivel.rjust(2, '0')).ljust(9, '0')
				else
					if codigo_nivel.length > 1
						errors.add(:codigo, 'não pode conter 2 digitos nesse nível')
					else
						codigo_parcial_pai += '0' if self.nivel_de_consolidacao.eql?('true')
						self.codigo = (codigo_parcial_pai + codigo_nivel).ljust(9, '0')
					end
				end
			else
				errors.add(:codigo, 'não pode ficar em branco')
			end
		end
	end

	def determina_grupo_de_conta
		if self.codigo.present?
			if (1..4).include?(self.codigo[0].to_i)
				self.grupo_de_conta_id = orcamento.try(:grupo_de_contas).try(:find_by, codigo: 1).try(:id)
			elsif (5..6).include?(self.codigo[0].to_i)
				self.grupo_de_conta_id = orcamento.try(:grupo_de_contas).try(:find_by, codigo: 2).try(:id)
			else
				self.grupo_de_conta_id = orcamento.try(:grupo_de_contas).try(:find_by, codigo: 3).try(:id)
			end
		end
	end

	def atribui_classificacao_da_conta
		if self.codigo.present?
			self.classe = self.codigo[0]
			self.grupo = self.codigo[1]
			self.subgrupo = self.codigo[2]
			self.titulo = self.codigo[3]
			self.subtitulo = self.codigo[4]
			self.item = self.codigo[5..6]
			self.subitem = self.codigo[7..8]
		end
	end

	def contas_pai(array, conta)
		array = [] if array.nil?
		if conta.conta_pai.nil?
			return array
		end
		array << conta.conta_pai
		contas_pai(array, conta.conta_pai)
	end

	def natureza_do_saldo_formatada
		@natureza_do_saldo_formatada ||=
			case natureza_do_saldo
			when 'credor'
				self.retificadora? ? 'D' : 'C'
			when 'devedor'
				self.retificadora? ? 'C' : 'D'
			when 'mista'
				'M'
			end
	end

	def completar_codigo_detalhamento_extra
		self.classe = conta_pai.classe
		self.grupo = conta_pai.grupo
		self.subgrupo = conta_pai.subgrupo
		self.titulo = conta_pai.titulo
		self.subtitulo = conta_pai.subtitulo
		self.item = conta_pai.item
		self.codigo = "#{detalhamento_extra}"

		
		#self.codigo = "#{conta_pai.classificacao_com_niveis_preenchidos.values.join}#{detalhamento_extra}"
	end

	#def to_sim_receita(unidade_orcamentaria, natureza_da_receita, fonte_de_recursos, mes_de_referencia, saldo_debito_total, saldo_credito_total)
	def to_sim_receita args
		begin
			texto = ""
			data_inicial = Date.new(orcamento.exercicio, args[:mes_de_referencia])
			data_final = data_inicial.end_of_month

			saldo_inicial_do_mes = args[:saldo_inicial_do_mes]

			if credor? || mista?
				saldo_final_do_mes = saldo_inicial_do_mes - args[:saldo_debito_total] + args[:saldo_credito_total]
			else
				saldo_final_do_mes = saldo_inicial_do_mes + args[:saldo_debito_total] - args[:saldo_credito_total]
			end

			codigo_fr = args[:fonte_de_recursos].codigo_completo rescue nil

			codigo_grupo_da_fonte =
				if codigo_fr.present?
					codigo_fr.to_s[0]
				else
					self.orcamento == contexto_atual ? '1' : '2'
				end

			texto << '307'.sim_preenche(3) + ',' #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.sim_preenche(3) + "," #2
			texto << orcamento.exercicio.to_s + '00' + ',' #3
			texto << args[:unidade_orcamentaria].unidade_gestora.codigo.to_s.rjust(2, '0') + ',' #4
			texto << codigo.sim_preenche_a_direita(15) + ',' #5
			texto << args[:ic_nr].sim_preenche_a_direita(15) + ',' #6
			texto << (args[:fonte_de_recursos].nil? ? '0' : args[:fonte_de_recursos][0]).to_s.sim_limite(1) + ',' #7
			texto << (args[:fonte_de_recursos].nil? ? '0' : args[:fonte_de_recursos][1..9]).to_s.sim_limite(9) + ',' #8
			texto << (args[:fonte_de_recursos].nil? ? '0000' : args[:fonte_de_recursos][4..7]).to_s.sim_limite(4) + ',' #9
			texto << "#{data_inicial.year}#{data_inicial.month.to_s.rjust(2, '0')}" + ',' #10
			texto << (saldo_inicial_do_mes > 0 ? 'C' : 'D').sim_limite(1) + ',' #11
			texto << saldo_inicial_do_mes.to_f.abs.to_s.sim_valor + ',' #12
			texto << args[:saldo_debito_total].to_s.sim_valor + ',' #13
			texto << args[:saldo_credito_total].to_s.sim_valor + ',' #14
			texto << (saldo_final_do_mes > 0 ? 'C' : 'D').sim_preenche(1) + ',' #15
			texto << saldo_final_do_mes.abs.to_s.sim_valor  #16

			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, self.try(:codigo_movimentacao), atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def to_sim_despesa(args)
		begin
			texto = ""
			data_inicial = Date.new(orcamento.exercicio, args[:mes_de_referencia])
			data_final = data_inicial.end_of_month

			saldo_inicial_do_mes = args[:saldo_inicial_do_mes]

			if credor? || mista?
				saldo_final_do_mes = saldo_inicial_do_mes - args[:saldo_debito_total] + args[:saldo_credito_total]
			else
				saldo_final_do_mes = saldo_inicial_do_mes + args[:saldo_debito_total] - args[:saldo_credito_total]
			end

			texto << '308'.sim_limite(3) + ',' #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.sim_limite(3) + "," #2
			texto << args[:subacao].acao.programa_de_governo.orcamento.exercicio.to_s + '00' + ',' #3
			texto << args[:subacao].unidade_orcamentaria.unidade_gestora.codigo.to_s.rjust(2, '0') + ',' #4
			texto << codigo.sim_preenche_a_direita(15) + ',' #5
			texto << args[:subacao].unidade_orcamentaria.orgao.codigo.sim_limite(2) + ',' #6
			texto << args[:subacao].unidade_orcamentaria.codigo.codigo_uo_to_sim + ',' #7
			texto << args[:subacao].funcao.codigo.sim_limite(2) + ',' #8
			texto << args[:subacao].subfuncao.codigo.sim_limite(3) + ',' #9
			texto << args[:acao].programa_de_governo.codigo.to_s.sim_limite(4) + ',' #10
			texto << args[:acao].natureza_da_acao.codigo.to_s.sim_limite(1) + ',' #11
			texto << args[:acao].codigo.to_s.sim_preenche(3) + ',' #12			
			#consultor Lukas informou que a cidade de Maracanau não utiliza sub-projeto(campo 13)
			texto << ( Configuracao.first.codigo_do_municipio_no_tcm != "099" ? args[:subacao].codigo.to_s .sim_preenche(4) : '"0000"') + ',' #13
			texto << (args[:ic_nd].to_s.first(6) + "00").sim_limite(8) + ',' # 14
			texto << (args[:fonte_de_recursos].nil? ? '0' : args[:fonte_de_recursos][0]).to_s.sim_limite(1) + ',' #15
			texto << (args[:fonte_de_recursos].nil? ? '0' : args[:fonte_de_recursos][1..9]).to_s.sim_limite(9) + ',' #16
			texto << (args[:fonte_de_recursos].nil? ? '0000' : args[:fonte_de_recursos][4..7]).to_s.sim_limite(4) + ',' #17
			texto << "#{data_inicial.year}#{data_inicial.month.to_s.rjust(2, '0')}" + ',' #18
			texto << (saldo_inicial_do_mes > 0 ? 'C' : 'D').sim_limite(1) + ',' #19
			texto << saldo_inicial_do_mes.to_f.abs.to_s.sim_valor + ',' #20
			texto << args[:saldo_debito_total].to_s.sim_valor + ',' #21
			texto << args[:saldo_credito_total].to_s.sim_valor + ',' #22
			texto << (saldo_final_do_mes > 0 ? 'C' : 'D').sim_limite(1) + ',' #23
			texto << saldo_final_do_mes.abs.to_s.sim_valor + ',' #24
			texto << "0" # temporariamente, ver a linha abaixo se deve enviar de outros exercicios
			#texto << (args[:orcamento_atual][:restos_a_pagar] ? args[:orcamento_atual][:exercicio].to_s + '00' : '0')# 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, self.try(:codigo_movimentacao), atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def informacao_complementar_possui_fonte_de_recursos?
		po_fp_fr? || po_fp_dc_fr? || po_fr? || po_fr_cf_nr? || po_fs_fr_cf_nd_es? || po_fs_fr_cf_nd_es_ai? ||
		po_fp_fr_co? || po_fr_co_nr? || po_fs_fr_co_nd? || po_fs_fr_co_nd_ai?
	end

	def informacao_complementar_possui_natureza_da_receita?
		po_fr_cf_nr? || po_fr_co_nr?
	end

	def informacao_complementar_possui_natureza_da_despesa?
		po_fs_fr_cf_nd_es? ||	po_fs_fr_cf_nd_es_ai? || po_fs_fr_co_nd? || po_fs_fr_co_nd_ai?
	end

	def informacao_complementar_possui_atributo_financeiro?
		po_fp? || po_fp_fr? || po_fp_dc_fr? || po_fp_dc? || po_fp_fr_co?
	end

	def informacao_complementar_possui_complemento_de_fonte?
		po_fp_fr_co? || po_fr_co? || po_fr_co_nr? || po_fs_fr_co_nd? || po_fs_fr_co_nd_ai?
	end

	def informacao_complementar_possui_ano_de_restos_a_pagar?
		po_fs_fr_cf_nd_es_ai? || po_fs_fr_co_nd_ai?
	end

	def informacao_complementar_possui_funcao_e_subfuncao?
		po_fs_fr_cf_nd_es? || po_fs_fr_cf_nd_es_ai? || po_fs_fr_co_nd? || po_fs_fr_co_nd_ai?
	end

	def informacao_complementar_possui_divida_consolidada?
		po_fp_dc_fr? || po_fp_dc?
	end
	
  def saldo_inicial_do_mes_por_informacao_complementar(ic_da_conta, data_inicio)
    movimentacoes_ate_a_data_inicial = self.movimentacoes_do_plano_de_contas.where(ic_po: ic_da_conta.ic_po, ic_fp: ic_da_conta.ic_fp,
      ic_dc: ic_da_conta.ic_dc, ic_fr: ic_da_conta.ic_fr, ic_co: ic_da_conta.ic_co, ic_nr: ic_da_conta.ic_nr, ic_nd: ic_da_conta.ic_nd,
      ic_fs: ic_da_conta.ic_fs, ic_ai: ic_da_conta.ic_ai).where('data_de_lancamento < ? AND extract(year from data_de_lancamento) = ?', data_inicio.beginning_of_month, data_inicio.year)

    total_credito = movimentacoes_ate_a_data_inicial.credito.sum(:valor)
    total_debito = movimentacoes_ate_a_data_inicial.debito.sum(:valor)

    if self.credor? || self.mista?
      self.saldo_inicial - total_debito + total_credito
    else
      self.saldo_inicial + total_debito - total_credito
    end
  end

	def movimentacoes_do_plano_de_contas_e_movimentacoes_filhas(data_inicial, data_final, unidade_orcamentaria_id = nil)
		movimentacoes = []

		string_where = "unidade_orcamentaria_id = #{unidade_orcamentaria_id}" if unidade_orcamentaria_id.present?

		if self.subitem != '00'
			movimentacoes << self.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
		else
			if self.item != '00'
				contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..6]}__'").where(orcamento_id: self.orcamento_id).all
				contas.each do |cont|
					movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
				end
			else
				if self.subtitulo != '0'
					contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..4]}____'").where(orcamento_id: self.orcamento_id).all
					contas.each do |cont|
						movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
					end
				else
					if self.titulo != '0'
						contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..3]}_____'").where(orcamento_id: self.orcamento_id).all
						contas.each do |cont|
							movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
						end
					else
						if self.subgrupo != '0'
							contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..2]}______'").where(orcamento_id: self.orcamento_id).all
							contas.each do |cont|
								movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
							end
						else
							if self.grupo != '0'
								contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..1]}_______'").where(orcamento_id: self.orcamento_id).all
								contas.each do |cont|
									movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
								end
							else
								contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0]}________'").where(orcamento_id: self.orcamento_id).all
								contas.each do |cont|
									movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
								end
							end
						end
					end
				end
			end
		end

		return movimentacoes
	end

	def movimentacoes_do_plano_de_contas_e_movimentacoes_filhas_por_ug(data_inicial, data_final, unidade_orcamentaria_ids=nil)
		movimentacoes = []
		ids_unidade_orcamentaria = unidade_orcamentaria_ids.nil? ? [] : unidade_orcamentaria_ids
		string_where = "unidade_orcamentaria_id in(#{ids_unidade_orcamentaria.join(', ')})" if unidade_orcamentaria_ids.present?

		if self.subitem != '00'
			movimentacoes << self.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
		else
			if self.item != '00'
				contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..6]}__'").where(orcamento_id: self.orcamento_id).all
				contas.each do |cont|
					movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
				end
			else
				if self.subtitulo != '0'
					contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..4]}____'").where(orcamento_id: self.orcamento_id).all
					contas.each do |cont|
						movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
					end
				else
					if self.titulo != '0'
						contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..3]}_____'").where(orcamento_id: self.orcamento_id).all
						contas.each do |cont|
							movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
						end
					else
						if self.subgrupo != '0'
							contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..2]}______'").where(orcamento_id: self.orcamento_id).all
							contas.each do |cont|
								movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
							end
						else
							if self.grupo != '0'
								contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0..1]}_______'").where(orcamento_id: self.orcamento_id).all
								contas.each do |cont|
									movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
								end
							else
								contas = Contabilidade::Conta.where("codigo like '#{self.codigo[0]}________'").where(orcamento_id: self.orcamento_id).all
								contas.each do |cont|
									movimentacoes << cont.movimentacoes_do_plano_de_contas.where(string_where).where('data_de_lancamento >= ? AND data_de_lancamento <= ?', data_inicial, data_final)
								end
							end
						end
					end
				end
			end
		end

		return movimentacoes
	end

  def saldo_final_da_conta_por_mes(data_inicio)
    saldo_inicial_do_mes = saldo_inicial_da_conta_por_mes(data_inicio)
    movimentacoes_do_mes = self.movimentacoes_do_plano_de_contas
      .where('data_de_lancamento >= ? AND data_de_lancamento <= ? AND extract(year from data_de_lancamento) = ?', data_inicio.beginning_of_month, data_inicio.end_of_month, data_inicio.year)

    total_credito = movimentacoes_do_mes.credito.sum(:valor)
    total_debito = movimentacoes_do_mes.debito.sum(:valor)

    if self.credor? || self.mista?
      saldo_inicial_do_mes - total_debito + total_credito
    else
      saldo_inicial_do_mes + total_debito - total_credito
    end
  end

	def tipo_de_saldo(data_final = nil, unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "data_de_lancamento <= '#{data_final.to_date.strftime('%F')}'" if data_final.present?
		condicoes << "unidade_orcamentaria_id = #{unidade_orcamentaria_id}" if unidade_orcamentaria_id.to_i.positive?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			(movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d) > 0 ? 'D' : 'C'

		else
			(movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d) > 0 ? 'C' : 'D'
		end
	end

	def tipo_de_saldo_por_ug(data_final = nil, unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "data_de_lancamento <= '#{data_final.to_date.strftime('%F')}'" if data_final.present?
		condicoes << "unidade_orcamentaria_id  in(#{unidade_orcamentaria_id.join(', ')})" if !unidade_orcamentaria_id.nil?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			(movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d) > 0 ? 'D' : 'C'

		else
			(movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d) > 0 ? 'C' : 'D'
		end
	end

	def saldo_da_conta(data_final = nil, unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "data_de_lancamento <= '#{data_final.to_date.strftime('%F')}'" if data_final.present?
		condicoes << "unidade_orcamentaria_id = #{unidade_orcamentaria_id}" if unidade_orcamentaria_id.to_i.positive?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d
		else
			movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d
		end
	end

	def tipo_de_saldo_inicial(unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "unidade_orcamentaria_id = #{unidade_orcamentaria_id}" if unidade_orcamentaria_id.to_i.positive?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			(movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).credito.sum(:valor).to_d) > 0 ? 'D' : 'C'

		else
			(movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).debito.sum(:valor).to_d) > 0 ? 'C' : 'D'
		end
	end

	def saldo_inicial_da_conta(unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "unidade_orcamentaria_id = #{unidade_orcamentaria_id}" if unidade_orcamentaria_id.to_i.positive?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).credito.sum(:valor).to_d
		else
			movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.abertura.where(condicoes.join(" AND ")).debito.sum(:valor).to_d
		end
	end

	def saldo_da_conta_por_ug(data_final = nil, unidade_orcamentaria_id = nil, filtro_subconta = false, sub_conta_pcasp_id = nil)
		condicoes = []
		condicoes << "data_de_lancamento <= '#{data_final.to_date.strftime('%F')}'" if data_final.present?
		condicoes << "unidade_orcamentaria_id in(#{unidade_orcamentaria_id.join(', ')})" if !unidade_orcamentaria_id.nil?
		if filtro_subconta == true
			if sub_conta_pcasp_id == nil
				condicoes << "sub_conta_pcasp_id is null"
			else
				condicoes << "sub_conta_pcasp_id = #{sub_conta_pcasp_id.to_i}"
			end
		end

		if credor? || mista? 
			movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d
		else
			movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).credito.sum(:valor).to_d - movimentacoes_do_plano_de_contas.where(condicoes.join(" AND ")).debito.sum(:valor).to_d
		end
	end

	def saldo_final_da_conta(orcamento = nil)
		orcamento = contexto_atual if orcamento.nil?

		if self.analitica?
			self.saldo_da_conta("#{orcamento.exercicio}-12-31")
		else
			regex = /0+$/
			codigo_da_conta = self.codigo.sub(regex) { |match| "_" * match.length }

			total_debitos = Contabilidade::MovimentacaoDoPlanoDeContas
				.where("codigo_da_conta like any (array['#{codigo_da_conta}']) and tipo_de_lancamento = 0 and data_de_lancamento between ? and ?", Date.new(orcamento.exercicio, 1, 1), Date.new(orcamento.exercicio, 12, 31)).sum(:valor).to_d
			total_creditos = Contabilidade::MovimentacaoDoPlanoDeContas.where("codigo_da_conta like any (array['#{codigo_da_conta}']) and tipo_de_lancamento = 1 and data_de_lancamento between ? and ?", Date.new(orcamento.exercicio, 1, 1), Date.new(orcamento.exercicio, 12, 31)).sum(:valor).to_d

			if total_debitos > total_creditos
				return total_debitos - total_creditos
			elsif total_debitos < total_creditos
				return total_creditos - total_debitos
			else
				return 0
			end
		end
	end

	def debito_do_mes(mes_de_referencia)
		data_inicial = Date.new(orcamento.exercicio, mes_de_referencia.to_i)
		@credito_do_mes ||= movimentacoes_do_plano_de_contas.debito
			.where("data_de_lancamento >= ? AND data_de_lancamento <= ?", data_inicial, data_inicial.end_of_month)
	end

	def credito_do_mes(mes_de_referencia)
		data_inicial = Date.new(orcamento.exercicio, mes_de_referencia.to_i)
		@credito_do_mes ||= movimentacoes_do_plano_de_contas.credito
			.where("data_de_lancamento >= ? AND data_de_lancamento <= ?", data_inicial, data_inicial.end_of_month)
	end

	def debito_ate_o_mes(mes_de_referencia)
		@debito_inicial_ate_o_mes ||= movimentacoes_do_plano_de_contas.debito
			.where("data_de_lancamento <= ?", Date.new(orcamento.exercicio, mes_de_referencia.to_i).end_of_month)
	end

	def credito_ate_o_mes(mes_de_referencia)
		@credito_inicial_ate_o_mes ||= movimentacoes_do_plano_de_contas.credito
			.where("data_de_lancamento <= ?", Date.new(orcamento.exercicio, mes_de_referencia.to_i).end_of_month)
	end

	def saldo_ate_o_mes(mes_de_referencia)
		@saldo_ate_o_mes ||= 
			if credor? || mista?  
				saldo_inicial - debito_ate_o_mes(mes_de_referencia) + credito_ate_o_mes(mes_de_referencia)
			else
				saldo_inicial + debito_ate_o_mes(mes_de_referencia) - credito_ate_o_mes(mes_de_referencia)
			end
	end

	def saldo_do_mes(mes_de_referencia)
		@saldo_do_mes ||= 
		if credor? || mista?  
			saldo_inicial - debito_do_mes(mes_de_referencia) + credito_do_mes(mes_de_referencia)
		else
			saldo_inicial + debito_do_mes(mes_de_referencia) - credito_do_mes(mes_de_referencia)
		end
	end

	def movimentacoes_do_plano_de_contas_com_contas_filhas
		return movimentacoes_do_plano_de_contas if contas_filhas.empty?

		Contabilidade::MovimentacaoDoPlanoDeContas.joins(conta_por_evento_contabil: :conta).where(contabilidade_contas: {id: [id, contas_filhas.pluck(:id)].flatten}).distinct
	end

	#private
	def cria_detalhamento_extra_da_conta_par
		evento_contabil = Contabilidade::EventoContabil.find_by(id: evento_contabil_id)

		conta_credito = evento_contabil.contas.where.not(id: conta_pai_id).last

		conta_filha_da_conta_credito = dup
		conta_filha_da_conta_credito.conta_pai_id = conta_credito.id
		conta_filha_da_conta_credito.completar_codigo_detalhamento_extra

		conta_filha_da_conta_credito.save
	end

	def deve_impedir_alteracao_para_contas_importadas_do_pcasp
		errors.add(:codigo, 'não é possível alterar uma conta padrão do sistema')
		return false
	end

	def deve_impedir_delecao_para_contas_importadas_do_pcasp
		errors.add(:codigo, 'não é possível remover uma conta padrão do sistema')
		return false
	end

	def deve_impedir_delecao_se_tiver_filhos
		if contas_filho.present?
			raise Exception.new('não é possível remover conta que tenha filhos')
		end
	end

	# def nao_deve_ter_irmao_importado
	# 	if conta_pai && conta_pai.contas_filho.any? { |conta| conta.importada_do_pcasp? &&  conta.id != self.id }
	# 		errors.add(:codigo, 'não pode ter irmãos padrões do sistema')
	# 	end
	# end

	def valida_conta_bancaria_para_o_sim(conta_bancaria)
		raise "Conta bancaria numero: #{conta_bancaria.try(:numero_da_conta)} (id: #{conta_bancaria.id}) não possui agencia vinculada " if conta_bancaria.try(:agencia).nil? 
	end

	def deve_ter_pai_cadastrado
		unless self.conta_pai_id || primeiro_registro == true
			errors.add(:base, 'não possui nível superior cadastrado')
		end
	end

	def deve_ter_pai_ativo
		if conta_pai && conta_pai.inativa?
			errors.add(:base, 'não pode derivar de uma conta inativa')
		end
	end

	def pai_nao_pode_ter_movimentacao
		if conta_pai && conta_pai.movimentacoes_do_plano_de_contas.any?
			errors.add(:base, 'não pode detalhar uma conta que já possui movimentações')
		end
	end

	def muda_nivel_detalhado_da_conta_pai
		conta_pai.update_column("analitica", false) if conta_pai && conta_pai.codigo.present? && conta_pai.analitica?
	end

	def atribui_natureza_do_saldo_da_conta_pai_para_novo_detalhamento
		if conta_pai.present?
			self.natureza_do_saldo = conta_pai.natureza_do_saldo
		end
	end

	def saldo_nao_pode_virar
		if self.devedor?
			errors.add(:saldo, 'conta devedora não pode ter saldo maior que zero') if self.saldo.to_f > 0
		elsif self.credor?
			errors.add(:saldo, 'conta credora não pode ter saldo menor que zero') if self.saldo.to_f < 0
		end
	end

end
