class Base::ContaBancariaPorUnidadeOrcamentaria < ApplicationRecord
	has_paper_trail
	
	include SimConcern

	attr_accessor :data_inicial, :data_final

	belongs_to :conta_bancaria
	belongs_to :unidade_orcamentaria, class_name: 'Loa::UnidadeOrcamentaria'
	
	has_many :transferencias_financeiras, class_name: 'Contabilidade::TransferenciaFinanceira'
	has_many :movimentacoes_da_conta_bancaria, class_name: 'Contabilidade::MovimentacaoDaContaBancaria', dependent: :restrict_with_exception

	delegate :banco, to: :conta_bancaria

	validates_presence_of :unidade_orcamentaria_id, :data_de_abertura

	validates_uniqueness_of :unidade_orcamentaria_id, scope: :conta_bancaria_id, message: "já existe para essa conta."

	before_destroy :valida_se_pode_excluir_de_acordo_com_campo_principal, unless: Proc.new { self.conta_bancaria.exclusao_completa == true }
	before_destroy :barra_exclusao_caso_tenha_saldo
	
	# after_create :gerar_movimentacao_para_unidade_principal
	
	after_destroy :atualizar_valor_para_campo_principal
	
	accepts_nested_attributes_for :movimentacoes_da_conta_bancaria, reject_if: :all_blank
	
	validates_presence_of :unidade_orcamentaria_id, :data_de_abertura
	validates_uniqueness_of :unidade_orcamentaria_id, scope: :conta_bancaria_id, message: "já existe para essa conta."
	#validates_numericality_of :saldo_atual, greater_than_or_equal_to: 0, allow_blank: true

	validate :valida_data_de_abertura_com_lote_do_sim, on: :create

	scope :do_exercicio_atual, -> (exercicio_atual) {
		joins(unidade_orcamentaria: :orgao).where(
			'loa_orgaos.orcamento_id = ?', exercicio_atual.id
		)
	}

	def numero_agencia_e_banco
		self.conta_bancaria ? self.conta_bancaria.to_s : ""
	end

	def fontes_de_recurso_da_conta
		conta_bancaria.fontes_de_recursos_da_conta_bancaria.joins(:fonte_de_recurso).where(base_fontes_de_recursos: { modulo_type: unidade_orcamentaria.orgao.orcamento.class.name, modulo_id: unidade_orcamentaria.orgao.orcamento.id }).map { |fonte_de_recurso_da_conta_bancaria| fonte_de_recurso_da_conta_bancaria.fonte_de_recurso }
	end

	def definir_como_principal
		unless principal
			conta_bancaria.contas_bancarias_por_unidade_orcamentaria.joins(unidade_orcamentaria: :orgao).where("loa_orgaos.orcamento_id =?", self.unidade_orcamentaria.orgao.orcamento.id).update_all(principal: false)
			self.conta_bancaria.atualiza_saldo_inicial
			self.update(principal: true)
		else
			true
		end
	end

	def saldo_por_data(data)
		if data.present?
			ActiveRecord::Base.connection.execute(
			"SELECT COALESCE(SUM(mov.valor), 0) AS saldo
			FROM contabilidade_movimentacoes_da_conta_bancaria mov
			WHERE mov.conta_bancaria_por_unidade_orcamentaria_id = #{self.id}
			AND mov.data_da_movimentacao <= '#{data.to_date.sim_data}'").first['saldo'].to_d
		else
			return 0
		end
	end

	def soma_das_movimentacoes_futuras_agrupadas_por_dia(data)
		ActiveRecord::Base.connection.execute("
			SELECT mov.data_da_movimentacao, SUM(mov.valor) AS valor
			FROM contabilidade_movimentacoes_da_conta_bancaria mov
			WHERE mov.conta_bancaria_por_unidade_orcamentaria_id = #{self.id}
			AND mov.data_da_movimentacao > '#{data.to_date.sim_data}'
			GROUP BY mov.data_da_movimentacao
			ORDER BY mov.data_da_movimentacao")
	end

	def saldo_mes_anterior_por_data(data)
		if data.present?
			movimentacoes_da_conta_bancaria.where('data_da_movimentacao <= ?', data.end_of_month).sum(:valor)
		else
			return 0
		end
	end

	def saldo_inferior_a_data_sem_movimento_inicial(data)
		movimentacoes_da_conta_bancaria.where('data_da_movimentacao < ? AND modulo_type <> ?', data, 'Base::ContaBancariaPorUnidadeOrcamentaria').sum(:valor)
	end

	def total_movimentado_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where('data_da_movimentacao >= ? AND data_da_movimentacao <= ?', data_inicial, data_final).sum{|f| f.valor.to_d}.to_d rescue 0
	end

	def total_movimentado_ate_a_data(orcamento, data)
		movimentacoes_da_conta_bancaria.where('data_da_movimentacao >= ? AND data_da_movimentacao <= ?', "01/01/#{orcamento.exercicio}", data).sum{|f| f.valor.to_d}.to_d rescue 0
	end

	def saldo_inicial_do_ano(data=nil)
		
		if data.nil?
			movimentacoes_da_conta_bancaria.where("(extract(year from data_da_movimentacao) = ? AND modulo_type = ?)", contexto_atual.exercicio, 'Base::ContaBancariaPorUnidadeOrcamentaria').sum{|f| f.valor.to_d}.to_d rescue 0
		else
			movimentacoes_da_conta_bancaria.where("(extract(year from data_da_movimentacao) = ? AND modulo_type = ?)", data.year, 'Base::ContaBancariaPorUnidadeOrcamentaria').sum{|f| f.valor.to_d}.to_d rescue 0
		end
	end

	def saldo_inferior_a_data(data)
		movimentacoes_da_conta_bancaria.where('data_da_movimentacao < ?', data).sum(:valor)
	end

	def movimentos_de_transferencia_recebidas_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor > 0", data_inicial, data_final, "Contabilidade::TransferenciaFinanceira")
	end

	def movimentos_de_transferencia_realizada_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::TransferenciaFinanceira")
	end

	def movimentos_de_receita_orcamentaria_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor > 0", data_inicial, data_final, "Contabilidade::TalaoDeReceita")
		if movimentos.any?
			movimentos.select{|movimento| !movimento.modulo.try(:extra_orcamentario?)}
		else
			movimentos
		end
	end

	def movimentos_de_receita_extra_orcamentaria_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor > 0", data_inicial, data_final, "Contabilidade::TalaoDeReceita")
		if movimentos.any?
			movimentos.select{|movimento| movimento.modulo.try(:extra_orcamentario?)}
		else
			movimentos
		end
	end

	def movimentos_de_receita_dedutora_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::TalaoDeReceita") 
	end

	def movimentos_de_pagamento_orcamentario_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::Pagamento")
		if movimentos.any?
			movimentos.select{|movimento| !movimento.modulo.resto_a_pagar}
		else
			movimentos
		end
	end

	def movimentos_de_estorno_de_pagamento_orcamentario_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor > 0", data_inicial, data_final, "Contabilidade::EstornoDePagamento")
		if movimentos.any?
			movimentos.select{|movimento| !movimento.modulo.pagamento.resto_a_pagar}
		else
			movimentos
		end
	end

	def movimentos_de_pagamento_restos_a_pagar_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::Pagamento")
		if movimentos.any?
			movimentos.select{|movimento| movimento.modulo.resto_a_pagar}
		else
			movimentos
		end
	end

	def movimentos_de_estorno_de_pagamento_restos_a_pagar_no_periodo(data_inicial, data_final)
		movimentos = movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor > 0", data_inicial, data_final, "Contabilidade::EstornoDePagamento")
		if movimentos.any?
			movimentos.select{|movimento| movimento.modulo.pagamento.resto_a_pagar}
		else
			movimentos
		end
	end

	def movimentos_de_despesa_extra_orcamentaria_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::DespesaExtraOrcamentaria")
	end

	def movimentos_de_anulacao_da_receita_no_periodo(data_inicial, data_final)
		movimentacoes_da_conta_bancaria.where("data_da_movimentacao >= ? AND data_da_movimentacao <= ? AND modulo_type = ? AND valor < 0", data_inicial, data_final, "Contabilidade::AnulacaoDoTalaoDeReceita")
	end

	#validações
	def valida_data_de_abertura_com_lote_do_sim
		errors.add(:data_de_abertura, "O SIM do mês já foi enviado, tente cadastrar uma data diferente para #{self.unidade_orcamentaria.nome}") if lote_do_sim_gerado?(data_de_abertura)
	end


	private
	def atualizar_valor_para_campo_principal
		conta = self.conta_bancaria
		if conta.present? && conta.contas_bancarias_por_unidade_orcamentaria.count == 1
			conta.contas_bancarias_por_unidade_orcamentaria.first.definir_como_principal
		end
	end

	def valida_se_pode_excluir_de_acordo_com_campo_principal
		conta = self.conta_bancaria
		if self.principal && conta.contas_bancarias_por_unidade_orcamentaria.count > 2
			raise Exception.new('Defina uma outra unidade gestora como principal para poder excluir.')
		end
	end

	def barra_exclusao_caso_tenha_saldo
		if self.saldo_atual.to_f > 0
			raise Exception.new('Não é possivel excluir conta com saldo.')
		end
	end
end
