class Contabilidade::EstornoDeLiquidacao < ApplicationRecord
	include AASM
	include GeradorDeEventosContabeis
	include IncrementadorDeCodigoConcern
	extend ActiveSupport::Concern

	# reversor_de_eventos_contabeis acao: 6, atributo_pai: 'liquidacao', atributo_data: 'data_do_estorno', atributo_codigo_movimentacao: 'numero_da_liquidacao'

	has_paper_trail

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

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

	attr_default :data_do_estorno, Date.today
	attr_default :derivado_de_cancelamento, false

	attr_accessor :nao_faz_duplicada_de_rp
	attr_default :nao_faz_duplicada_de_rp, false

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

	validates_presence_of :justificativa, :liquidacao_id, :usuario_id, :data_do_estorno

	validate :valida_se_ja_houve_envio_do_sim
	validate :valida_se_data_esta_no_orcamento
	validate :valida_data_com_anulacao_de_pagamento

	after_create :estorna_a_liquidacao
	after_create :adiciona_valor_de_estorno_no_empenho
	after_destroy :cancela_estorno_da_liquidacao

	before_create :cria_estorno_no_restos_a_pagar, if: Proc.new { self.liquidacao.empenho.empenho_de_restos_a_pagar.present? }
	before_save :lanca_as_mudancas_no_restos_a_pagar_se_tiver, if: Proc.new { self.liquidacao.empenho.empenho_de_restos_a_pagar.present? }
	before_save :seta_orcamento_do_estorno_de_liquidacao
	before_save :atribui_codigo_disponivel

	enum status: {
		solicitado: 0,
		enviado_para_controladoria: 1,
		enviado_para_contabilidade: 2,
		confirmado: 3,
		enviado_para_administrativo: 4,
		recebido: 5,
		enviado_para_o_financeiro: 6,
		recebido_pelo_financeiro: 7,
		autorizado: 8
	}

	scope :confirmadas_ate_autorizadas, -> { where(status: [:confirmado, :enviado_para_o_financeiro, :recebido_pelo_financeiro, :autorizado])}
	scope :do_orcamento, -> { joins(:liquidacao).where("contabilidade_liquidacoes.restos_a_pagar is false") }
	scope :de_restos_a_pagar, -> { joins(:liquidacao).where("contabilidade_liquidacoes.restos_a_pagar is true") }

	aasm column: :status, enum: true, whiny_transitions: false do
		state :solicitado, initial: true
		state :enviado_para_controladoria
		state :confirmado
		state :enviado_para_contabilidade
		state :recebido
		state :enviado_para_o_financeiro
		state :recebido_pelo_financeiro
		state :enviado_para_administrativo
		state :autorizado

		event :enviar_para_controladoria, guard: :envia_pra_controladoria? do
			transitions from: :solicitado, to: :enviado_para_controladoria
		end

		event :retornar_para_administrativo do
			transitions from: :enviado_para_controladoria, to: :solicitado, guard: :reprovado_pela_controladoria?
			transitions from: :enviado_para_contabilidade, to: :solicitado
		end

		event :enviar_para_contabilidade, guard: :envia_pra_contabilidade? do
			transitions from: :solicitado, to: :enviado_para_contabilidade


			transitions from: :enviado_para_controladoria, to: :enviado_para_contabilidade do
				guard do
					aprovado_pela_controladoria?
				end
			end
		end

		event :enviar_para_administrativo, guard: :aprovado_pela_controladoria? do
			transitions from: :enviado_para_controladoria, to: :enviado_para_administrativo do
				guard do
					!envia_pra_contabilidade?
				end
			end
		end

		event :receber do
			transitions from: :enviado_para_contabilidade, to: :recebido
		end

		event :confirmar do
			transitions from: :solicitado, to: :confirmado do
				guard do
					!envia_pra_contabilidade?
				end
			end

			transitions from: :enviado_para_administrativo, to: :confirmado
			transitions from: :recebido, to: :confirmado

			after do
				self.estorna_a_liquidacao
				self.adiciona_valor_de_estorno_no_empenho
			end
		end
	end

	# HELPERS AASM
	def envia_pra_controladoria?
		return Configuracao.last.envia_empenho_de_diaria_para_controladoria? if self.liquidacao.empenho_de_diaria?
		Configuracao.last.envia_empenho_para_controladoria?
	end

	def envia_pro_copfin?
		return Configuracao.last.envia_empenho_de_diaria_para_copfin? if self.liquidacao.empenho_de_diaria?
		Configuracao.last.envia_empenho_para_copfin?
	end

	def envia_pra_contabilidade?
		Configuracao.last.envia_liquidacao_para_contabilidade?
	end

	def aprovado_pela_controladoria?
		self.vistos.any? && self.vistos.last.aprovado? && updated_at < self.vistos.last.created_at
	end

	def reprovado_pela_controladoria?
		self.vistos.any? && !self.vistos.last.aprovado? && updated_at < self.vistos.last.created_at
	end

	def pode_criar_mais_vistos?
		enviado_para_controladoria? && (self.vistos.empty? || self.vistos.last.created_at < updated_at)
	end

	#OUTROS MÉTODOS NÃO AASM
	def numero_da_liquidacao
		liquidacao.empenho_e_numero
	end

	def detalhamento_do_plano_de_contas
		liquidacao.empenho.try(:detalhamento_do_plano_de_contas)
	end

	def valor
		return self.liquidacao.valor.to_f
	end

	def confirmado_ate_autorizadas
		self.confirmado? || self.enviado_para_o_financeiro? || self.recebido_pelo_financeiro? ||self.autorizado?
	end
	

	def estorna_a_liquidacao
		if self.confirmado?
			self.liquidacao.update_attribute(:estornada, true) if self.liquidacao.present?
		end
	end

	def cancela_estorno_da_liquidacao
		self.liquidacao.update_attribute(:estornada, false) if self.liquidacao
		self.liquidacao.empenho.update_attribute(:valor_liquidado_estornado, self.liquidacao.empenho.valor_liquidado_estornado.to_f - self.liquidacao.valor.to_f)
	end

	def adiciona_valor_de_estorno_no_empenho
		if self.confirmado?
			self.liquidacao.empenho.update_attribute(:valor_liquidado_estornado, self.liquidacao.empenho.valor_liquidado_estornado.to_f + self.liquidacao.valor.to_f)
		end
	end

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

	def to_sim(data_referencia)
		begin
			exercicio_do_orcamento = liquidacao.empenho.orcamento.exercicio.to_s

			texto = ""
			texto << "610".sim_preenche(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.sim_preenche(3) + ","
			texto << exercicio_do_orcamento +  "00" + ","
			texto << liquidacao.unidade_orcamentaria.orgao.codigo.to_s.sim_preenche(2) + ","
			texto << liquidacao.unidade_orcamentaria.codigo.to_s.codigo_uo_to_sim + ","
			texto << liquidacao.empenho.data_do_empenho.sim_data + ","
			texto << liquidacao.empenho.numero_do_empenho.to_s.sim_limite(8) + ","
			texto << liquidacao.data_da_liquidacao.sim_data + ","
			texto << data_do_estorno.sim_data + ","
			texto << data_referencia.to_s + ","

			comeco_do_mes = Date.new(data_referencia[0..3].to_i,data_referencia[4..5].to_i, 1)

			assessor_informatica = ::ConfiguracaoSim.joins(:pessoa).where(tipo_de_responsavel: "assessor_informatica").where("base_pessoas.cnpj is not null").first

			texto << assessor_informatica.try(:pessoa).try(:nome).to_s.sim_limite(40) + ","

			texto << justificativa.to_s.sim_descricao.sim_limite(255)

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

	def valida_se_ja_houve_envio_do_sim
		if existe_lote_do_sim?
			errors.add(:sim, 'O SIM do mês já foi enviado')
		elsif mes_bloqueado?
			errors.add(:sim, '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.liquidacao.try(:orcamento).try(:exercicio)
			errors.add(:data_do_estorno, 'A data do estorno da liquidação, não está dentro do orçamento da liquidação.')
		end
	end

	def valida_data_com_anulacao_de_pagamento
		if self.liquidacao.pagamentos.map{|pag| self.data_do_estorno < pag&.estorno_de_pagamento&.data_do_estorno}.any?
			errors.add(:data_do_estorno, 'A data do estorno da liquidação, não pode ser anteriro ao do Estorno de pagamento')
		end
	end


	def existe_lote_do_sim?
		ultima_data = data_do_estorno

		if ultima_data.present?
			lote_sim = Tcm::Lote.find_by(
				orcamento_id: liquidacao.orcamento_id,
				tipo: [Tcm::Lote.tipos[:todos], Tcm::Lote.tipos[:contabilidade]],
				mes_de_referencia: ultima_data.month,
				situacao: [Tcm::Lote.situacoes[:gerado], Tcm::Lote.situacoes[:finalizado]]
			)

			return lote_sim.present?
		end

		return false
	end

	def codigo_da_liquidacao
		liquidacao.numero rescue ''
	end

	def cria_estorno_no_restos_a_pagar
		unless nao_faz_duplicada_de_rp == true
			liquidacao_de_rp = self.liquidacao.empenho.empenho_de_restos_a_pagar.liquidacoes.where(numero: self.liquidacao.numero).first

			unless liquidacao_de_rp.estorno_de_liquidacao.present?
				estorno = liquidacao_de_rp.build_estorno_de_liquidacao(usuario_id: self.usuario_id, data_do_estorno: self.data_do_estorno,
					justificativa: self.justificativa, status: self.status, derivado_de_cancelamento: self.derivado_de_cancelamento)

				estorno.nao_faz_duplicada_de_rp = true
				estorno.save(validate: false)
			end
		end
	end

	def lanca_as_mudancas_no_restos_a_pagar_se_tiver
		campos_alterados = self.changed

		if campos_alterados.size > 0
			estorno_liquidacao_de_rp = self.liquidacao.empenho.empenho_de_restos_a_pagar.liquidacoes.where(numero: self.liquidacao.numero).first.estorno_de_liquidacao

			if estorno_liquidacao_de_rp.present?
				campos_alterados.each do |campo|
					unless campo == 'liquidacao_id'
						estorno_liquidacao_de_rp.update_column(campo, self["#{campo}".to_sym])
					end
				end
			end
		end
	end

	def seta_orcamento_do_estorno_de_liquidacao
		self.orcamento_id = self.liquidacao.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

end
