class Contabilidade::ControleDePagamento < ApplicationRecord
	has_paper_trail

	belongs_to :orcamento
	belongs_to :unidade_orcamentaria, class_name: 'Loa::UnidadeOrcamentaria'
	belongs_to :fonte_de_recurso, class_name: 'Base::FonteDeRecursos'
	belongs_to :elemento_de_despesa, class_name: 'Base::ElementoDeDespesa'
	belongs_to :pessoa, class_name: 'Base::Pessoa'

	has_many :liquidacoes_do_controle_de_pagamento, dependent: :destroy
	has_many :liquidacoes, through: :liquidacoes_do_controle_de_pagamento, class_name: 'Contabilidade::Liquidacao'
	has_many :retencoes_folha, through: :liquidacoes, class_name: 'Contabilidade::Retencao' #retenções direta de folha de pg
	has_many :notas_fiscais, through: :liquidacoes, class_name: 'Contabilidade::NotaFiscal'
	has_many :pagamentos, through: :liquidacoes, class_name: 'Contabilidade::Pagamento'
	has_many :pagamentos_das_retencoes, through: :pagamentos
	has_many :taloes_de_receita, through: :pagamentos_das_retencoes
	has_many :despesa_extra_orcamentarias, through: :pagamentos_das_retencoes
	has_many :agrupamentos_de_retencoes_do_controle_de_pg, class_name: 'Contabilidade::AgrupamentoDeRetencoesDoControleDePg', dependent: :destroy
	has_many :contas_do_grupo_de_retencoes_do_controle_de_pg, through: :agrupamentos_de_retencoes_do_controle_de_pg, class_name: 'Contabilidade::ContaDoGrupoDeRetencoesDoControleDePg'
	has_many :grupos_de_retencoes_do_controle_de_pagamento, dependent: :destroy
	has_many :erros_de_pagamento, class_name: "Contabilidade::ErroDePagamento", dependent: :destroy

	validates_presence_of :data, :forma_de_pagamento
	validates_presence_of :liquidacoes_do_controle_de_pagamento, message: 'Informe as liquidações de folha para seguir'
	
	validates_associated :liquidacoes_do_controle_de_pagamento
	validates :liquidacoes_do_controle_de_pagamento, uniq_nested_attributes: { atributo: :liquidacao_id, mensagem: "Liquidação deve ser única dentro de um controle" }
	
	accepts_nested_attributes_for :liquidacoes_do_controle_de_pagamento, reject_if: :all_blank, allow_destroy: true
	accepts_nested_attributes_for :agrupamentos_de_retencoes_do_controle_de_pg, reject_if: :all_blank, allow_destroy: true

	enum status: {
		autorizado: 0,
		pago_parcial: 1,
		pago_total: 2
	}

	enum forma_de_pagamento: {
		caixa: 1,
		lote_bancario: 2,
		debito_em_conta: 3,
		cheque: 4
	}

