class Contabilidade::TransferenciaDeSaldoPcasp < ApplicationRecord
	has_paper_trail

	attr_accessor :topico_sub_conta_pcasp, :ics_debito, :ics_credito

	belongs_to :orcamento, class_name: "Orcamento"
	belongs_to :unidade_orcamentaria, class_name: "Loa::UnidadeOrcamentaria"
	belongs_to :sub_conta_pcasp, class_name: "Contabilidade::SubContaPcasp"
	belongs_to :conta_credito, class_name: "Contabilidade::Conta", foreign_key: "conta_credito_id"
	belongs_to :conta_debito, class_name: "Contabilidade::Conta", foreign_key: "conta_debito_id"
	belongs_to :evento_contabil, class_name: "Contabilidade::EventoContabil"
	belongs_to :movimentacao_credito, class_name: "Contabilidade::MovimentacaoDoPlanoDeContas", foreign_key: "movimento_credito_id"
	belongs_to :movimentacao_debito, class_name: "Contabilidade::MovimentacaoDoPlanoDeContas", foreign_key: "movimento_debito_id"

	belongs_to :fonte_de_recursos_conta_debito, :class_name => "Base::FonteDeRecursos", :foreign_key => "fonte_de_recursos_conta_debito_id"
	belongs_to :fonte_de_recursos_conta_credito, :class_name => "Base::FonteDeRecursos", :foreign_key => "fonte_de_recursos_conta_credito_id"
	belongs_to :natureza_da_receita_conta_debito, :class_name => "Base::NaturezaDaReceita", :foreign_key => "natureza_da_receita_conta_debito_id"
	belongs_to :natureza_da_receita_conta_credito, :class_name => "Base::NaturezaDaReceita", :foreign_key => "natureza_da_receita_conta_credito_id"
	belongs_to :funcao_e_subfuncao_conta_debito, :class_name => "Base::Subfuncao", :foreign_key => "funcao_e_subfuncao_conta_debito_id"
	belongs_to :funcao_e_subfuncao_conta_credito, :class_name => "Base::Subfuncao", :foreign_key => "funcao_e_subfuncao_conta_credito_id"

	validate :valida_se_ja_houve_envio_do_sim, if: Proc.new { new_record? }
	validate :transferencia_sem_par_3_e_4
	validate :valida_se_contas_tem_mesmo_nivel_de_detalhamento
	validate :valida_se_as_informacoes_complementares_foram_preenchidas
	validates_presence_of :data_transferencia, :unidade_orcamentaria_id, :conta_debito_id, :conta_credito_id, :historico, :valor

	before_update :apagar_movimentos_do_plano_de_contas
	before_destroy :apagar_movimentos_do_plano_de_contas
	after_save :criar_evento_contabil_para_movimento
	after_save :criar_movimentos_do_plano_de_contas
	after_save :associar_movimentacoes

  belongs_to :conta_extra_orcamentaria_conta_debito, 
             class_name: 'Contabilidade::ContaExtraOrcamentaria',
             foreign_key: 'conta_extra_orcamentaria_conta_debito_id', 
             optional: true
	belongs_to :conta_extra_orcamentaria_conta_credito, 
           	 class_name: 'Contabilidade::ContaExtraOrcamentaria',
           		foreign_key: 'conta_extra_orcamentaria_conta_credito_id', 
           		optional: true

	enum topico_sub_conta_pcasp: {
		divida_ativa: 1,
		almoxarifado: 2,
		precatorio: 3,
		provisoes: 4,
		imobilizado: 5,
		rpps: 6,
		operacao_de_credito: 7,
		investimentos: 8,
		obrigacoes: 9,
		transferencia_de_saldo_pcasp: 900,
		abertura: 999,
	}

	enum indicador_do_superavit_financeiro: {
		financeiro: 1,
		permanente: 2,
	}

	def criar_evento_contabil_para_movimento

		evento = Contabilidade::EventoContabil.joins(:contas_por_eventos_contabeis).where(
			contabilidade_eventos_contabeis: {
				orcamento_id: self.orcamento.id,
				classe: 99,
				tipo: 2,
				modelo: 999,
				lancamento_manual: true
			},
			contabilidade_contas_por_eventos_contabeis: {
				tipo_de_lancamento: 0,
				conta_id: self.conta_debito.id,
				conta_par_id: self.conta_credito.id
			}
		).uniq.last

		if evento.nil?
			evento = Contabilidade::EventoContabil.new(
				nome: "EVENTO DE CONTABILIZAÇÃO MANUAL PCASP Nº #{self.id}",
				orcamento_id: self.orcamento.id,
				classe: 99,
				tipo: 2,
				modelo: 999,
				lancamento_manual: true,
				lancado_por_formulario: true
			)

			evento.save!

			evento.contas_por_eventos_contabeis.create!(
				conta_id: self.conta_debito.id,
				conta_par_id: self.conta_credito.id,
				tipo_de_lancamento: 0
			)
		end

		self.update_column(:evento_contabil_id, evento.id)
	end

	def criar_movimentos_do_plano_de_contas
		movimento = Contabilidade::MovimentacaoDoPlanoDeContas.create!(
			conta_por_evento_contabil_id: self.evento_contabil.contas_por_eventos_contabeis.first.id,
			valor: self.valor,
			data_de_lancamento: self.data_transferencia,
			tipo_de_lancamento: 0,
			unidade_orcamentaria_id: self.unidade_orcamentaria_id,
			historico: self.historico,
			sub_conta_pcasp_id: self.sub_conta_pcasp_id,
			lancamento_manual: 900
		)
	end

	def associar_movimentacoes
		movimento_debito = self.evento_contabil.movimentacoes_do_plano_de_contas.debito.last
		movimento_credito = self.evento_contabil.movimentacoes_do_plano_de_contas.credito.last

		movimento_debito.update_columns(self.ics_debito)
		movimento_credito.update_columns(self.ics_credito)

		movimento_debito.update_column(:conta_extra_orcamentaria_id, self.conta_extra_orcamentaria_conta_debito_id) if self.conta_extra_orcamentaria_conta_debito_id.present?
		movimento_credito.update_column(:conta_extra_orcamentaria_id, self.conta_extra_orcamentaria_conta_credito_id) if self.conta_extra_orcamentaria_conta_credito_id.present?

		self.update_column(:movimento_debito_id, movimento_debito.id)
		self.update_column(:movimento_credito_id, movimento_credito.id)
	end

	def apagar_movimentos_do_plano_de_contas
		self.movimentacao_credito.delete
		self.movimentacao_debito.delete
	end

	def mes_bloqueado?
		if data_transferencia.present? && Configuracao.last.utiliza_evento_contabil?
			orcamento = orcamento.present? ? orcamento : Orcamento.find_by(exercicio: data_transferencia.year)
			mes = Contabilidade::BloqueioMensalDoPcasp.find_by(mes_referencia: data_transferencia.month, orcamento_id: orcamento.id, bloqueado: true)
			return mes.present?
		else
			return false
		end
	end

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

	def transferencia_sem_par_3_e_4
		return if conta_credito == nil || conta_debito == nil

		if (conta_credito.classe == "3" && conta_debito.classe == "4") || (conta_credito.classe == "4" && conta_debito.classe == "3")
			errors.add(:contas, 'Não pode ter classe 3 e 4 juntas no lançamento.')
		end
	end

	def valida_se_contas_tem_mesmo_nivel_de_detalhamento
		return if conta_credito == nil || conta_debito == nil || Configuracao.first&.permitir_niveis_diferentes_na_transferencia_pcasp == true

		if (conta_credito.subtitulo != conta_debito.subtitulo)
			errors.add(:contas, 'Par de contas deve ter o mesmo nivel de detalhamento')
		end
	end

	def valida_se_as_informacoes_complementares_foram_preenchidas
		self.ics_credito = informacoes_complementares_da_conta_de('credito')
		self.ics_debito = informacoes_complementares_da_conta_de('debito')

		valida_ics_obrigatorios_da_conta_de('debito')
		valida_ics_obrigatorios_da_conta_de('credito')
	end

	def ano_de_inscricao_resto_a_pagar_da_conta_debito
		if self.conta_debito && self.conta_debito.informacao_complementar_possui_ano_de_restos_a_pagar?
			return self.conta_debito.orcamento.exercicio
		end
	end

	def ano_de_inscricao_resto_a_pagar_da_conta_credito
		if self.conta_credito && self.conta_credito.informacao_complementar_possui_ano_de_restos_a_pagar?
			return self.conta_credito.orcamento.exercicio
		end
	end

	def divida_consolidada_conta_debito
		divida_consolidada = (self.conta_debito.codigo == "212110205" || !self.	sub_conta_pcasp.divida_consolidada?) ? '1' : nil rescue nil
		divida_consolidada if self&.sub_conta_pcasp&.divida_consolidada?
	end

	def divida_consolidada_conta_credito
		divida_consolidada = (self.conta_credito.codigo == "212110205" || !self.sub_conta_pcasp.divida_consolidada?) ? '1' : nil rescue nil
		divida_consolidada if self&.sub_conta_pcasp&.divida_consolidada?
	end

	def poder_ou_orgao_formatados
		return '20231' unless self.unidade_orcamentaria.try(:tipo_de_unidade_administrativa).try(:executivo?)
		self.unidade_orcamentaria.rpps? ? '10132' : '10131'
	end

	def codigo_fonte_de_recursos_formatado_da_conta_de(tipo_de_conta)
		fonte_de_recursos = Base::FonteDeRecursos.find(self["fonte_de_recursos_conta_#{tipo_de_conta}_id".to_sym])
		fonte_de_recursos.codigo_completo.first(4) rescue nil
	end

	def codigo_fonte_de_recursos_complemento_formatado_da_conta_de(tipo_de_conta)
		fonte_de_recursos = Base::FonteDeRecursos.find(self["fonte_de_recursos_conta_#{tipo_de_conta}_id".to_sym])
		return if fonte_de_recursos == nil
		fonte_de_recursos.codigo_completo[4..7] unless fonte_de_recursos.codigo_completo[4..7].to_i == 0
	end

	def codigo_divida_consolidada_da_conta_de(tipo_de_conta)
		tipo_de_conta == 'debito' ? self.divida_consolidada_conta_debito : self.divida_consolidada_conta_credito
	end

	def codigo_natureza_da_receita_da_conta_de(tipo_de_conta)
		tipo_de_conta == 'debito' ? self.natureza_da_receita_conta_debito.codigo : self.natureza_da_receita_conta_credito.codigo
	end

	def codigo_natureza_despesa_da_conta_de(tipo_de_conta)
		tipo_de_conta == 'debito' ? self.natureza_da_despeza_conta_debito.codigo : self.natureza_da_despeza_conta_credito.codigo
	end

	def codigo_funcao_e_subfuncao_da_conta_de(tipo_de_conta)
		tipo_de_conta == 'debito' ? self.natureza_da_despeza_conta_debito.codigo : self.natureza_da_despeza_conta_credito.codigo
	end

	def codigo_ano_exercicio_conta_de(tipo_de_conta)
		conta = tipo_de_conta == 'debito' ? self.conta_debito : self.conta_credito

		conta.orcamento.exercicio if conta.informacao_complementar_possui_ano_de_restos_a_pagar
	end

	def informacoes_complementares_da_conta_de(tipo_de_conta)
		ic_po = self.poder_ou_orgao_formatados rescue nil
		ic_fp = self["atributo_do_superavit_financeiro_conta_#{tipo_de_conta}".to_sym] rescue nil
		ic_fr = self.codigo_fonte_de_recursos_formatado_da_conta_de(tipo_de_conta) rescue nil
		ic_co = self.codigo_fonte_de_recursos_complemento_formatado_da_conta_de(tipo_de_conta) rescue nil
		ic_dc = self.codigo_divida_consolidada_da_conta_de(tipo_de_conta) rescue nil
		ic_nr = self.codigo_natureza_da_receita_da_conta_de(tipo_de_conta) rescue nil
		ic_nd = self.codigo_natureza_despesa_da_conta_de(tipo_de_conta) rescue nil
		ic_fs = self.codigo_funcao_e_subfuncao_da_conta_de(tipo_de_conta) rescue nil
		ic_ai = self.codigo_ano_exercicio_conta_de(tipo_de_conta) rescue nil

		{
			ic_po: ic_po,
			ic_fp: ic_fp,
			ic_fr: ic_fr,
			ic_co: ic_co,
			ic_dc: ic_dc,
			ic_nr: ic_nr,
			ic_nd: ic_nd,
			ic_fs: ic_fs,
			ic_ai: ic_ai,
		}
	end
	
	def valida_ics_obrigatorios_da_conta_de(tipo_de_conta)
		obrigatorios = []
		mensagem_de_erro = 'Informação complementar não foi preenchida.'
		conta = tipo_de_conta == 'debito' ? self.conta_debito : self.conta_credito

		return if conta == nil

		atributo_do_superavit_financeiro_campo = "atributo_do_superavit_financeiro_conta_#{tipo_de_conta}".to_sym
		if conta.informacao_complementar_possui_atributo_financeiro? && !self[atributo_do_superavit_financeiro_campo].present?
			errors.add(atributo_do_superavit_financeiro_campo, mensagem_de_erro)
		end

		fonte_de_recursos_campo = "fonte_de_recursos_conta_#{tipo_de_conta}_id".to_sym
		if conta.informacao_complementar_possui_fonte_de_recursos? && !self[fonte_de_recursos_campo].present?
			errors.add(fonte_de_recursos_campo, mensagem_de_erro)
		end

		natureza_da_despesa_campo = "natureza_da_despesa_conta_#{tipo_de_conta}_id".to_sym
		if conta.informacao_complementar_possui_natureza_da_despesa? && !self[natureza_da_despesa_campo].present?
			errors.add(natureza_da_despesa_campo, mensagem_de_erro)
		end

		natureza_da_receita_campo = "natureza_da_receita_conta_#{tipo_de_conta}_id".to_sym
		if conta.informacao_complementar_possui_natureza_da_receita? && !self[natureza_da_receita_campo].present?
			errors.add(natureza_da_receita_campo, mensagem_de_erro)
		end

		funcao_e_subfuncao_campo = "funcao_e_subfuncao_conta_#{tipo_de_conta}_id".to_sym
		if conta.informacao_complementar_possui_natureza_da_receita? && !self[natureza_da_receita_campo].present?
			errors.add(funcao_e_subfuncao_campo, mensagem_de_erro)
		end
	end

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

			return lote_sim.present?
		else
			return false
		end
	end
end
