class Contabilidade::PagamentoDaRetencao < ApplicationRecord
	include GeradorDeEventosContabeis
	has_paper_trail

	attr_accessor :fonte_de_recursos_origem_id, :conta_bancaria_despesa_invalida

	belongs_to :retencao, class_name: 'Contabilidade::Retencao'
	belongs_to :conta_origem, class_name: 'Base::ContaBancaria', foreign_key: 'conta_origem_id'
	belongs_to :conta_destino, class_name: 'Base::ContaBancaria', foreign_key: 'conta_destino_id'

	has_one :despesa_extra_orcamentaria, dependent: :destroy
	has_many :taloes_de_receita, dependent: :destroy
	has_one :transferencia_financeira, dependent: :destroy
	has_many :pagamentos_do_lote_bancario, class_name: "Contabilidade::PagamentoDoLoteBancario"

	has_one :pagamento, through: :retencao
	has_one :liquidacao, through: :pagamento
	has_one :empenho, through: :liquidacao

	validates :retencao_id, presence: true
	validates :data_do_pagamento, presence: true
	validates :conta_origem_id, presence: true, if: proc { exige_despesa_extra? }
	validates :conta_destino_id, presence: true, if: proc { exige_talao_extra? }
	validates :fonte_de_recursos_destino_id, presence: true, if: proc { exige_transferencia? }

	validate :conta_origem_deve_possuir_saldo, if: proc { (exige_despesa_extra? || exige_transferencia?) && conta_origem_id_changed? }
	validate :unidade_orcamentaria_deve_possuir_pessoa, unless: proc { exige_transferencia? }
	validate :valida_se_ja_houve_envio_do_sim
	
	#validate :conta_destino_deve_possuir_unidade_principal
	validate :unidade_orcamentaria_da_conta_destino_deve_possuir_pessoa, if: proc { conta_destino.present? && exige_transferencia? == false}
	validate :conta_destino_deve_possuir_natureza_da_receita, if: proc { conta_destino.present? && exige_transferencia? == false}
	validate :validate_pessoa_da_conta_destino, if: proc { conta_destino.present? }
	validate :validate_pessoa_da_conta_origem
	validate :validate_unidade_orcamentaria_principal_da_conta_origem
	validate :validate_unidade_orcamentaria_principal_da_conta_destino, if: proc { conta_destino.present? }
	validate :validate_natureza_da_receita_no_orcamento, if: proc { exige_talao_extra? } # deve ter as mesmas condições de gera_talao_de_receita

	after_save :gera_talao_de_receita, if: proc { exige_talao_extra? }
	after_save :gera_despesa_extra, if: proc { conta_origem.present? && exige_transferencia? == false}
	after_save :gera_transferencia_financeira, if: proc { exige_transferencia? }

	def validate_pessoa_da_conta_destino
		unless conta_destino.unidade_orcamentaria_principal(contexto_atual.try(:id)).try(:pessoa).try(:present?)
			errors.add(:base, "não existe pessoa associada a unidade orçamentária da conta destino")
		end
	end

	def validate_pessoa_da_conta_origem
		unless conta_origem.unidade_orcamentaria_principal(contexto_atual.try(:id)).try(:pessoa).try(:present?)
			errors.add(:base, "não existe pessoa associada a unidade orçamentária da conta origem")
		end
	end

	def validate_unidade_orcamentaria_principal_da_conta_origem
		unless conta_origem.unidade_orcamentaria_principal(contexto_atual.try(:id)).present?
			errors.add(:base, "conta origem não tem unidade orçamentária principal")
		end
	end

	def validate_unidade_orcamentaria_principal_da_conta_destino
		unless conta_destino.unidade_orcamentaria_principal(contexto_atual.try(:id)).present?
			errors.add(:base, "conta destino não tem unidade orçamentária principal")
		end
	end

	def esta_em_um_lote?
		self.pagamentos_do_lote_bancario.joins(:lote_bancario).where(contabilidade_lotes_bancarios: { status: [1, 2]}).size > 0 ? true : false
	end

	def gera_despesa_extra

		if despesa_extra_orcamentaria.nil? || despesa_extra_orcamentaria.conta_bancaria_id != conta_origem_id
			# Importante ser destroy para desfazer todas as movimentações
			despesa_extra_orcamentaria.try(:destroy)
			unidade_orcamentaria_atual = Loa::UnidadeOrcamentaria.find(liquidacao.unidade_orcamentaria_do_exercicio_atual(contexto_atual).id)
			# unidade do exercicio correspodente pegando da liquidacao
			#unidades_vinculadas_ids = unidade_orcamentaria_atual.unidades_orcamentaria_vinculada.pluck(:unidade_orcamentaria_vinculada_id)
			#unidade_id = Loa::UnidadeOrcamentaria.joins(:orgao).where(codigo: unidade_orcamentaria_atual.codigo, loa_orgaos: { codigo: unidade_orcamentaria_atual.orgao.codigo }).pluck(:id).first

			if self.retencao.pagar?
				credor_id = self.retencao.conta_extra_orcamentaria_do_exercicio_atual.pessoa_id
			else
				credor_id = conta_destino.unidade_orcamentaria_principal(contexto_atual.try(:id)).pessoa.id
			end

			despesa_extra = Contabilidade::DespesaExtraOrcamentaria.new(
				pagamento_da_retencao_id: id, 
				orcamento_id: contexto_atual.id,
				unidade_orcamentaria_id: unidade_orcamentaria_atual.id,
				conta_bancaria_id: conta_origem_id,
				data_de_emissao: self.data_do_pagamento,
				conta_extra_orcamentaria_id: retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:id),
				credor_id: credor_id,
				# a pessoa da unidade destino
				tipo_de_documento: 'outro_tipo_de_documento_de_credito',
				numero_do_documento: '0',
				valor_da_despesa: retencao.valor_calculado,
				historico: historico_da_despesa_extra,
				lancamento_automatico_retencao: true,
				mes_de_competencia: self.data_do_pagamento.month,
				ano_de_competencia: self.data_do_pagamento.year
			)
			
			unless despesa_extra.valid?
				despesa_extra.errors.each do |key, value|
					self.errors.add(:base, "#{value.try(:capitalize)}")
				end
			end

			despesa_extra.save!
		end
	end

	def historico_da_despesa_extra
		historico = ["Repasse da retenção de"]

		if retencao.conta_extra_orcamentaria_do_exercicio_atual.present?
			historico.push("#{retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:descricao)}")
		end

		if retencao.try(:nota_fiscal).present?
			historico.push("NF #{retencao.try(:nota_fiscal).try(:numero_da_nota)}")
		end

		if retencao.try(:pagamento).present?

			historico.push("Ref. ao pagamento #{retencao.pagamento.try(:numero)}")

			if retencao.pagamento.credor.present?
				historico.push("de Credor #{retencao.pagamento.try(:credor).try(:nome)}")
			end

			if retencao.pagamento.empenho.present?
				historico.push("Empenho #{retencao.pagamento.empenho.try(:numero_do_empenho)}")
			end
		end

		return historico.join(" ")
	end

	def validate_natureza_da_receita_no_orcamento
		if taloes_de_receita.where(origem_do_talao: 'extra_orcamentario').empty? || taloes_de_receita.where(origem_do_talao: 'extra_orcamentario').joins(:conta_bancaria_por_unidade_orcamentaria).pluck('base_contas_bancarias_por_unidade_orcamentaria.conta_bancaria_id').include?(conta_origem_id) == false
			natureza_da_receita = retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:natureza_da_receita)
			if natureza_da_receita.present?
				unidade_por_natureza_da_receita = Loa::UnidadeOrcamentariaPorNaturezaDaReceita.find_by(natureza_da_receita_id: natureza_da_receita.id)
				errors.add(:base, "Receita #{natureza_da_receita.try(:codigo_e_descricao)} não localizada") if unidade_por_natureza_da_receita.nil?
			end
		end
	end

	def gera_talao_de_receita(orcamento = nil)
		orcamento = contexto_atual if orcamento.nil?
		
		if taloes_de_receita.where(origem_do_talao: 'extra_orcamentario').empty? || taloes_de_receita.where(origem_do_talao: 'extra_orcamentario').joins(:conta_bancaria_por_unidade_orcamentaria).pluck('base_contas_bancarias_por_unidade_orcamentaria.conta_bancaria_id').include?(conta_origem_id) == false
			talao = taloes_de_receita.where(origem_do_talao: 'extra_orcamentario').joins(:conta_bancaria_por_unidade_orcamentaria).where('base_contas_bancarias_por_unidade_orcamentaria.conta_bancaria_id = ?', conta_origem_id).last

			# Importante ser destroy para desfazer todas as movimentações
			talao.try(:destroy) unless talao.nil?
			conta_bancaria_da_unidade_principal = conta_destino.conta_bancaria_por_unidade_orcamentaria_principal(orcamento.try(:id))

			#unidade_orcamentaria_atual = Loa::UnidadeOrcamentaria.find(liquidacao.unidade_orcamentaria.id)
			#unidades_vinculadas_ids = unidade_orcamentaria_atual.unidades_orcamentaria_vinculada.pluck(:unidade_orcamentaria_vinculada_id)
			#unidade_id = Loa::UnidadeOrcamentaria.joins(:orgao).where(codigo: unidade_orcamentaria_atual.codigo, loa_orgaos: { codigo: unidade_orcamentaria_atual.orgao.codigo }).pluck(:id).first

			talao = Contabilidade::TalaoDeReceita.new(
				pagamento_da_retencao_id: self.id,
				orcamento_id: contexto_atual.id,
				unidade_orcamentaria_id: conta_bancaria_da_unidade_principal.unidade_orcamentaria_id,
				conta_bancaria_por_unidade_orcamentaria_id: conta_bancaria_da_unidade_principal.id,
				data_do_talao: self.data_do_pagamento,
				valor: retencao.valor_calculado,
				documento_de_credito: '0',
				tipo_de_documento: 'outro_tipo_de_documento_de_credito',
				tipo_do_talao: 'original',
				lancamento_automatico_retencao: true
			)
			natureza_da_receita = retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:natureza_da_receita)
			unidade_da_conta_extra = natureza_da_receita.unidades_orcamentarias_por_natureza_da_receita.where(unidade_orcamentaria_id: conta_bancaria_da_unidade_principal.unidade_orcamentaria_id).first rescue nil

			if natureza_da_receita.present? && unidade_da_conta_extra.present?
				talao.origem_do_talao = 'orcamentario'
				talao.skip_callback = true
				talao.natureza_da_receita_id = natureza_da_receita.id
				talao.pessoa_id = conta_origem.unidade_orcamentaria_principal(orcamento.try(:id)).pessoa.id
				talao.historico = "Receita Orçamentária #{natureza_da_receita.descricao}, decorrente do pagamento #{pagamento&.numero}. Contribuinte #{talao&.pessoa&.nome}."				

				orcamentos_da_receita = Loa::OrcamentoDaReceita.where( unidade_orcamentaria_por_natureza_da_receita_id: unidade_da_conta_extra.id).order(:fonte_de_recursos_id)

				orcamentos_da_receita.each do |orcamento_da_receita|
					percentual_da_fonte = (orcamento_da_receita.valor / unidade_da_conta_extra.valor_total)

					valor = (percentual_da_fonte * talao.valor)

					if orcamento_da_receita == orcamentos_da_receita.last
						valor = talao.valor - talao.complementos_por_fonte_do_talao_de_receita.sum(&:valor)
					end

					complemento = talao.complementos_por_fonte_do_talao_de_receita.build(
						orcamento_da_receita: orcamento_da_receita,
						valor: valor
					)
				end 

			else
				talao.origem_do_talao  = 'extra_orcamentario'
				talao.conta_extra_orcamentaria_id = retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:id)
				talao.pessoa_id = conta_origem.unidade_orcamentaria_principal(orcamento.try(:id)).pessoa.id
				talao.historico = "Receita Extraorçamentária #{retencao.conta_extra_orcamentaria_do_exercicio_atual.descricao}, decorrente do pagamento #{pagamento&.numero}. Contribuinte #{talao&.pessoa&.nome}."
			end

			unless talao.valid?
				talao.errors.each do |key, value|
					self.errors.add(:base, "#{value.try(:capitalize)}")
				end
			end
			
			if talao.save!
				talao.skip_callback = false if talao.orcamentario?
				talao.gerar_todos_os_movimentos(:data_do_talao, orcamento)
			end
		end
	end

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

		if transferencia_financeira.nil? || transferencia_financeira.conta_bancaria_origem_id != conta_origem_id
			# Importante ser destroy para desfazer todas as movimentações
			transferencia_financeira.try(:destroy)

			unidade_orcamentaria_atual = Loa::UnidadeOrcamentaria.find(liquidacao.unidade_orcamentaria_do_exercicio_atual(orcamento).id)
			conta_bancaria_origem = conta_origem.conta_por_unidade(unidade_orcamentaria_atual) rescue nil
			conta_bancaria_destino = conta_destino.conta_bancaria_por_unidade_orcamentaria_principal(orcamento.try(:id)) rescue nil

			transferencia_financeira = Contabilidade::TransferenciaFinanceira.new(
				pagamento_da_retencao_id: id,
				conta_bancaria_origem_id: conta_bancaria_origem.try(:id),
				conta_bancaria_destino_id: conta_bancaria_destino.try(:id),
				data: self.data_do_pagamento,
				tipo_de_transferencia: 'concessao_de_transferencia_para_execucao_orcamentaria',
				valor: retencao.valor_calculado,
				documento_bancario: 'outro',
				orcamento_id: pagamento.orcamento_id,
				fonte_de_recurso_origem_id: fonte_de_recursos_do_empenho.id,
				fonte_de_recurso_destino_id: fonte_de_recursos_destino_id,
				status: 'confirmado',
				historico: 'Transferência financeira originada de uma retenção'
			)

			unless transferencia_financeira.valid?
				transferencia_financeira.errors.each do |key, value|
					self.errors.add(:base, "#{value.try(:capitalize)}")
				end
			end

			transferencia_financeira.save!
		end
	end

	def exige_despesa_extra?
		retencao.present? && ( retencao.pagar? || retencao.pagar_recolher? ) && retencao.natureza_da_receita_possui_vinculo_com_unidade? == false
	end

	def exige_talao_extra?
		retencao.present? && retencao.pagar_recolher? 
	end

	def exige_transferencia?
		retencao.present? && retencao.transferir?
	end

	def fonte_de_recursos_do_empenho
		@fonte_de_recursos_do_empenho ||= empenho.fonte_de_recursos
	end

	private
	def conta_origem_deve_possuir_saldo
		conta_da_unidade_orcamentaria = conta_origem.contas_bancarias_por_unidade_orcamentaria.where(unidade_orcamentaria_id: liquidacao.unidade_orcamentaria_do_exercicio_atual(contexto_atual).id).first
		if conta_da_unidade_orcamentaria.present?
			saldo_atual = conta_da_unidade_orcamentaria.saldo_atual.to_d
			saldo_do_dia_do_lancamento = conta_da_unidade_orcamentaria.saldo_por_data(self.data_do_pagamento)
			
			errors.add(:conta_origem_id, "(Conta Bancária) Saldo insuficiente, saldo disponível na data selecionada #{saldo_do_dia_do_lancamento.to_d.real_contabil}") if retencao.valor_calculado.to_d > saldo_do_dia_do_lancamento
			errors.add(:conta_origem_id, "(Conta Bancária) Saldo insuficiente, saldo atual da conta: #{saldo_atual.to_d.real_contabil}") if retencao.valor_calculado.to_d > saldo_atual
		else
			errors.add(:conta_origem_id, "Não possui unidade orçamentária correspondente ao pagamento/liquidação")
		end
	end

	def unidade_orcamentaria_deve_possuir_pessoa
		errors.add(:unidade_orcamentaria, 'Deve possuir Pessoa cadastrada na Unidade Orçamentária') if liquidacao.unidade_orcamentaria.pessoa_id.nil?
	end

	def conta_destino_deve_possuir_unidade_principal
		natureza_da_receita = retencao.conta_extra_orcamentaria_do_exercicio_atual.try(:natureza_da_receita)
		unless natureza_da_receita.present?
			errors.add(:conta_destino_id, "Conta Extra-orçamentária associada deve possuir uma Receita para gerar talão orçamentario")
		end
	end

	def conta_destino_deve_possuir_natureza_da_receita
		errors.add(:conta_destino_id, "Deve possui alguma unidade principal") if conta_destino.contas_bancarias_por_unidade_orcamentaria.find_by(principal: true).nil?
	end

	def unidade_orcamentaria_da_conta_destino_deve_possuir_pessoa
		errors.add(:conta_destino_id, "'Deve possuir Pessoa cadastrada na Unidade Orçamentária") if conta_destino.contas_bancarias_por_unidade_orcamentaria.where(principal:true).last.unidade_orcamentaria.pessoa.nil?
	end

	def existe_lote_do_sim?
		if pagamento.data_sim.present?
			orcamento_id = Orcamento.find_by_exercicio(exercicio: pagamento.data_sim.year)
			return Tcm::Lote.find_by(
				orcamento_id: orcamento_id,
				tipo: [Tcm::Lote.tipos[:todos], Tcm::Lote.tipos[:contabilidade]],
				mes_de_referencia: pagamento.data_sim.month,
				situacao: [Tcm::Lote.tipos[:gerado], Tcm::Lote.tipos[:finalizado]]
			).present?
		end

		return false
	end

	def valida_se_ja_houve_envio_do_sim
		errors.add(:sim, 'SIM do Mês já foi enviado') if existe_lote_do_sim?
	end

end
