class Contabilidade::EstornoDePagamento < ApplicationRecord
	# include ReversorDeEventosContabeis
	# reversor_de_eventos_contabeis acao: 7, atributo_pai: 'pagamento', atributo_data: 'data_do_estorno', atributo_codigo_movimentacao: 'numero_do_pagamento'
	include GeradorDeEventosContabeis
	include IncrementadorDeCodigoConcern
	
	has_paper_trail

	belongs_to :pagamento
	belongs_to :usuario, required: false
	belongs_to :orcamento
	belongs_to :arquivo, class_name: 'Tcm::Arquivo', required: false

	delegate :unidade_orcamentaria, to: :pagamento
	delegate :sub_elemento_de_despesa, to: :pagamento
	delegate :numero, to: :pagamento
	delegate :liquidacao, to: :pagamento, allow_nil: true
	delegate :empenho, to: :liquidacao, allow_nil: true

	attr_default :data_do_estorno, Date.today

	validates_presence_of :data_do_estorno, :justificativa, :pagamento_id
	validates_presence_of :usuario_id

	validates_uniqueness_of :numero_do_estorno, scope: :orcamento_id, case_sensitive: false, :allow_blank => true, :allow_nil => true

	validates :data_do_estorno, date: true
	validate :valida_data_do_pagamento_no_estorno

	validate :impedir_se_for_prepagamento
	validate :data_do_estorno_superior_a_conta_bancaria, if: Proc.new{ |estorno| estorno.pagamento.present? }
	validate :valida_se_ja_houve_envio_do_sim
	validate :valida_se_data_esta_no_orcamento

	before_save :seta_orcamento_do_estorno
	before_save :atribui_codigo_disponivel

	after_create :estorna_o_pagamento
	after_create :retira_valor_pago_da_liquidacao
	after_create :lancar_estorno_nos_movimentos_bancarios
	#after_create {self.pagamento.setar_novamente_valores_a_pagar_do_empenho}

	after_destroy :apagar_movimentos_bancarios
	before_destroy :cancela_estorno

	scope :do_orcamento, -> { joins(:pagamento).where("contabilidade_pagamentos.resto_a_pagar is false") }
	scope :de_restos_a_pagar, -> { joins(:pagamento).where("contabilidade_pagamentos.resto_a_pagar is true") }

	def numero_do_pagamento
		pagamento.numero
	end

	def detalhamento_do_plano_de_contas
		pagamento.detalhamento_do_plano_de_contas
	end

	def valor
		return self.pagamento.valor.to_f
	end

	def valida_data_do_pagamento_no_estorno
		if self.data_do_estorno.present?
			self.errors.add(:data_do_estorno, "não pode ser anterior à data do pagamento.") if self.data_do_estorno < self.pagamento.data
		end
	end

	def valida_saldo_conta_bancaria
		unidade_do_empenho = self.pagamento.liquidacao.unidade_orcamentaria_do_exercicio_atual(contexto_atual)

		self.pagamento.contas_bancarias_por_pagamento.each do |conta_por_pagamento|
			conta_da_unidade = conta_por_pagamento.conta_bancaria.contas_bancarias_por_unidade_orcamentaria.find_by(unidade_orcamentaria_id: unidade_do_empenho.id)

			saldo_atual = conta_da_unidade.saldo_atual - conta_por_pagamento.valor_pago_was.to_d
			saldo_do_dia_do_lancamento = conta_da_unidade.saldo_por_data(self.data_do_estorno) - conta_por_pagamento.valor_pago_was.to_d

			if conta_da_unidade.present?
				errors.add(:valor, "Saldo insuficiente, o saldo disponível na data selecionada ficará em #{saldo_do_dia_do_lancamento.to_d.real_contabil}") if saldo_do_dia_do_lancamento < 0
				errors.add(:valor, "Saldo insuficiente, o saldo atual da conta ficará em: #{saldo_atual.to_d.real_contabil}") if saldo_atual < 0
			end
		end
	end

	def valida_se_ja_houve_envio_do_sim
		if lote_do_sim_gerado?(data_do_estorno)
			errors.add(:data_do_estorno, 'O SIM do mês já foi enviado')
		elsif mes_bloqueado?
			errors.add(:data_do_estorno, 'O mês do objeto está bloqueado, solicite o desbloqueio ao administrador')
		end
	end

	def valida_se_data_esta_no_orcamento
		if self.data_do_estorno.present? && self.data_do_estorno.try(:year) != self.pagamento.try(:orcamento).try(:exercicio)
			errors.add(:data_do_estorno, 'A data do estorno do pagamento, não está dentro do orçamento do pagamento.')
		end
	end

	def estorna_o_pagamento
		if self.pagamento.present?
			self.pagamento.update_column(:estornado, true) 
			self.pagamento.update_column(:status, 'estornado')
		end
	end

	def retira_valor_pago_da_liquidacao
		if self.pagamento&.liquidacao&.present?
			self.pagamento.liquidacao.update_column(:valor_pago, self.pagamento.liquidacao.valor_pago.to_f - self.pagamento.valor)
		end
	end

	def enviado_ao_sim?
		self.mes_bloqueado? || (arquivo_id.present? && arquivo_id > 0 && arquivo.lote.lote_processado_ou_enviado?)
	end

	def cancela_estorno
		self.valida_saldo_conta_bancaria

		if self.errors.empty?
			self.pagamento.update_column(:estornado, false) 
			self.pagamento.update_column(:status, :confirmado)
			if self.pagamento.liquidacao
				self.pagamento.liquidacao.update_column(:valor_pago, self.pagamento.liquidacao.valor_pago.to_f + self.pagamento.valor)
			end
			self.destroy ? true : false
		else
			return false
		end
	end

	def to_sim(data_referencia, poder_associado)
		begin
			exercicio_do_orcamento = pagamento.liquidacao.empenho.orcamento.exercicio.to_s
			#feito assim pois o enum do tipo de unidade adiministrativa começa com o enum 0 - legislativo e 1 - executivo
			tipo_de_poder = poder_associado.to_i == 1 ? 1 : 2

			texto = ""
			texto << "611".sim_preenche(3) + "," #1
			texto << Configuracao.first.codigo_do_municipio_no_tcm.sim_preenche(3) + "," #2
			texto << exercicio_do_orcamento.sim_limite_sem_aspas(4, "00") + "," #3
			texto << pagamento.liquidacao.unidade_orcamentaria.orgao.codigo.to_s.sim_preenche(2) + "," #4
			texto << pagamento.liquidacao.unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + "," #5
			texto << pagamento.liquidacao.empenho.data_do_empenho.sim_data + "," #6
			texto << pagamento.liquidacao.empenho.numero_do_empenho.to_s.sim_limite(8) + "," #7
			texto << (pagamento.liquidacao.empenho.ordinario? ? '"000"' : '"001"') + "," #8
			texto << pagamento.numero.to_s.sim_limite(8) + "," #9
			texto << data_do_estorno.sim_data + "," #10
			texto << data_referencia.to_s + "," #11
			texto << Configuracao.last.responsavel_pelo_registro_contabel_atual_PJ(tipo_de_poder).try(:pessoa).try(:nome).to_s.sim_limite(40) + "," #12
			texto << justificativa.to_s.sim_descricao.sim_limite(255) #13
	
			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, "", atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def lancar_estorno_nos_movimentos_bancarios
		return false if pagamento.contas_bancarias_por_pagamento.empty?

		self.pagamento.contas_bancarias_por_pagamento.each do |conta_por_pagamento|
			movimentacao = Contabilidade::MovimentacaoDaContaBancaria.where(modulo: self.pagamento, valor: conta_por_pagamento.valor_pago * - 1).first

			if movimentacao.present?
				movimentacao_de_estorno = movimentacao.dup
				movimentacao_de_estorno.modulo = self
				movimentacao_de_estorno.data_da_movimentacao = self.data_do_estorno
				movimentacao_de_estorno.valor = self.pagamento.valor
				movimentacao_de_estorno.historico = "Estorno do #{movimentacao_de_estorno.historico}"
				movimentacao_de_estorno.save
			end
		end
	end

	def apagar_movimentos_bancarios
		Contabilidade::MovimentacaoDaContaBancaria.where(modulo: self).delete_all
	end

	def seta_orcamento_do_estorno
		self.orcamento_id = self.pagamento.orcamento_id
	end

	def atribui_codigo_disponivel
		gerar_codigo(send(:data_do_estorno), :numero_do_estorno, :data_do_estorno, :orcamento_id, self.orcamento_id)
	end

	private
	def impedir_se_for_prepagamento
		if pagamento.present? && pagamento.prepagamento
			errors.add(:justificativa, 'não é possível realizar o estorno deste pagamento, pois se trata de um prepagamento')
			return false
		end
	end

	def dados_da_movimentacao_bancaria(conta_bancaria)
		{
			conta_bancaria_por_unidade_orcamentaria_id: pagamento.conta_por_unidade_do_empenho(conta_bancaria).try(:id),
			modulo: self,
			data_da_movimentacao: self.data_do_estorno,
			historico: self.justificativa
		}
	end

	def data_do_estorno_superior_a_conta_bancaria
		contas_invalidas = self.pagamento.contas_bancarias_por_pagamento.map{ |conta_bancaria_por_pagamento| conta_bancaria_por_pagamento.conta_bancaria.to_s if conta_bancaria_por_pagamento.conta_bancaria.data_de_abertura.present? && conta_bancaria_por_pagamento.conta_bancaria.data_de_abertura > self.data_do_estorno }.join(", ")
		errors.add(:data_do_estorno, "Inferior a Data de Abertura das Seguintes Contas: #{contas_invalidas}") if contas_invalidas.present?
	end
end