# retenÃ§Ãµes de nota fiscal
	def retencoes
		Contabilidade::Retencao.where(nota_fiscal_id: notas_fiscais.pluck(:id)).all
	end

	def valor_liquidacoes
		liquidacoes.sum(:valor)
	end

	def valor_descontos
		retencoes_folha.sum(&:valor_calculado)
	end

	def valor_liquido_total
		valor_liquidacoes.to_f - valor_descontos.to_f
	end

	# BOOLEANS
	def todas_as_contas_das_liquidacoes_cadastradas?
		self.liquidacoes_do_controle_de_pagamento.all? {|lcp| !lcp.conta_destino_id.nil?}
	end

	def todas_as_contas_das_retencoes_cadastradas?
		if self.folha_de_pagamento?
			todas_as_contas = true

			self.retencoes_folha.group_by{ |retencao| [retencao.conta_extra_orcamentaria, retencao.tipo_de_acao]}.each do |grupo, retencoes|
				agrupamento = self.agrupamentos_de_retencoes_do_controle_de_pg.where(tipo_de_acao: grupo[1], conta_extra_id: grupo[0])

				if agrupamento.any?
					if !agrupamento.last.contas_do_grupo_de_retencoes_do_controle_de_pg.any? || !agrupamento.last.todas_as_contas_cadastradas_corretamente?
						todas_as_contas = false
					end
				else  
					todas_as_contas = false
				end
			end
		else
			todas_as_contas = true

			self.retencoes.group_by{ |retencao| [retencao.imposto_type, retencao.tipo_de_acao]}.each do |grupo, retencoes|
				agrupamento = self.agrupamentos_de_retencoes_do_controle_de_pg.where(imposto_type: grupo[0])

				if agrupamento.any?
					if !agrupamento.last.contas_do_grupo_de_retencoes_do_controle_de_pg.any? || !agrupamento.last.todas_as_contas_cadastradas_corretamente?
						todas_as_contas = false
					end
				else  
					todas_as_contas = false
				end
			end
		end

		return todas_as_contas
	end

	def pode_pagar?
		autorizado? && liquidacoes_do_controle_de_pagamento.all?{|liquidacao_cp| liquidacao_cp.conta_destino.present? }
	end

	def pode_realizar_algum_tipo_de_pagamento?
		pode_pagar? || pode_pagar_retencoes? || pode_pagar_total?
	end

	def pode_pagar_total?
		autorizado? && pode_pagar? && grupos_de_retencoes_do_controle_de_pagamento.all?(&:conjunto_totalmente_preenchido?) 
	end

	def pode_pagar_retencoes?
		pago_parcial? && grupos_de_retencoes_do_controle_de_pagamento.all?(&:conjunto_totalmente_preenchido?)
	end

	def taloes_de_receita_extra_orcamentario
		Contabilidade::TalaoDeReceita.where(pagamento_id: pagamentos.ids).extra_orcamentario
	end

	def valor_liquido_pago?
		self.pagamentos.any?
	end

	def valor_retencoes_pago?
		if todas_as_contas_das_retencoes_cadastradas?
			if self.folha_de_pagamento?
				valor_retencoes_pago = true

				self.retencoes_folha.group_by{ |retencao| [retencao.conta_extra_orcamentaria, retencao.tipo_de_acao]}.each do |grupo, retencoes|
					agrupamento = self.agrupamentos_de_retencoes_do_controle_de_pg.where(tipo_de_acao: grupo[1], conta_extra_id: grupo[0])

					if agrupamento.any? && agrupamento.last.todas_as_contas_cadastradas_corretamente?
						agrupamento.last.contas_do_grupo_de_retencoes_do_controle_de_pg.each do |controle|
							if !controle.pago?
								valor_retencoes_pago = false
							end
						end
					end
				end
			else
				valor_retencoes_pago = true

				self.retencoes.group_by{ |retencao| [retencao.imposto_type, retencao.tipo_de_acaos]}.each do |grupo, retencoes|
					agrupamento = self.agrupamentos_de_retencoes_do_controle_de_pg.where(imposto_type: imposto[0])

					if agrupamento.any? && agrupamento.last.todas_as_contas_cadastradas_corretamente?
						agrupamento.last.contas_do_grupo_de_retencoes_do_controle_de_pg.each do |controle|
							if !controle.pago?
								valor_retencoes_pago = false
							end
						end
					end
				end
			end
		end
	end

	private

	def cria_pagamentos_liquido orcamento_atual
		ActiveRecord::Base.transaction do
			begin
				liquidacoes_do_controle_de_pagamento.each do |liquidacao_do_controle_pg|
					pagamento = Contabilidade::Pagamento.new(
						liquidacao_id: liquidacao_do_controle_pg.liquidacao_id, 
						estornado: false, 
						data: Date.today, 
						valor: liquidacao_do_controle_pg.valor_liquido_da_liquidacao, 
						status: 2, 
						data_da_solicitacao: Date.today, 
						tipo_de_lancamento: 0, 
						orcamento_id: orcamento_atual.id, 
						remover_retencoes_do_pagamento: true)
					
					liquidacao_do_controle_pg.contas_das_liquidacoes_do_controle_de_pagamento.each do |conta_da_liquidacao|
						pagamento.contas_bancarias_por_pagamento.build(conta_bancaria_id: conta_da_liquidacao.conta_origem_id, valor_pago: liquidacao_do_controle_pg.valor_liquido_da_liquidacao)
					end

					pagamento.save!
				end	

				self.update_column(:status, Contabilidade::ControleDePagamento.status[:pago_parcial])
			rescue => exception
				raise
				raise ActiveRecord::Rollback, "Erro ao processar pagamentos lÃ­quido."
			end
		end
	end

	def cria_pagamentos_das_retencoes orcamento_atual
		ActiveRecord::Base.transaction do
			begin
				agrupamentos_de_retencoes_do_controle_de_pg.each do |grupo_de_retecoes|
					if grupo_de_retecoes.tipo_de_acao == 'pagar'
						# AÃ§Ã£o Pagar: para a RetenÃ§Ã£o gera uma despesa extra para o Fornecedor da conta.
						pagamento_retencao_acao_pagar(orcamento_atual, grupo_de_retecoes)
					elsif grupo_de_retecoes.tipo_de_acao == 'pagar_recolher'
						# AÃ§Ã£o Pagar/Recolher: Gera uma despesa extra na secretaria do pagamento, uma receita extra ou orÃ§amentaria 
						# (dependendo do tipo de conta, se ela tiver receita orÃ§amentÃ¡ria vinculada) 
						# para a secretaria vinculada a conta destino.
						pagamento_retencao_acao_pagar_recolher(orcamento_atual, grupo_de_retecoes)
					elsif grupo_de_retecoes.tipo_de_acao == 'transferir'
						# TalÃ£o de Receita OrÃ§amentÃ¡rio de RetenÃ§Ã£o. E opcionalmente pode transferir o valor para outra conta (conta destino)
						pagamento_retencao_acao_transferir(orcamento_atual, grupo_de_retecoes)
					end
				end

				self.update_column(:status, Contabilidade::ControleDePagamento.status[:pago_total])
			rescue => exception
				raise
				raise ActiveRecord::Rollback, "Erro ao processar pagamentos retidos."
			end
		end
	end


	def cria_pagamentos_totais orcamento_atual
		ActiveRecord::Base.transaction do
			begin
				cria_pagamentos_liquido(orcamento_atual)
				cria_pagamentos_das_retencoes(orcamento_atual)
			rescue => exception
				raise
				raise ActiveRecord::Rollback, "Erro ao processar pagamentos totais."
			end
		end
	end

	def pagamento_retencao_acao_pagar orcamento_atual, grupo_de_retecoes
		pessoa_associada = grupo_de_retecoes.conta_extra_orcamentaria.pessoa_id
		grupo_de_retecoes.contas_do_grupo_de_retencoes_do_controle_de_pg.each do |contas_do_grupo|
			retencoes = contas_do_grupo.lista_de_retencoes_do_grupo_de_contas
			retencoes.each do |retencao|

				if grupo_de_retecoes.folha?
					unidade = retencao.liquidacao.unidade_orcamentaria.id
				else
					unidade = retencao.nota_fiscal.liquidacao.unidade_orcamentaria.id
				end

				despesa = Contabilidade::DespesaExtraOrcamentaria.create!(
					tipo_de_lancamento: 0, 
					orcamento_id: orcamento_atual.id, 
					data_de_emissao: Date.today, 
					unidade_orcamentaria_id: unidade, 
					conta_extra_orcamentaria_id: grupo_de_retecoes.conta_extra_id, 
					conta_bancaria_id: contas_do_grupo.conta_origem_id, 
					credor_id: pessoa_associada, 
					tipo_de_documento: 2,
					numero_do_documento: '0',
					lancamento_automatico_retencao: true,
					valor_da_despesa: retencao.valor_calculado,
					historico: "Pagamento da RetenÃ§Ã£o da liquidaÃ§Ã£o #{retencao.liquidacao.empenho_e_numero} do Controle de Pagamento"
				)
			end

			contas_do_grupo.update_column(:pago, true)
		end
	end

	def pagamento_retencao_acao_pagar_recolher orcamento_atual, grupo_de_retecoes
		grupo_de_retecoes.contas_do_grupo_de_retencoes_do_controle_de_pg.each do |contas_do_grupo|
			retencoes = contas_do_grupo.lista_de_retencoes_do_grupo_de_contas
			retencoes.each do |retencao|
				
				if grupo_de_retecoes.folha?
					unidade = retencao.liquidacao.unidade_orcamentaria.id
				else
					unidade = retencao.nota_fiscal.liquidacao.unidade_orcamentaria.id
				end
				pessoa_associada_despesa = contas_do_grupo.conta_de_origem.unidade_orcamentaria_principal.unidade_gestora.gestor_no_periodo(Date.today).agente_publico_municipal.pessoa
				pessoa_associada_receita = contas_do_grupo.conta_de_destino.unidade_orcamentaria_principal.unidade_gestora.gestor_no_periodo(Date.today).agente_publico_municipal.pessoa

				#criaÃ§Ã£o da despesa extra
				Contabilidade::DespesaExtraOrcamentaria.create!(
					tipo_de_lancamento: 0, 
					orcamento_id: orcamento_atual.id,
					data_de_emissao: Date.today, 
					unidade_orcamentaria_id: unidade, 
					conta_extra_orcamentaria_id: grupo_de_retecoes.conta_extra_id, 
					conta_bancaria_id: contas_do_grupo.conta_origem_id, 
					credor_id: pessoa_associada_despesa.id, 
					tipo_de_documento: 2,
					numero_do_documento: '0',
					lancamento_automatico_retencao: true,
					valor_da_despesa: retencao.valor_calculado,
					historico: "Pagamento da RetenÃ§Ã£o da liquidaÃ§Ã£o #{retencao.liquidacao.empenho_e_numero} do Controle de Pagamento"
				)

				if contas_do_grupo.conta_de_destino.conta_vinculada?
					origem_do_talao = 0
				else
					origem_do_talao = 1
				end

				#criaÃ§Ã£o da receita orÃ§amentaria
				Contabilidade::TalaoDeReceita.create!(
					conta_extra_orcamentaria_id: grupo_de_retecoes.conta_extra_id,
					conta_bancaria_por_unidade_orcamentaria_id: contas_do_grupo.conta_destino_id,
					origem_do_talao: origem_do_talao, 
					orcamento_id: orcamento_atual.id,
					pessoa_id: pessoa_associada_receita.id,
					tipo_de_documento: 2,
					tipo_do_talao: 0,
					unidade_orcamentaria_id: unidade,
					data_do_talao: Date.today,
					valor: retencao.valor_calculado,
					talao_de_desconto: false,
					lancamento_automatico_retencao: true,
					retencao_id: retencao.id,
					historico: "Pagamento da RetenÃ§Ã£o da liquidaÃ§Ã£o #{retencao.liquidacao.empenho_e_numero} do Controle de Pagamento"
				)
			end

			contas_do_grupo.update_column(:pago, true)
		end
	end

	def pagamento_retencao_acao_transferir orcamento_atual, grupo_de_retecoes
		# eduarda vai explciar o fluxo
	end

	

end
